你有几个问题:
我想创建一个像素大小的点。
你确定吗?如果你在 Retina 显示器上怎么办?如果您要绘制到将在 600 dpi 激光打印机上打印的 PDF,该怎么办?
我认为这样做的方法是使用 NSBezierPath 绘制一条线,起点等于终点
这是一种绘制lineWidth 大小的点的方法,如果您将lineCap 属性设置为.round 或.square。默认的lineCap 是.butt,这会导致一个空笔划。如果将lineCap 设置为.round 或.square,则会得到一个非空笔划。
let fromPoint = NSMakePoint(CGFloat(100) , CGFloat(100))
let toPoint = NSMakePoint(CGFloat(100) , CGFloat(100))
path.move(to: fromPoint)
path.line(to: toPoint)
您需要了解坐标网格与像素的关系:
在非 Retina 屏幕上,整数坐标默认位于像素边缘,而不是像素中心。沿整数坐标的笔划跨越像素之间的边界,因此(使用lineWidth = 1.0)您会得到多个部分填充的像素。要填充单个像素,您需要使用以.5(像素中心)结尾的坐标。
在 Retina 屏幕上,整数和半整数坐标默认位于像素的边缘,而不是像素的中心。沿整数或半整数坐标的笔划跨越像素之间的边界。使用lineWidth = 1.0,两边的像素都被完全填满了。如果您只想填充单个像素,则需要使用以.25 或.75 结尾的坐标以及lineWidth 的0.5。
在未缩放的 PDF 上下文中,1.0 的 lineWidth 名义上对应于 1/72 英寸,填充像素的数量取决于输出设备。
path.lineWidth = 1.0
这只能在非 Retina 屏幕上为您提供“单个像素大小的点”(或者如果您缩放了图形上下文)。如果您真的想要 Retina 屏幕上的单个像素点,则需要对其进行调整。但是你最好坚持点而不是像素。
说了这么多,以下是创建和描边NSBezierPath 以填充一个像素的方法:
import AppKit
func dotImage() -> CGImage {
let gc = CGContext(data: nil, width: 20, height: 20, bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpace(name: CGColorSpace.sRGB)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
let nsGc = NSGraphicsContext(cgContext: gc, flipped: false)
NSGraphicsContext.current = nsGc; do {
let path = NSBezierPath()
path.move(to: .init(x: 10.5, y: 10.5))
path.line(to: .init(x: 10.5, y: 10.5))
path.lineWidth = 1
path.lineCapStyle = .round
NSColor.blue.set()
path.stroke()
}; NSGraphicsContext.current = nil
return gc.makeImage()!
}
let image = dotImage()
结果:
但是,您可能更喜欢直接填充矩形:
func dotImage2() -> CGImage {
let gc = CGContext(data: nil, width: 20, height: 20, bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpace(name: CGColorSpace.sRGB)!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
let nsGc = NSGraphicsContext(cgContext: gc, flipped: false)
NSGraphicsContext.current = nsGc; do {
NSColor.blue.set()
CGRect(x: 10, y: 10, width: 1, height: 1).fill()
}; NSGraphicsContext.current = nil
return gc.makeImage()!
}
let image2 = dotImage2()
结果: