0

I am trying to generate a chrome text effect in core graphics and I am having some trouble getting it to look right.

This is the effect I am trying to achieve:

Desired Effect

This is what I have managed to get:

enter image description here

As you can see the gradient edge is quite jagged. Is there a way to achieve this effect without resorting to static pngs for the texture fill?

This is the my code:

- (void)setGradientFill:(UILabel*)label
{
    CGSize textSize = [label.text sizeWithFont:label.font];
    CGFloat width = textSize.width;         // max 1024 due to Core Graphics limitations
    CGFloat height = textSize.height;       // max 1024 due to Core Graphics limitations

    // create a new bitmap image context
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), YES, 0.0);

    // get context
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetAllowsAntialiasing(context, true);
    CGContextSetShouldAntialias(context, true);

    // push context to make it current (need to do this manually because we are not drawing in a UIView)
    UIGraphicsPushContext(context);

    CGContextSetAllowsAntialiasing(context, YES);
    CGContextSetShouldAntialias(context, YES);

    //draw gradient
    CGGradientRef glossGradient;
    CGColorSpaceRef rgbColorspace;
    size_t num_locations = 5;
    CGFloat locations[5] = { 0.0, 0.5, 0.5, 0.65, 1};
    CGFloat components[20] = {
        1.0, 1.0, 1.0, 1.0,
        1.0, 1.0, 1.0, 1.0,
        0.878, 0.878, 0.878, 0.878,
        0.78, 0.78, 0.78, 1.0,
        1, 1, 1, 1.0
    };
    rgbColorspace = CGColorSpaceCreateDeviceRGB();
    glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
    CGPoint start = CGPointMake(0, 0);
    CGPoint end = CGPointMake(width/4, height*1.2);
    CGContextDrawLinearGradient(context, glossGradient, start, end, kCGGradientDrawsAfterEndLocation);

    CGGradientRelease(glossGradient);
    CGColorSpaceRelease(rgbColorspace);

    // pop context
    UIGraphicsPopContext();

    // get a UIImage from the image context
    UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up drawing environment
    UIGraphicsEndImageContext();

    label.textColor = [UIColor colorWithPatternImage:gradientImage];
}

Edit for suggestion by lefteris:

I tried the extension you made (image on the left below). I am getting some very blurry text with artefacts around the edges for some reason.

enter image description here

I am sizing the UIImage frame to the image to avoid content stretching:

_test.frame = CGRectMake(_test.frame.origin.x, _test.frame.origin.y, image.size.width, image.size.height);
_test.image = image;

Edit Closest Solution:

Solution by lefteris is the closest, you can't really avoid the jagged gradient edges though. If you can you should use static pngs generated in photoshop for the texture (not the text just the fill) but you may need several versions (as I do) to get the right effect on each screen as the texture is applied according to the size of the frame not the contained text. Doing it dynamically is possible but limited it seems.

Imran
  • 1,488
  • 1
  • 15
  • 36

2 Answers2

1

Use the UIGraphicsBeginImageContextWithOptions function with the scale factor set to 0.0.

The UIGraphicsBeginImageContext function calls that function with the scale factor set to 1.0, unconditionally, even on Retina (@2x) devices. On a Retina device, you want 2.0. 0.0 will automatically detect the correct scale factor to use and use it.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • It helps but the edges are still jagged, It's like there is no anti aliasing. I have updated the question to reflect the 0.0 scale factor setting. – Imran Dec 24 '12 at 11:05
0

You should also smooth out your gradient.

Use this instead:

CGFloat locations[5] = { 0.0, 0.45, 0.55, 0.65, 1};

I edit my answer to provide a different solution/suggestion:

I have created a 3D Text rendering UIImage Extension that renders 3D Text, which can be found here

Using that category extension, with a bit of modifications, I ended up with this image:

enter image description here

I believe you should not create the gradient as a background color and apply it as a whole, over a label, as it is also adding the gradient over the background as well, which is making it look less realistic.

I created the image above, with these settings:

UIImage *my3dImage = [UIImage create3DImageWithText:@"61" Font:[UIFont fontWithName:@"Helvetica-Bold" size:180] ForegroundColor:[UIColor colorWithRed:(255/255.f) green:(255/255.f) blue:(255/255.f) alpha:1.0] ShadowColor:[UIColor blackColor] outlineColor:[UIColor lightGrayColor] depth:4 useShine:YES];

And I modified my source code to apply a different shine effect:

  size_t num_locations = 5;
    CGFloat locations[5] = { 0.0, 0.49, 0.51, 0.65, 1};
    CGFloat gradientComponents[20] = {
        1.0, 1.0, 1.0, 1.0,
        1.0, 1.0, 1.0, 1.0,
        0.878, 0.878, 0.878, 0.878,
        0.78, 0.78, 0.78, 1.0,
        1, 1, 1, 1.0
    };

    CGGradientRef glossGradient = CGGradientCreateWithColorComponents(genericRGBColorspace, gradientComponents, locations, num_locations);
    CGPoint start = CGPointMake(0, expectedSize.height/4);
    CGPoint end = CGPointMake(expectedSize.width/4, expectedSize.height);

The reason, I believe my approach is better, is because it actually is creating a mask from the text and then applies the gradient, meaning only the text get's the gradient and not the background.

Community
  • 1
  • 1
Lefteris
  • 14,550
  • 2
  • 56
  • 95
  • Thanks Lefteris. I did try this previously and several variations with different values for the 0.45 component. They either end up looking jagged or are too gradual which doesn't really match the example I provided. – Imran Dec 24 '12 at 13:51
  • Thanks Lefteris ill give it a try, not sure how adding the gradient directly to the text fill would cause issues though? – Imran Dec 28 '12 at 11:28
  • Actually I'm suggesting to replace the text with an image you create by the category extension I have created. – Lefteris Dec 28 '12 at 11:40
  • I updated the question with details from your suggestion, doesn't quite work as I expected. – Imran Jan 04 '13 at 13:58
  • Did you use my code as it is, and replaced only the gradient code part? It is working fine for me! – Lefteris Jan 04 '13 at 14:06