Search code examples
cocoatextcore-animationblurrycatextlayer

Blurry CATextLayer with scale transform


I'm at my wits end. I've tried what I think is everything to get rid of CATextLayer blurriness to no avail. Admittedly, some things help, but text is still far too blurry when zoomed in 2x and nowhere near the crispness of non-CATextLayer text.

Here's my situation. I've got a layer-hosted view which can contain many sublayers, including CATextLayers (inside a custom CALayer container). Now, the user can zoom the main content layer (which contains all the sublayer content) and this is done by applying a scale transform to the content layer.

Here are the things I'm currently doing. Most make a difference, though not a huge difference. If I zoom in all the way (which is 2x scale factor) I get seriously blurry text.

  • Ensure integral coordinates - works and makes an appreciable difference, but still far too blurry at 2x scale

  • Turn off shadows on CATextLayer container layers - makes a difference though not at 2x scale

  • Ensure background color of text layer is fully opaque - seems to make a difference though not at 2x scale

  • Set the contentsScale property to the window backing scale factor for all layers - seems to make a difference though not at 2x scale

  • Override -drawInContext: of this custom CATextLayer subclass to smooth, anti-alias, and sub-pixel quantize the hell out of everything - fonts render much truer, but still blurry at 2x scale

Code fragment:

- (void) drawInContext:(CGContextRef)context
{
    // Allow font smoothing.
    CGContextSetAllowsAntialiasing(context, YES);
    CGContextSetAllowsFontSmoothing(context, YES);
    CGContextSetAllowsFontSubpixelPositioning(context, YES);
    CGContextSetAllowsFontSubpixelQuantization(context, YES);

    // Set drawing attributes.
    CGContextSetStrokeColorWithColor(context, self.foregroundColor);
    CGContextSetFillColorWithColor(context, self.backgroundColor);
    CGContextFillRect(context, self.bounds);

    // Font smoothing.
    CGContextSetShouldAntialias(context, YES);
    CGContextSetShouldSmoothFonts(context, YES);
    CGContextSetShouldSubpixelPositionFonts(context, YES);
    CGContextSetShouldSubpixelQuantizeFonts(context, YES);

    [super drawInContext:context];
}

What am I missing?


Solution

  • For the sharpest possible text, in addition to everything mentioned in the original question, the solution that had the biggest impact is to double the contentsScale value for text layers to twice the window backing scale factor - if you want to support crisp text at 2x zoom.

    If you want to support crisp text at higher zoom values then use the max zoom factor for your application as an adjusted contentsScale. For example, you can do this by overriding -setContentsScale: for a custom CATextLayer subclass:

    - (void) setContentsScale:(CGFloat)contentsScale
    {
        [super setContentsScale:contentsScale * kMaxZoomFactor];
    }
    

    Note, be sure that changes to view backing properties (e.g., change to screen with higher resolution) are propagated to your layer-hosted hierarchy by overriding -viewDidChangeBackingProperties for your layer-hosted view:

    - (void) viewDidChangeBackingProperties
    {
        // Propagate self.window.backingScaleFactor change to layer hierarchy.
    }