Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ugly looking text when drawing NSAttributedString in CGContext

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:

And here's the NSLayoutManager version:

Anything I'm missing?

like image 559
orestis Avatar asked Apr 03 '09 21:04

orestis


1 Answers

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.

like image 128
orestis Avatar answered Nov 07 '22 00:11

orestis