【问题标题】:Draw a Point using NSBezierPath使用 NSBezierPath 绘制一个点
【发布时间】:2018-09-01 15:03:15
【问题描述】:

我想创建一个像素大小的点。我假设这样做的方法是使用 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,那么它将创建一个长度为两个像素的正方形,但还会添加另一个像素长度的模糊层(见下图,放大看细节)。

然而,我想做的只是放一个像素大小的蓝色小方块,没有模糊。有谁知道我该怎么做?

【问题讨论】:

  • 注意,你不应该使用NSMakePoint - 而是使用CGPoint(x:,y:)
  • 试试CGPoint(x:100.5, y: 100.5)
  • 如果你想通过 bezierPath 绘制一个矩形,你需要绘制每一行。意味着在 path.move 之后你需要在左下角或右上角添加一条线,就像你想要的那样,而不是将下一行添加到下一个角,直到你到达你的位置。起点。

标签: swift cocoa nsbezierpath


【解决方案1】:

你有几个问题:

我想创建一个像素大小的点。

你确定吗?如果你在 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 结尾的坐标以及lineWidth0.5

  • 在未缩放的 PDF 上下文中,1.0lineWidth 名义上对应于 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()

结果:

【讨论】:

    【解决方案2】:

    在 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 的值,你可以得到恰好一个像素的阴影。

    【讨论】:

      【解决方案3】:

      在:x,y 处绘制一个点,大小为:width,height。

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

      要微调“像素”的位置和大小,请在 DoubleCGFloat 参数中使用小数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-30
        • 2023-03-21
        • 2020-08-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多