如何在调整大小期间防止CALayer图像边缘拉伸?

时间:2014-02-14 04:09:56

标签: macos cocoa calayer appkit

我将CALayer的.contents设置为CGImage,从绘制到NSBitMapImageRep中派生出来。

据我所知,从文档和WWDC视频中,将图层的.contentsCenter设置为类似{{0.5,0.5},{0,0}}的NSRect,并结合kCAGravityResize的.contentsGravity应该导致Core动画通过拉伸中间像素,水平顶部和底部以及垂直侧面来调整图层大小。

这几乎可行,但并不完全。图层或多或少地正确调整大小,但如果我在位图的边缘绘制线条,当我调整窗口大小时,可以看到线条的厚度非常轻微地波动。在调整大小降低到原始图层大小的1/4左右之前,它的微妙程度几乎不会成为问题,低于这一点,线条可以变薄并完全消失。如果我以不同的尺寸多次绘制位图,则线条厚度的微小差异非常明显。

我最初研究了一个像素对齐问题,但不能这样,因为当我调整RH边缘的大小时,静止LH边缘(例如)的厚度会波动。它发生在1x和2x屏幕上。


这是一些测试代码。它是来自图层支持的NSView子类的updateLayer方法(我正在使用替代的非DrawRect绘制路径):

- (void)updateLayer {

    id image = [self imageForCurrentScaleFactor]; // CGImage 

    self.layer.contents = image;
    // self.backingScaleFactor is set from  the window's backingScaleFactor
    self.layer.contentsScale = self.backingScaleFactor; 
    self.layer.contentsCenter = NSMakeRect(0.5, 0.5, 0, 0);
    self.layer.contentsGravity = kCAGravityResize;

}

这里有一些测试绘图代码(创建上面的imageForCurrentScaleFactor提供的图像):

    CGFloat width = rect.size.width;
    CGFloat height = rect.size.height;
    NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
                                                                         pixelsWide: width * scaleFactor
                                                                         pixelsHigh: height * scaleFactor
                                                                      bitsPerSample: 8
                                                                    samplesPerPixel: 4
                                                                           hasAlpha: YES
                                                                           isPlanar: NO
                                                                     colorSpaceName: NSCalibratedRGBColorSpace
                                                                        bytesPerRow: 0
                                                                       bitsPerPixel: 0];

    [imageRep setSize:rect.size];

    [NSGraphicsContext saveGraphicsState];

    NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
    [NSGraphicsContext setCurrentContext:ctx];

    [[NSColor whiteColor] setFill];
    [NSBezierPath fillRect:rect];

    [[NSColor blackColor] setStroke];
    [NSBezierPath setDefaultLineWidth:1.0f];
    [NSBezierPath strokeRect:insetRect];

    [NSGraphicsContext restoreGraphicsState];

    // image for CALayer.contents is now [imageRep CGImage]

2 个答案:

答案 0 :(得分:0)

解决方案(如果您正在谈论我认为您正在讨论的问题)是在图像的外边缘处形成透明像素边距。一个像素厚,一直到处都可以。原因是问题(如果它是我认为的问题)仅出现在触摸图像外边缘的可见像素上。因此,我们的想法是让没有可见像素触及图像的外边缘。

答案 1 :(得分:0)

我找到了一个实际的答案,但是会对那些知道如何运作的人详细填写评论感兴趣。

问题确实与CALayer的拉伸方式有关。我正在绘制一个任意大小的位图,基于(如CALayer文档建议的那样)使用零宽度和高度的.contentsCenter实际上会进行九部分图像拉伸,选择单个中心像素作为中央拉伸部分。将此位图作为图层的.contents,然后我可以将CALayer的大小调整为任何所需的大小(向下或向上)。

原来,'画布尺寸'是问题。 CALayer拉伸边缘部分的方式发生了奇怪的事情(至少在调整大小时)。相反,通过使绘制的初始框架变得很小(即,足够大以适合我的轮廓绘图加上中心拉伸部分的几个像素),在拉伸过程中没有任何虚假的东西进入边缘。

如果使用rect创建的位图刚好足以适合内容和可伸展的中心像素,则位图会正确拉伸,即:

NSRect rect = NSMakeRect(0, 0, lineWidth * 2 + 2, lineWidth * 2 + 2);

这个微小的图像完美地延伸到任何更大的尺寸。