10

I need to render UIBezierPaths without anti-aliasing and then save them as PNG to retain the full pixel representations (for example, not let JPEG muck the image up). I've tried calling the CG functions below just before stroking the UIBezierPaths, but it seems none have any effect on the resultant rendered image. The paths are still rendered with anti-aliasing (i.e. smoothed).

CGContextSetShouldAntialias(c, NO);
CGContextSetAllowsAntialiasing(c, NO);
CGContextSetInterpolationQuality(c, kCGInterpolationNone);

Any hits would be greatly appreciated.

alonjr
  • 143
  • 1
  • 7

2 Answers2

16

When I use those options, it turns off antialiasing. On the left is with the default options. On the right, with your options.

enter image description here

This is easy to control if you're using a UIView subclass. This is my drawRect:

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetShouldAntialias(context, NO);

    [[UIColor redColor] setStroke];
    UIBezierPath *path = [self myPath];
    [path stroke];
}

And to capture the screen, from How to take a screenshot programmatically

- (void)captureScreen
{
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO, [UIScreen mainScreen].scale);
    else
        UIGraphicsBeginImageContext(self.window.bounds.size);
    [self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    NSData *data = UIImagePNGRepresentation(image);
    [data writeToFile:[self screenShotFilename] atomically:YES];
}

If you're using a CAShapeLayer, then I don't think you can control the antialiasing on screen, because as the documentation says:

The shape will be drawn antialiased, and whenever possible it will be mapped into screen space before being rasterized to preserve resolution independence. However, certain kinds of image processing operations, such as CoreImage filters, applied to the layer or its ancestors may force rasterization in a local coordinate space.

But, regardless of the antialiasing on screen, if you want to have your snapshot of the screen not be antialiased, you can insert your CGContextSetShouldAntialias into the captureScreen routine:

- (void)captureScreen
{
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)])
        UIGraphicsBeginImageContextWithOptions(self.window.bounds.size, NO, [UIScreen mainScreen].scale);
    else
        UIGraphicsBeginImageContext(self.window.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetShouldAntialias(context, NO);
    [self.window.layer renderInContext:context];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    NSData * data = UIImagePNGRepresentation(image);
    [data writeToFile:[self screenShotFilename] atomically:YES];
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Wow, @rob, thanks for such a comprehensive response, i've learned a few new things along the line :) – alonjr Mar 15 '13 at 17:45
  • @Rob, could you advise why anti-aliasing brings that grey background (left picture)? Thanks. – berec Dec 29 '17 at 21:23
  • 1
    @berec - That was just some random artifact of how I created that old image snapshot. I've regenerated those images (using same code, above). The aliasing chosen on the context on which you draw a stroke does not affect the background. Sorry if that old image made it confusing. – Rob Dec 29 '17 at 22:35
5

Where are you getting c from? Are you certain that c refers to the same thing as UIGraphicsGetCurrentContext() in the drawing cycle that you use [UIBezierPath stroke]? It's difficult to tell from the above sample.

If you want to be certain that you're drawing to the same context that you're configuring, fetch the CGPath from the UIBezierPath, and draw it directly:

- (void)drawRect:(CGRect)rect {
  CGContextRef context = UIGraphicGetCurrentContext();
  CGPathRef path = [self.bezier CGPath];
  CGContextSetShouldAntialias(context, NO);
  CGContextAddPath(context, path);
  CGContextStrokePath(context);
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610