【问题标题】:How to get a 1 pixel line with NSBezierPath?如何使用 NSBezierPath 获得 1 像素线?
【发布时间】:2014-03-08 19:35:08
【问题描述】:

我正在开发一个自定义控件。要求之一是画线。虽然这可行,但我注意到我的 1 像素宽的线条看起来并不像 1 像素宽的线条 - 我知道,它们并不是真正的像素,但你知道我的意思。它们看起来更像是两三个像素宽。当我用 1 像素虚线和 2 像素间隙绘制虚线时,这一点变得非常明显。 1 像素的虚线实际上看起来像细线而不是点。

我已阅读 Cocoa Drawing 文档,虽然 Apple 提到了 setLineWidth 方法,但将线宽更改为小于 1.0 的值只会使线条看起来更模糊,而不是更细。

所以,我怀疑还有其他因素影响了我的线条的外观。

有什么想法吗?

【问题讨论】:

    标签: cocoa nsbezierpath


    【解决方案1】:

    Bezier 路径是以它们的路径为中心绘制的,所以如果你沿着 X 坐标画一条 1 像素宽的路径,那么这条线实际上是沿着 Y 坐标画的 { -0.5, 0.5 } 解决方法通常是将坐标偏移0.5 以便在子像素边界不绘制线。您应该能够将边界框移动 0.5 以获得更清晰的绘图行为。

    【讨论】:

    • 对于一个框,您通常希望将其插入或超出笔划宽度的一半,而不是移动它。
    • Apple 没有记录如此重要的方面,这真是令人惊讶。
    • 在可可和石英的绘图指南中有介绍
    【解决方案2】:

    Francis McGrew 已经给出了正确的答案,但是因为我曾经做过一次关于这个的演示,所以我想我应该添加一些图片。

    这里的问题是 Quartz 中的坐标位于像素之间的交点处。这在填充矩形时很好,因为位于坐标内的每个像素都会被填充。但是线条在技术上(数学上!)是不可见的。要绘制它们,Quartz 必须实际绘制一个具有给定线宽的矩形。这个矩形以坐标为中心:

    所以当你让 Quartz 用整数坐标描边一个矩形时,它的问题是它只能绘制整个像素。但是在这里你看到我们有半个像素。所以它的作用是平均颜色。对于 50% 黑色(线条颜色)和 50% 白色(背景)线条,它只是将每个像素绘制为灰色:

    这就是您褪色的图纸的来源。修复现在很明显:不要在像素之间绘制,您可以通过将点移动半个像素来实现,因此您的坐标以所需像素为中心:

    当然,只是抵消可能不是您想要的。因为如果您将填充的变体与描边的变体进行比较,则描边在右下角大一个像素。如果你是例如剪切到矩形,这将切断右下角:

    由于人们通常期望矩形在指定的矩形内描边,因此您通常会向中心偏移0.5,因此右下角实际上向上移动了一个像素。或者,许多绘图应用程序从中心偏移 0.5,以避免边框和填充之间的重叠(当您使用透明度进行绘图时,这看起来很奇怪)。

    请注意,这只适用于 1x 屏幕。 2x Retina 屏幕实际上表现出这个问题的方式不同,因为下面的每个像素实际上是由 4 个 Retina 像素绘制的,这意味着它们实际上可以绘制半个像素。但是,如果您想要一条 0.5pt 的尖线,您仍然会遇到同样的问题。此外,由于 Apple 将来可能会推出其他 Retina 屏幕,例如每个像素都由 9 个 Retina 像素 (3x) 组成,或者其他什么,你真的不应该依赖这个。相反,现在有 API 调用将矩形转换为“支持对齐”,无论您是运行 1x、2x 还是虚构的 3x,它都会为您执行此操作。

    PS - 因为我费力地写了这一切,所以我把它放在我的网站上:http://orangejuiceliberationfront.com/are-your-rectangles-blurry-pale-and-have-rounded-corners/ 在那里我会更新和修改这个描述并添加更多图片。

    【讨论】:

    【解决方案3】:

    答案是(埋在)Apple Docs 中:

    "为避免在绘制一个点宽的水平或垂直线时出现抗锯齿,如果该线的宽度为奇数个像素,则必须将该位置偏移 0.5 个点到整体的任一侧-编号位置"

    隐藏在 Drawing and Printing Guide for iOS: iOS Drawing Concepts 中,但在当前的标准 (OS X) Cocoa Drawing Guide 中找不到任何具体内容。

    至于调用setDefaultLineWidth: 的效果docs 也说明:

    "0 的宽度被解释为可以在特定设备上渲染的最细线。实际渲染的线宽可能与指定的宽度相差多达 2 个设备像素,具体取决于设备的位置相对于像素网格和当前抗锯齿设置的线条。线条的宽度也可能受到活动图形上下文的当前变换矩阵中指定的缩放因子的影响。"

    【讨论】:

    • 至于 0 的宽度,我试过了,只是没有得到一条线,而不是最细的线。
    • @Unome 耶。视网膜或普通旧显示器..?
    • 非视网膜显示屏。 Retina 显示屏显示正常。
    • edit:Retina 显示屏不显示模糊线,但是如果您将宽度设置为 0,它也不会显示任何线条,而是设备最细的线条(即非视网膜也有同样的问题)
    【解决方案4】:

    我发现一些信息表明这是由抗锯齿引起的。暂时关闭抗锯齿很容易:

    [[NSGraphicsContext currentContext] setShouldAntialias: NO];
    

    这会给出一条清晰的 1 像素线。画完再开机。

    我尝试了 Francis McGrew 建议的解决方案,将 x 坐标偏移 0.5,但这对我的线条外观没有任何影响。

    编辑: 更具体地说,我分别更改了 x 和 y 坐标以及 0.5 的偏移量。

    编辑 2: 我一定做错了什么,因为以 0.5 的偏移量更改坐标实际上确实有效。最终结果比通过关闭抗锯齿获得的结果要好,所以我会让 Francis MsGrew 的答案成为公认的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-27
      • 1970-01-01
      • 2011-07-01
      • 2021-06-28
      • 2013-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多