CAShapeLayer как маска для CALayer: закругленные углы вытянуты - PullRequest
7 голосов
/ 21 октября 2011

Я хочу замаскировать CALayer с помощью CAShapeLayer, потому что изменения формы можно анимировать.

Когда я использую CAShapeLayer в качестве маски, закругленные углы растягиваются.Однако, если я приму ту же форму, создам NSImage с ней и использую изображение, чтобы замаскировать мои CALayer, закругленные углы идеально подойдут.

Rounded corners with NSImage on the left, CAShapeLayer on the right

Вот код, который я использую для маскировки слоев (вы также можете скачать весь пример проекта ):

CGColorRef backgroundColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0f);

[self.window.contentView setLayer:[CALayer layer]];
[self.window.contentView setWantsLayer:YES];
[[self.window.contentView layer] setFrame:[self.window.contentView frame]];


CALayer *imageBasedMaskLayer = [CALayer layer];
[imageBasedMaskLayer setContents:(id)[[self maskWithSize:NSMakeSize(50, 50)] CGImageForProposedRect:NULL context:nil hints:nil]];
[imageBasedMaskLayer setFrame:CGRectMake(0, 0, 50, 50)];

CALayer *layerWithImageBasedMaskLayer = [CALayer layer];
[layerWithImageBasedMaskLayer setBackgroundColor:backgroundColor];
[layerWithImageBasedMaskLayer setMask:imageBasedMaskLayer];


CAShapeLayer *shapeBasedMaskLayer = [CAShapeLayer layer];
CGPathRef maskShape = [self newMaskPathWithFrame:NSMakeRect(0, 0, 50, 50)];
[shapeBasedMaskLayer setPath:maskShape];
[shapeBasedMaskLayer setFillRule:kCAFillRuleEvenOdd];
CGPathRelease(maskShape);
[shapeBasedMaskLayer setFrame:CGRectMake(0, 0, 50, 50)];

CALayer *layerWithShapeBasedMaskLayer = [CALayer layer];
[layerWithShapeBasedMaskLayer setBackgroundColor:backgroundColor];
[layerWithShapeBasedMaskLayer setMask:nil];
[layerWithShapeBasedMaskLayer setMask:shapeBasedMaskLayer];


[layerWithImageBasedMaskLayer setFrame:CGRectMake(50, 50, 50, 50)];
[layerWithShapeBasedMaskLayer setFrame:CGRectMake(120, 50, 50, 50)];
[[self.window.contentView layer] addSublayer:layerWithImageBasedMaskLayer];
[[self.window.contentView layer] addSublayer:layerWithShapeBasedMaskLayer];

CGColorRelease(backgroundColor);

И два метода, которые я использую для создания NSImageи CGPathRef.

- (NSImage *)maskWithSize:(CGSize)size;
{
    NSImage *maskImage = [[NSImage alloc] initWithSize:size];

    [maskImage lockFocus];
    [[NSColor blackColor] setFill];

    CGPathRef mask = [self newMaskPathWithFrame:CGRectMake(0, 0, size.width, size.height)];
    CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextAddPath(context, mask);
    CGContextFillPath(context);
    CGPathRelease(mask);

    [maskImage unlockFocus];

    return [maskImage autorelease];
}

- (CGPathRef)newMaskPathWithFrame:(CGRect)frame; 
{
    CGFloat cornerRadius = 3;

    CGFloat height = CGRectGetMaxY(frame);
    CGFloat width = CGRectGetMaxX(frame);

    CGMutablePathRef maskPath = CGPathCreateMutable();

    CGPathMoveToPoint(maskPath, NULL, 0, height-cornerRadius);
    CGPathAddArcToPoint(maskPath, NULL, 0, height, cornerRadius, height, cornerRadius);
    CGPathAddLineToPoint(maskPath, NULL, width-cornerRadius, height);
    CGPathAddArcToPoint(maskPath, NULL, width, height, width, height-cornerRadius, cornerRadius);
    CGPathAddLineToPoint(maskPath, NULL, width, cornerRadius);
    CGPathAddArcToPoint(maskPath, NULL, width, 0, width-cornerRadius, 0, cornerRadius);
    CGPathAddLineToPoint(maskPath, NULL, cornerRadius, 0);
    CGPathAddArcToPoint(maskPath, NULL, 0, 0, 0, cornerRadius, cornerRadius);

    CGPathCloseSubpath(maskPath);

    return maskPath;
}

Обратите внимание, что фактическая маска, которую я хочу создать, сложнее, чем скругленный прямоугольник, в противном случае я бы использовал некоторые из более простых методов рисования.Я пробовал различные CGPath функции рисования, проблема не исчезла.

Что мне здесь не хватает?

1 Ответ

2 голосов
/ 07 июля 2013

Это потому, что CAShapeLayer предпочитает скорость, а не точность. Вот почему это может быть иногда выключено.

Форма будет нарисована сглаженной, и, когда это возможно, она будет отображена в пространство экрана перед растеризацией, чтобы сохранить независимость разрешения. Однако некоторые виды операций обработки изображений, такие как фильтры CoreImage, применяемые к слою или его предкам, могут вызвать растеризацию в локальном координатном пространстве.

Примечание: Растеризация формы может способствовать скорости, а не точности. Например, пиксели с несколькими пересекающимися сегментами пути могут не давать точных результатов.

http://developer.apple.com/library/Mac/#documentation/GraphicsImaging/Reference/CAShapeLayer_class/Reference/Reference.html

Решение

Вы можете использовать

layerWithShapeBasedMaskLayer.cornerRadius = 3.0;

чтобы закруглить все углы. Вам больше не нужно CAShapeLayer. Вы даже можете продолжать использовать CAShapeLayer в качестве подслоя для анимации пути. Просто установите

layerWithShapeBasedMaskLayer.masksToBounds = YES;
...