У вас есть несколько проблем:
Я хочу создать одну точку размера пикселя.
Ты уверен? Что делать, если вы находитесь на дисплее Retina? Что, если вы рисуете в PDF-файл, который будет напечатан на лазерном принтере с разрешением 600 точек на дюйм?
Я предположил, что способ сделать это - нарисовать линию, используя 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, lineWidth
из 1.0
номинально соответствует 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()
Результат:
