【问题标题】:UIBezierPath stroke 1px line and fill 1px width rectangle - different results.UIBezierPath 描边 1px 的线条并填充 1px 宽度的矩形 - 不同的结果。
【发布时间】:2025-12-12 10:35:02
【问题描述】:

这是一个简单的绘图

    

- (void)drawRect:(CGRect)rect
{
    //vertical line with 1 px stroking
    UIBezierPath *vertLine = [[UIBezierPath alloc] init];
    [vertLine moveToPoint:CGPointMake(20.0, 10.0)];
    [vertLine addLineToPoint:CGPointMake(20.0, 400.0)];
    vertLine.lineWidth = 1.0;
    [[UIColor blackColor] setStroke];
    [vertLine stroke];

    //vertical rectangle 1px width 
    UIBezierPath *vertRect= [UIBezierPath bezierPathWithRect:CGRectMake(40.0, 10.0, 1.0, 390.0)];
    [[UIColor blackColor] setFill];
    [vertRect fill];

}

在非视网膜 3GS 和模拟器上,第一行模糊,看起来比 1 px 宽,但第二行清晰。

不幸的是,我既没有 iPhone4 也没有新的 iPad 来测试,但在视网膜模拟器上,两条线看起来是一样的。

问题:对于非视网膜和视网膜设备,矩形而不是描边是获得相同结果的唯一方法吗?

【问题讨论】:

    标签: ios drawing uibezierpath


    【解决方案1】:

    您正在填充矩形的内部,但您正在从中心抚摸线条。由于两种情况下的坐标(矩形的角以及直线中的起点和终点坐标)都定义为整数值(没有分数),因此坐标位于精确的点边界上。

    我在上面谈到线的点时说“坐标”,以免将它们与屏幕上的点混淆。出于同样的原因,我还说“点边界”而不是“像素边界”。 iOS 用所谓的“点”而不是像素来定义它的坐标和所有点。点是与分辨率无关的测量。视网膜和非视网膜设备在屏幕上的点数相同,只是它们对应的实际像素数不同。

    让我们看一下在点边界上划一条线(就像在你的问题中一样)与填充角在点边界上的矩形相比:

    在下面的插图中,我在非视网膜屏幕和视网膜屏幕上用黑色抚摸一条线并用橙色填充一个矩形。我还用蓝色勾勒出了线条和矩形。在这两种情况下,您都可以看到该分辨率下一个点的大小,并将其与实际的像素网格进行比较。

    在非视网膜的情况下,您可以看到尝试使用 1 点线(在这种情况下对应于 1 像素线宽)从中心描边线将填充顶部和一半的像素的一半在下面的像素上。由于像素仅填充了一半,因此这些像素的不透明度为 50%。这会导致颜色变浅(在白色背景上)。由于顶部和下方的像素都是填充的,因此抚摸填充顶部和下方的像素。这使得线条看起来好像是 2 个像素宽而不是 1 个像素。

    您可以快速将其与内部填充的矩形进行比较。

    视网膜屏幕上的相同情况看起来不同。在这种情况下,一个点的大小是相同的,但它由 4 个像素而不是 1 个像素组成。这一次,当划线时,线上方的半点和线下方的半点将完全填满该行像素上面和下面因为更高分辨率的屏幕。这意味着线条看起来好像是 1 磅宽,并且颜色看起来完全不透明。

    我们还可以看到填充的矩形看起来是一样的。


    要解决此问题,您可以将线的点放在半像素上。在低分辨率设备上从中心划线意味着该线向上延伸半个点,向下延伸半个点。由于线条的中心现在位于点的中心,这意味着描边线完全位于像素内,线条看起来很清晰。这样做不会对视网膜线产生任何影响,因为向下(或向上)移动半个点,仍然意味着您完全填充了上方和下方的像素。

    在下图中(针对视网膜),我同时显示了点网格和像素网格。

    【讨论】:

      【解决方案2】:

      使用 stroke 和 fill 得到不同结果的原因是它们对参数的解释不同。

      Stroke 在坐标的每一侧添加一半的线宽。所以,你的点是 20.0,线宽是 1px。理论上,结果将是 (19.5-20.5) 之间的 1 像素黑线。由于设备屏幕上没有任何非整数像素,它将在 (19-21) 之间转换为 2 像素灰度/模糊线。为了避免这种情况,您需要将每个坐标与 0.5 相加(如在 CGPointMake(20.5, 10.5) 中),这样宽度就不会再在像素之间划分。

      但是,填充中的参数用于设置要填充的区域的边框,CGRectMake(40.0, 10.0, 1.0, 390.0) 表示介于 (40 - 41) 之间的区域。因此,像素上没有小数部分看起来模糊。

      【讨论】:

        最近更新 更多