Ugly looking text when drawing NSAttributedString

2019-01-23 18:33发布

问题:

I want to display strings inside CoreAnimation layers, but unfortunately CATextLayer is not enough, mostly because it's difficult to use when using constraints and you want to wrap the text.

I am using NSLayoutManager, using the following code (PyObjC):

NSGraphicsContext.saveGraphicsState()

# THIS SOLVES THIS ISSUE
CGContextSetShouldSmoothFonts(ctx, False)

graphics = NSGraphicsContext.graphicsContextWithGraphicsPort_flipped_(ctx, True)
NSGraphicsContext.setCurrentContext_(graphics)

height = size.height
xform = NSAffineTransform.transform();
xform.translateXBy_yBy_(0.0, height)
xform.scaleXBy_yBy_(1.0, -1.0)
xform.concat()

self.textContainer.setContainerSize_(size)

glyphRange = self.layoutManager.glyphRangeForTextContainer_(self.textContainer)

self.layoutManager.drawBackgroundForGlyphRange_atPoint_(glyphRange, topLeft)
self.layoutManager.drawGlyphsForGlyphRange_atPoint_(glyphRange, topLeft)

NSGraphicsContext.restoreGraphicsState()

This is all fine and working, but the only issue is that it produces bad-looking text (although it is antialised).

Here's the CATextLayer version: http://i39.tinypic.com/23h0h1d.png

And here's the NSLayoutManager version: http://i40.tinypic.com/2vv9rw5.png

Anything I'm missing?

回答1:

I'm answering this because the coretext-dev archives are not searchable, and Aki Inoue from Apple just answered my question:

Since CALayer cannot represent subpixel color (aka font smoothing), you need to disable it. I believe CATextLayer does it by default.

Do CGContextSetShouldSmoothFonts(context, false).

Thanks, Aki!

Another comment by Milen Dzhumerov:

I don't believe this is accurate. We're drawing text into CALayers with subpixel anti-aliasing. You just have to make sure that you've drawn behind the text before drawing the text itself. See http://www.cocoabuilder.com/archive/message/cocoa/2008/3/28/202581 for references.

Milen is correct, in case you know the background colour beforehand, you can do:

CGContextSetRGBFillColor(ctx, r, g, b, a)
CGContextFillRect(ctx, (topLeft, size))
CGContextSetShouldSmoothFonts(ctx, True)

And you get pretty sub-pixel anti-aliased text. However, if you don't know the background colour, you need to turn off font smoothing or you'll get garbled results.