如何使用NSBezierPath获得1像素线?

时间:2011-11-04 22:49:39

标签: cocoa nsbezierpath

我正在开发一个自定义控件。其中一个要求是绘制线条。虽然这有效,但我注意到我的1像素宽线看起来并不像1像素宽线 - 我知道,它们不是像素,但你知道我的意思。它们看起来更像是两个或三个像素宽。当我绘制一条带有1像素短划线和2像素间隙的虚线时,这变得非常明显。 1像素破折号实际上看起来像小线而不是点。

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

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

有什么想法吗?

4 个答案:

答案 0 :(得分:25)

Bezier路径以其路径为中心绘制,因此如果沿X坐标绘制1像素宽的路径,该线实际沿Y坐标绘制{-0.5,0.5}解决方案通常是通过以下方式偏移坐标: 0.5使得线不在子像素边界中绘制。您应该能够将边界框移动0.5,以获得更清晰的绘图行为。

答案 1 :(得分:18)

Francis McGrew已经给出了正确答案,但自从我做了一次这样的介绍后,我想我会添加一些照片。

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

The rectangle you tell Quartz to draw when you specify integral coordinates

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

Half pixels averaged out when drawing between pixels

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

Coordinates offset by 0.5 towards lower right

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

Comparison between offset stroked and non-offset filled rectangle

由于人们通常希望矩形在指定的矩形内行程,你通常做的是向中心偏移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/我将更新并修改此描述并添加更多图像。

答案 2 :(得分:9)

答案是(隐藏)在Apple Docs中:

为了避免在绘制一个点宽的水平或垂直线时进行抗锯齿处理,如果线条的宽度为奇数个像素,则必须将位置偏移0.5点到整个任意一侧 - 编号位置

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

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

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

答案 3 :(得分:6)

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

[[NSGraphicsContext currentContext] setShouldAntialias: NO];

这给出了一条清晰的1像素线。绘图后再将其重新打开。

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

编辑: 更具体地说,我单独更改了x和y坐标,并且偏移量为0.5。

编辑2: 我一定做错了,因为用0.5的偏移量改变坐标实际上是行得通的。最终结果优于通过关闭抗锯齿获得的结果,因此我将让Francis MsGrew回答接受的答案。