CATextLayer太早进行栅格化并且模糊不清

时间:2013-05-03 09:22:47

标签: macos opengl core-animation cashapelayer catextlayer

我遇到CATextLayer的麻烦,这可能是由于我,但我没有找到任何关于这个主题的帮助。我在OS X上(在iOS上它应该是相同的。)

我创建了一个带有比例因子>的CATextLayer图层; 1,我得到的是一个模糊的文字。我认为,在应用比例之前,该图层是光栅化的。这是预期的行为吗?我希望它不是,因为它没有任何意义...... CAShapeLayer在应用了转换矩阵之后进行了栅格化,为什么CATextLayer应该不同?

如果我做错了什么......那是什么?

CATextLayer *layer = [CATextLayer layer];
layer.string = @"I like what I am doing";
layer.font = (__bridge CFTypeRef)[NSFont systemFontOfSize:24];
layer.fontSize = 24;
layer.anchorPoint = CGPointZero;
layer.frame = CGRectMake(0, 0, 400, 100);
layer.foregroundColor = [NSColor blackColor].CGColor;
layer.transform = CATransform3DMakeScale(2., 2., 1.);
layer.shouldRasterize = NO;
[self.layer addSublayer:layer];

我目前使用的解决方案是将图层的contentsScale属性设置为比例因子。问题是此解决方案无法扩展:如果任何父层的比例因子发生变化,那么contentsScale也应该更新。我应该编写代码来遍历层树,以更新所有CATextLayers的contentsScale属性......不完全是我想要做的。

另一种解决方案,即不是真正的解决方案,是将文本转换为形状并使用CAShapeLayer。但后来我没有看到CATextLayers的重点。

CALayer的自定义子类可以帮助解决这个问题吗?

编辑:即使CAGradientLayer能够渲染其内容,如CAShapeLayer,之后应用其变换矩阵。有人可以解释一下它是如何可能的吗?

编辑2:我的猜测是路径和渐变呈现为OpenGL显示列表,因此它们在OpenGL本身的屏幕上以实际大小进行栅格化。文本由Core Animation栅格化,因此它们是OpenGL的位图。

我认为我暂时会使用contentsScale解决方案。也许,在将来,我会将文本转换为形状。为了在没什么工作的情况下获得最佳效果,这就是我现在使用的代码:

[CATransaction setDisableActions:YES];

CGFloat contentsScale = ceilf(scaleOfParentLayer);
// _scalableTextLayer is a CATextLayer
_scalableTextLayer.contentsScale = contentsScale;
[_scalableTextLayer displayIfNeeded];

[CATransaction setDisableActions:NO];

4 个答案:

答案 0 :(得分:1)

尝试所有方法后,我现在使用的解决方案是CALayer的自定义子类。我根本不使用CATextLayer。

我使用此自定义setter方法覆盖contentsScale属性:

- (void)setContentsScale:(CGFloat)cs
{
    CGFloat scale = MAX(ceilf(cs), 1.); // never less than 1, always integer
    if (scale != self.contentsScale) {
        [super setContentsScale:scale];
        [self setNeedsDisplay];
    }
}

属性的值始终四舍五入为整数上限。当舍入值更改时,必须重新绘制图层。

我的CALayer子类的显示方法创建了一个位图图像,其中包含文本大小乘以contentsScale因子和屏幕比例因子。

- (void)display
{
    CGFloat scale = self.contentsScale * [MyUtils screenScale];

    CGFloat width = self.bounds.size.width * scale;
    CGFloat height = self.bounds.size.height * scale;

    CGContextRef bitmapContext = [MyUtils createBitmapContextWithSize:CGSizeMake(width, height)];

    CGContextScaleCTM(bitmapContext, scale, scale);
    CGContextSetShouldSmoothFonts(bitmapContext, 0);

    CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)(_text));

    CGContextSetTextPosition(bitmapContext, 0., self.bounds.size.height-_ascender);
    CTLineDraw(line, bitmapContext);
    CFRelease(line);

    CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
    self.contents = (__bridge id)(image);
    CGImageRelease(image);

    CGContextRelease(bitmapContext);
}

当我更改层次结构的根层的比例因子时,我循环所有文本层并将contentsScale属性设置为相同的因子。仅当比例因子的舍入值发生变化时才会调用显示方法(即如果前一个值为1.6,现在我设置为1.7,则没有任何反应。但如果新值为2.1,则重新显示图层。)

重绘速度方面的成本很低。我的测试是连续改变第3代上40个文本层的层次结构的比例因子。 iPad兼容。它就像黄油一样。

答案 1 :(得分:0)

CATextLayer不同,因为底层CoreText呈现具有指定字体大小的字形(基于实验的有根据的猜测)。

您可以向父图层添加操作,只要它的比例发生变化,它就会更改文本图层的字体大小。

模糊也可能来自未对齐的像素。如果将文本图层置于非整数位置或超层层次结构中的任何变换,则会发生这种情况。

或者你可以继承CALayer,然后在drawInContext中使用Cocoa绘制文本: 看这里的例子: http://lists.apple.com/archives/Cocoa-dev/2009/Jan/msg02300.html http://people.omnigroup.com/bungi/TextDrawing-20090129.zip

答案 2 :(得分:0)

如果你想拥有CAShapeLayer的确切行为,那么你需要将你的字符串转换为贝塞尔曲线路径并让CAShapeLayer渲染它。这有点工作但是你会得到你正在寻找的确切行为。另一种方法是缩放fontSize。这样每次都会产生清晰的文本,但它可能不适合您的确切情况。

答案 3 :(得分:0)

要绘制文本,因为CAShapeLayer会查看Apple示例代码“CoreAnimationText”: http://developer.apple.com/library/mac/#samplecode/CoreAnimationText/Listings/Readme_txt.html