4

I have some troubles with CATextLayer, that could be due to me, but I didn't find any help on this topic. I am on OS X (on iOS it should be the same).

I create a CATextLayer layers with scale factor > 1 and what I get is a blurred text. The layer is rasterized before applying the scale, I think. Is this the expected behavior? I hope it is not, because it just makes no sense... A CAShapeLayer is rasterized after that its transformation matrix is applied, why the CATextLayer should be different?

In case I am doing something wrong... what is it??

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];

The solution I use at the moment is to set the contentsScale property of the layer to the scale factor. The problem is that this solution doesn't scale: if the scale factor of any of the parent layers changes, then contentsScale should be updated too. I should write code to traverse the layers tree to update the contentsScale properties of all CATextLayers... not exactly what I would like to do.

Another solution, that is not really a solution, is to convert the text to a shape and use a CAShapeLayer. But then I don't see the point of having CATextLayers.

A custom subclass of CALayer could help in solving this problem?

EDIT: Even CAGradientLayer is able to render its contents, like CAShapeLayer, after that its transformation matrix is applied. Can someone explain how it is possible?

EDIT 2: My guess is that paths and gradients are rendered as OpenGL display lists, so they are rasterized at the actual size on the screen by OpenGL itself. Texts are rasterized by Core Animation, so they are bitmaps for OpenGL.

I think that I will go with the contentsScale solution for the moment. Maybe, in the future, I will convert texts to shapes. In order to get best results with little work, this is the code I use now:

[CATransaction setDisableActions:YES];

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

[CATransaction setDisableActions:NO];
4

4 に答える 4

1

すべてのアプローチを試した後、私が現在使用しているソリューションは、CALayer のカスタム サブクラスです。私は CATextLayer をまったく使用しません。

次のカスタム セッター メソッドを使用して、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。バターのように機能します。

于 2013-11-13T11:50:52.720 に答える
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

于 2013-05-03T12:46:13.630 に答える
0

テキストを CAShapeLayer として描画するには、Apple サンプル コード「CoreAnimationText」を参照してください: http://developer.apple.com/library/mac/#samplecode/CoreAnimationText/Listings/Readme_txt.html

于 2013-05-06T10:26:45.823 に答える