I want to create a mask using text and need to convert the text into an image. But after converting, I found that it did not match exactly with the original text. I conducted the following tests.
class MyView: NSView{
override func draw(_ dirtyRect: NSRect) {
guard let g = NSGraphicsContext.current?.cgContext else{ return }
let antialiasing = false //true or false is useless
g.setAllowsAntialiasing(antialiasing)
g.setShouldAntialias(antialiasing)
let text: NSString = "HELLO"
let pos: CGPoint = .init(x: 20, y: 20)
let font: NSFont = .init(name: "Arial Black", size: 96)!
var textAttr: [NSAttributedString.Key: Any] = [
.font : font
]
//Draw the blue text in new CGContext, generate an image, draw the image in current CGContext
let image = createImage(dirtyRect: dirtyRect) { g in
g.setAllowsAntialiasing(antialiasing)
g.setShouldAntialias(antialiasing)
textAttr[.foregroundColor] = NSColor.blue
text.draw(at: pos, withAttributes: textAttr)
}
g.draw(image!, in: dirtyRect, byTiling: false)
//Draw the yellow text with all same properties
let attr: [NSAttributedString.Key: Any] = [
.font : font,
.foregroundColor : NSColor.yellow
]
text.draw(at: pos, withAttributes: attr)
}
private func createImage(dirtyRect: NSRect, drawingHandler: ((_ g: CGContext) -> Void)?) -> CGImage?{
NSGraphicsContext.saveGraphicsState()
let g = CGContext(data: nil,
width: Int(dirtyRect.width),
height: Int(dirtyRect.height),
bitsPerComponent: 8,
bytesPerRow: 0,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
NSGraphicsContext.current = NSGraphicsContext(cgContext: g, flipped: false)
drawingHandler?(g)
let image = g.makeImage()
NSGraphicsContext.restoreGraphicsState()
return image
}
}
As can be seen, the two texts cannot completely overlap after being stacked, and the blue text below reveals a circle of edges.
I tried other createImage methods, such as creating an NSImage and drawing text on it, or creating a CGLayer and drawing text on this layer then create the image, and the results were similar.
The only perfect solution is to use the current CGContext and disable Anti-aliasing to generate text image. But the problem with this method is that the generated image will contain all the drawn content, some of which may not be necessary for me.
private func createImage(dirtyRect: NSRect, drawingHandler:((_ g: CGContext) -> Void)?) -> CGImage?{
drawingHandler?(NSGraphicsContext.current!.cgContext)
return NSGraphicsContext.current?.cgContext.makeImage()
}
Any solutions? Thanks.

Try printing out the
gs in bothdraw(_:)andcreateImage. They are of different types, so it is not surprising that they behave differently (though I cannot tell you exactly what the difference is).The two texts do overlap, if you draw into an
NSImage, using this initialiser, then convert that image toCGImageusing the current context.