Dexter
Dexter

Reputation: 5736

CAShapeLayer drawing path strangely

I'm building a UIBezierPath as follows:

UIBezierPath *path = [UIBezierPath bezierPath];
CGFloat yOrigin = 2;
for (int i = 0; i < lines; ++i) {
    [path moveToPoint:CGPointMake(0, yOrigin)];
    [path addLineToPoint:CGPointMake(width, yOrigin)];
    yOrigin += distance;
}
[path closePath];

I then put it in a CAShapeLayer:

CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
[shapeLayer setFrame:aFrame];
[shapeLayer setPath:[bezierPath CGPath]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setMasksToBounds:YES];

However, when added to my scroll view, here is the result:

Path Image

Why does it do this? The first four lines are gray, almost! It looks like an aliasing problem, but the path is so straightforward.

Upvotes: 2

Views: 2731

Answers (2)

Vivien
Vivien

Reputation: 787

I got the same situation: vertical and horizontal black lines become grey.

It look like UiView 'snap' coordinates on pixels, not CALayer.

My solution to this problem is to add 0.5 on coodinates.

CGRect bounds_on_pixels(const CGRect& bounds) {

    const float f = 0.5f;
    return CGRectMake(bounds.origin.x+f, bounds.origin.y+f, bounds.size.width-1.f, bounds.size.height-1.f);
}

Note: -1.f on size is facultative.

Example of usage with a CAShapeLayer and a UIBezierPath

CAShapeLayer* shape_layer = [CAShapeLayer layer];
// initialize shape_layer...
shape_layer.path =
[
 UIBezierPath bezierPathWithRoundedRect:bounds_on_pixels(self.view.bounds)
 byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight
 cornerRadii:CGSizeMake(10.0, 10.0)
].CGPath;
// add shape_layer: [self.view.layer insertSublayer:shape_layer atIndex:0];

@tonklon : thx for the idea !

Upvotes: 0

tonklon
tonklon

Reputation: 6767

Yes this is an antialiasing problem.

The path you use has a width of 1 pt. In the above example the upper three instances of the CAShapeLayer are positioned exactly centered on a whole point value eg. 5.0, where the lower instance is positioned between two whole points eg. 14.5.

When the system renders your Layers to the screens pixels it antialiases the first three lines, drawing both touched pixels with 50% transparency. The lower line perfectly sit on a pixel and is therefore drawn in perfect black.

I hope a little ASCII Art helps understanding.

 pt          upper lines                           lower line
5.0|---------                                      -------------------------------
   |  pixel  --------------------------------------  perfectly aligned center 5.5
6.0|---------  line between pixels centered on 6.0 -------------------------------
   |  pixel  --------------------------------------
7.0|---------

Upvotes: 5

Related Questions