Search code examples
iphonecore-animationgradientantialiasingradial-gradients

CALayer and CGGradientRef anti-aliasing?


I'm having an odd issue with CALayer drawing for the iPhone. I have a root layer which adds a bunch of sublayers representing "bubbles". The end result is supposed to look something like this:

http://www.expensivedna.com/IMG_0018.PNG http://www.expensivedna.com/IMG_0018.PNG

The problem is that I can't seem to get the layer to anti-alias (notice the jaggies on the bubbles). My code overwriting drawInContext for the bubble CALayer is as follows:

- (void)drawInContext:(CGContextRef)theContext{
CGContextSetAllowsAntialiasing(theContext, true);
CGContextSetShouldAntialias(theContext, true);

size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 0.5,  // Start color
                          1.0, 1.0, 1.0, 0.0 }; // End color
CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef glossGradient = 
CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);

CGPoint topCenter = CGPointMake(25, 20);
CGPoint midCenter = CGPointMake(25, 25);
CGContextDrawRadialGradient(theContext, glossGradient, midCenter, 20, topCenter, 10, kCGGradientDrawsAfterEndLocation); 

}

Now the really odd thing is that if I slightly alter the drawing code to only draw normal red circles as follows:

- (void)drawInContext:(CGContextRef)theContext{
CGContextSetAllowsAntialiasing(theContext, true);
CGContextSetShouldAntialias(theContext, true);

CGContextSetFillColorWithColor(theContext, [UIColor redColor].CGColor);
CGContextFillEllipseInRect(theContext, CGRectMake(0, 0, 40,40));
}

Everything seems to antialias OK:

http://www.expensivedna.com/IMG_0017.PNG http://www.expensivedna.com/IMG_0017.PNG

I can't seem to figure out this seemingly odd behavior. Am I missing some difference between antialiasing gradients and normal circles?

Thanks guys.


Solution

  • Thanks guys. So it turns out that the answer is a combination of your two answers (coob and Alex). Basically it seems like the CGDrawRadialGradient function only has aliasing at the starting circle, not the ending one. Since I want aliased "edges" on both, I first set the function to draw from the inside out which takes care of the first "edge", but produces the following:

    Step 1

    Then, I clip the image as suggested by coob and that gives a nice aliasing around the final edge of the bubble:

    Step 2

    Looks good enough for me!

    - (void)drawInContext:(CGContextRef)theContext{
    CGContextSetAllowsAntialiasing(theContext, true);
    CGContextSetShouldAntialias(theContext, true);
    
    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = { 1.0, 1.0, 1.0, 0.0,  // Start color
                  1.0, 1.0, 1.0, 0.5 }; // End color
    CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef glossGradient = 
    CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
    
    CGContextAddEllipseInRect(theContext, CGRectMake(5, 5, 40, 40));
    CGContextClip(theContext);
    
    CGPoint topCenter = CGPointMake(25, 20);
    CGPoint midCenter = CGPointMake(25, 25);
    CGContextDrawRadialGradient(theContext, glossGradient, topCenter, 10, midCenter, 20, kCGGradientDrawsAfterEndLocation);
    

    }