CATextLayer is rasterized too early and it is blurry

I have some problems with CATextLayer that may be due to me, but I did not find any help on this topic. I'm on OS X (on iOS it should be the same).

I create CATextLayer layers with a zoom factor> 1 and I get blurry text. I think the layer is rasterized before applying the scale. Is this expected behavior? I hope this is not the case because it just doesn’t make sense ... CAShapeLayer is rasterized after the transformation matrix is ​​applied, why should the CATextLayer be different?

If I do 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'm currently using is to set the contentsScale property of the layer to a scale factor. The problem is that this solution does not scale: if the scale factor for any of the parent layers changes, then the contentsScale must also be updated. I have to write code to traverse the layer tree to update the contentsScale properties of all CATextLayers ... not quite what I would like to do.

Another solution, which is not really a solution, is to convert text to form and use CAShapeLayer. But then I see no reason to have CATextLayers.

Can a custom subclass of CALayer help solve this problem?

EDIT: even a CAGradientLayer can display its contents, such as CAShapeLayer, after which its transformation matrix is ​​applied. Can someone explain how this is possible?

EDIT 2: I assume that the paths and gradients are displayed as OpenGL display lists, so they are rasterized by the actual size on the screen by OpenGL itself. Texts are rasterized by Core Animation, so they are bitmaps for OpenGL.

I think for now I will move on with the contentScale solution. Perhaps in the future I will convert texts to forms. To get the best results with a little work, this is the code I'm using now:

 [CATransaction setDisableActions:YES]; CGFloat contentsScale = ceilf(scaleOfParentLayer); // _scalableTextLayer is a CATextLayer _scalableTextLayer.contentsScale = contentsScale; [_scalableTextLayer displayIfNeeded]; [CATransaction setDisableActions:NO]; 
+4
source share
4 answers

After using all the approaches, the solution I'm using now is a custom subclass of CALayer. I do not use CATextLayer at all.

I override the contentsScale property with this custom method method:

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

The property value is always rounded to the top integer value. When the rounded value changes, the layer should be redrawn.

The display method of my CALayer subclass creates a bitmap for the size of the text, multiplied by the contentsScale factor and the screen zoom factor.

 - (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); } 

When I change the scale factor of the root layer of my hierarchy, I loop on all text layers and set the contentsScale property to the same factor. The display method is only called if the rounded value of the scale factor changes (i.e., if the previous value was 1.6, and now I set 1.7, nothing happens, but if the new value is 2.1, then this layer will be re-displayed).

The cost in terms of redrawing speed is a bit. My test is to constantly change the scale factor of the hierarchy of 40 text layers on the 3rd gene. IPad. It works like butter.

+1
source

CATextLayer differs in that the core CoreText displays glyphs with the specified font size (an educated guess based on experiments).

You can add an action to the parent layer so that once it zooms out, it changes the font size of the text layer.

Blurring may also occur due to inconsistent pixels. This can happen if you place the text layer in a non-target position or any transformation in the hierarchy of super layers.

Alternatively, you can subclass CALayer and then draw the text using Cocoa in drawInContext: see example here: http://lists.apple.com/archives/Cocoa-dev/2009/Jan/msg02300.html http: // people.omnigroup.com/bungi/TextDrawing-20090129.zip

0
source

If you want to have the exact behavior of CAShapeLayer, you will need to convert your string to a bezier path and render CAShapeLayer rendering. This is a bit of work, but then you will have the exact order you are looking for. An alternative approach is to scale fontSize. This gives clear text every time, but it may not be appropriate for the situation.

0
source

To draw text like CAShapeLayer, see the Apple CoreAnimationText example code: http://developer.apple.com/library/mac/#samplecode/CoreAnimationText/Listings/Readme_txt.html

0
source

Source: https://habr.com/ru/post/1478949/


All Articles