Нарисуйте точку, используя NSBezierPath - PullRequest
0 голосов
/ 01 сентября 2018

Я хочу создать одну точку размера пикселя. Я предположил, что способ сделать это - нарисовать линию, используя NSBezierPath с начальной точкой, равной конечной точке (см. Код ниже), но при этом получается полностью пустое окно.

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, тогда он создаст квадрат с длиной в два пикселя, но также добавит еще один слой размытия длиной в пиксель (см. Изображение ниже, увеличено для деталей).

pictureOfSquare

Однако я хочу просто поместить маленький синий квадрат размером в один пиксель без размытия. Кто-нибудь знает, как я могу это сделать?

Ответы [ 3 ]

0 голосов
/ 02 сентября 2018

Помимо решения Роба Мэйоффа, я нашел другое решение, используя 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, вы можете получить ровно один пиксель с затенением.

0 голосов
/ 05 сентября 2018

Чтобы нарисовать точку в точке: x, y размера: ширина, высота.

NSBezierPath(ovalIn: CGRect(x: 5.5, y: 5.5, width: 4.0, height: 4.0)).fill()

Чтобы точно настроить размещение и размер в пикселях, используйте десятичные дроби в аргументах Double или CGFloat.

0 голосов
/ 01 сентября 2018

У вас есть несколько проблем:

Я хочу создать одну точку размера пикселя.

Ты уверен? Что делать, если вы находитесь на дисплее 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()

Результат:

dot image

Однако вы можете предпочесть просто заполнить прямоугольник напрямую:

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()

Результат:

dot image 2

...