使用 NSBezierPath 绘制一个点
Draw a Point using NSBezierPath
我想创建一个像素大小的点。我假设这样做的方法是使用 NSBezierPath 画一条线,起点等于终点(见下面的代码),但这样做会使一个完全空的 window。
func drawPoint() {
let path = NSBezierPath()
NSColor.blue.set()
let fromPoint = NSMakePoint(CGFloat(100) , CGFloat(100))
let toPoint = NSMakePoint(CGFloat(100) , CGFloat(100))
path.move(to: fromPoint)
path.line(to: toPoint)
path.lineWidth = 1.0
path.stroke()
path.fill()
}
但是,如果我将 toPoint 坐标从 100,100 更改为 101,101(或 100,100 以外的任何一组坐标),则会显示蓝色形状。有谁知道为什么会这样吗?
我发现的另一个问题是,如果你让 toPoint 的坐标等于(例如)101,101,那么它会创建一个长度为两个像素的正方形,但还会添加另一个像素长度的模糊层(见图下面,放大查看详细信息)。
然而,我想做的只是放一个只有一个像素大小的蓝色小方块,没有模糊。有谁知道我该怎么做?
你有几个问题:
I want to create a single pixel-size dot.
你确定吗?如果你在 Retina 显示屏上怎么办?如果您正在绘制将在 600 dpi 激光打印机上打印的 PDF 怎么办?
I assumed the way to do so was to draw a line using NSBezierPath with the starting point being equal to the end point
这是一种绘制 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
结尾的坐标和 0.5
的 lineWidth
。
在未缩放的 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()
结果:
在 rob mayoff 的解决方案之上,我想出了一个使用 NSGraphicsContext 的不同解决方案。我刚刚获取了 Core Graphics 上下文并关闭了抗锯齿(见下文)
let context = NSGraphicsContext.current?.cgContext
context?.setShouldAntialias(false)
然后,我创建了一个像素大小的路径:
context.setLineWidth(0.5)
context.setStrokeColor(color)
context.beginPath()
context.move(to: CGPoint(x: 100, y: 100))
context.addLine(to: CGPoint(x: 100.15, y: 100.15))
context.strokePath()
向直线添加端点时,我注意到通过给它一个大于 100 但小于 100.5 的值,您可以准确地得到一个像素的阴影。
要在:x,y 处画一个点,大小:宽,高。
NSBezierPath(ovalIn: CGRect(x: 5.5, y: 5.5, width: 4.0, height: 4.0)).fill()
要微调 "pixel" 位置和大小,请在 Double
或 CGFloat
参数中使用小数。
我想创建一个像素大小的点。我假设这样做的方法是使用 NSBezierPath 画一条线,起点等于终点(见下面的代码),但这样做会使一个完全空的 window。
func drawPoint() {
let path = NSBezierPath()
NSColor.blue.set()
let fromPoint = NSMakePoint(CGFloat(100) , CGFloat(100))
let toPoint = NSMakePoint(CGFloat(100) , CGFloat(100))
path.move(to: fromPoint)
path.line(to: toPoint)
path.lineWidth = 1.0
path.stroke()
path.fill()
}
但是,如果我将 toPoint 坐标从 100,100 更改为 101,101(或 100,100 以外的任何一组坐标),则会显示蓝色形状。有谁知道为什么会这样吗?
我发现的另一个问题是,如果你让 toPoint 的坐标等于(例如)101,101,那么它会创建一个长度为两个像素的正方形,但还会添加另一个像素长度的模糊层(见图下面,放大查看详细信息)。
然而,我想做的只是放一个只有一个像素大小的蓝色小方块,没有模糊。有谁知道我该怎么做?
你有几个问题:
I want to create a single pixel-size dot.
你确定吗?如果你在 Retina 显示屏上怎么办?如果您正在绘制将在 600 dpi 激光打印机上打印的 PDF 怎么办?
I assumed the way to do so was to draw a line using NSBezierPath with the starting point being equal to the end point
这是一种绘制 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
结尾的坐标和0.5
的lineWidth
。在未缩放的 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()
结果:
在 rob mayoff 的解决方案之上,我想出了一个使用 NSGraphicsContext 的不同解决方案。我刚刚获取了 Core Graphics 上下文并关闭了抗锯齿(见下文)
let context = NSGraphicsContext.current?.cgContext
context?.setShouldAntialias(false)
然后,我创建了一个像素大小的路径:
context.setLineWidth(0.5)
context.setStrokeColor(color)
context.beginPath()
context.move(to: CGPoint(x: 100, y: 100))
context.addLine(to: CGPoint(x: 100.15, y: 100.15))
context.strokePath()
向直线添加端点时,我注意到通过给它一个大于 100 但小于 100.5 的值,您可以准确地得到一个像素的阴影。
要在:x,y 处画一个点,大小:宽,高。
NSBezierPath(ovalIn: CGRect(x: 5.5, y: 5.5, width: 4.0, height: 4.0)).fill()
要微调 "pixel" 位置和大小,请在 Double
或 CGFloat
参数中使用小数。