gpt4 book ai didi

iphone - iPhone编程中核心图形中坐标的定位规律是什么?

转载 作者:行者123 更新时间:2023-12-03 20:23:06 26 4
gpt4 key购买 nike

我需要在视网膜显示器上绘制一些形状,因此我将逻辑点转换为视网膜显示器,如下所示:

self.inverseScale = (CGFloat)1.0/[UIScreen mainScreen].scale;
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, inverseScale, inverseScale);


在那之后定义下一步:

CGContextSetShouldAntialias(context, NO);
CGContextSetLineWidth(context, 1.0);


好的,现在我得到了640x960的视网膜显示尺寸,但是我对坐标有点困惑。
我认为如果需要绘制一些矩形框,则需要执行下一个:

CGContextSetFillColorWithColor(context, someBlackColor);
CGContextFillRect(context, displaySizeRect);
CGContextSetStrokeColorWithColor(context, white);
CGContextAddRect(context, CGRectMake(0, 0, 639, 959));
CGContextStrokePath(context);


但是相反,我发现我需要写:

CGContextAddRect(context, CGRectMake(0, 1, 639, 959));


代替:

CGContextAddRect(context, CGRectMake(0, 0, 639, 959));


为什么???

我的转换是错误的还是什么?

最佳答案

底部的编辑/添加

听起来您可能对坐标系如何映射到像素网格感到困惑。当您绘制到CGContext中时,您将绘制到一个“连续的”基于浮点的平面中。该连续平面被映射到显示器的像素网格上,使得整数值落在屏幕像素之间的线上。

从理论上讲,在任何给定设备的默认比例下(对于非视网膜,默认比例为1.0,对于视网膜为2.0),如果您从0,0-> 320,480绘制矩形,并使用100%不透明黑色笔触,宽度为1.0pt,您可以预期得到以下结果:


在非视网膜上,您的外部矩形将具有1像素宽
屏幕的不透明度为50%。
在视网膜上,您将有一个1像素
屏幕周围的矩形区域具有100%的不透明度。


这是由于零线正好位于显示器的边缘,介于显示器中的第一行/列像素和显示器中的第一行/列像素之间。在这种情况下,当您以默认比例绘制1pt线时,它会沿着路径的中心绘制,因此一半会从显示屏幕上绘制出来,并且在连续平面中,您会得到一条线,其宽度从- 0.5pt至0.5pt。在非视网膜显示上,它将以50%的不透明度变为1px的线条,在视网膜显示上将以100%的不透明度以1px的线条呈现。

编辑

OP说:


  我的目标是在视网膜显示器上绘制一些形状,并且每条线
  将为1像素宽度和100%不透明度。


这个目标没有什么要求关闭抗锯齿功能。如果您看到在水平和垂直线条上启用了抗锯齿功能的Alpha混合,则说明您没有在正确的位置或以正确的尺寸绘制线条。


  在这种情况下,函数:CGContextMoveToPoint(context,X,Y);
  在X轴上2个像素之间,引擎会选择合适的像素,然后在
  Y轴将选择较高的一个。但是在功能上:
  CGContextFillRect(context,someRect);它会像地图上那样填满
  像素网格(1到1)。


您看到此令人困惑的行为的原因是您已关闭了抗锯齿功能。能够看到和理解这里发生的事情的关键是保留抗锯齿功能,然后进行必要的更改,直到正确使用它为止。开始执行此操作的最简单方法是将CGContext的变换保持默认值不变,并更改传递给绘制例程的值。的确,您也可以通过转换CGContext来完成其中的一些工作,但这增加了在传递给任何CG例程的每个坐标上完成的数学步骤,因此您无法在调试器中介入,因此,我非常建议您从标准转换开始,并保留AA。在尝试弄乱上下文转换之前,您将需要全面了解CG绘图的工作原理。


  苹果是否定义了一些方法将图形映射到像素而不是
  像素之间的线条?


一句话,“不”,因为CGContext不是通用的位图(例如,您可能会绘制为PDF,因此您无法通过询问CGContext来知道)。您要绘制到的平面本质上是一个浮点平面。这就是它的工作原理。完全有可能使用浮点平面实现100%像素精确的位图绘制,但是要这样做,您必须了解这些东西的实际工作原理。

通过获取给定的默认CGContext并进行以下调用,您可能能够更快地启动:

CGContextTranslateCTM(context, 0.5, 0.5); 


这将为您传递给所有后续图形调用的每个点添加(0.5、0.5)(直到您调用CGContextRestoreGState)。如果您没有对上下文进行其他更改,则应该这样,以便从0,0-> 10,0绘制一条线时,即使启用了抗锯齿,它也将完美地对齐像素。 (请参阅上面的初始答案,以了解为什么是这种情况。)

使用这个0.5、0.5技巧可以使您入门更快,但是如果您要使用像素精确的CG绘图,则确实需要了解基于浮点的图形上下文如何工作以及它们与位图的关系。可能会(也可能不会)支持他们。

关闭AA,然后轻推数值直到它们“正确”,这只是在以后麻烦。例如,假设某天UIKit向您传递了一个上下文,该上下文在转换中具有不同的翻转?在这种情况下,您的一个值可能会四舍五入,而该值曾经在这里四舍五入(因为现在应用翻转时,它会乘以-1.0)。对于已转换为负象限的上下文,可能会发生相同的问题。此外,您不知道(并且不能严格依赖版本之间的关系)关闭AA时CoreGraphics将使用什么取整规则,因此,无论出于何种原因,最终由于不同的CTM而被移交给上下文会得到不一致的结果。

作为记录,您可能希望关闭抗锯齿功能的时间是绘制非直线路径并且不希望CoreGraphics对边缘进行Alpha混合时。您可能确实希望获得这种效果,但是关闭AA会使学习,理解和确定发生的事情变得更加困难,因此我强烈建议您将其打开,直到您完全理解这些内容,并获得所有直线图形的完美像素为止对齐。然后翻转开关以消除非直线路径上的AA。

关闭抗锯齿功能并不能神奇地使CGContext被作为位图进行寻址。它采用一个浮点平面,并添加了一个圆角步骤,您无法看到(代码级),无法控制,并且难以理解和预测。最后,您仍然有一个浮点平面。

只是一个简单的例子,以帮助阐明:

- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);

BOOL doAtDefaultScale = YES;
if (doAtDefaultScale)
{
// Do it by using the right values for the default context
CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(ctx, 0.5); // We're working in scaled pixel. 0.5pt => 1.0px
CGContextStrokeRect(ctx, CGRectMake(25.25, 25.25, 50, 50));
}
else
{
// Do it by transforming the context
CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]);
CGContextScaleCTM(ctx, 0.5, 0.5); // Back out the default scale
CGContextTranslateCTM(ctx, 0.5, 0.5); // Offset from edges of pixels to centers of pixels
CGContextSetLineWidth(ctx, 1.0); // we're working in device pixels now, having backed out the scale.
CGContextStrokeRect(ctx, CGRectMake(50, 50, 100, 100));
}
CGContextRestoreGState(ctx);
}


制作一个新的单视图应用程序,使用此drawRect:方法添加一个自定义视图子类,然后在.xib文件中设置默认视图以使用您的自定义类。该if语句的两面在视网膜显示器上产生相同的结果:一个100x100的设备像素(非alpha混合正方形)。第一面通过使用默认比例的“正确”值来实现。它的另一面通过退出2x缩放比例,然后将平面从与设备像素的边缘对齐转换为与设备像素的中心对齐来完成此操作。请注意笔触宽度是如何不同的(比例因子也适用于它们)。希望这会有所帮助。

OP回答:


  但请注意,有一些alpha混合功能。这是
  3200x缩放的屏幕截图:


不完全是。相信我。这是有原因的,并且不是在上下文中打开抗锯齿功能。 (此外,我认为您的意思是3200%的缩放比例,而不是3200x的缩放比例-在3200x的缩放比例下,单个像素不适合30英寸的显示器。)

在我给出的示例中,我们正在绘制一个矩形,因此我们不必考虑行尾,因为它是一条闭合路径-线是连续的。现在,您正在绘制单个线段,因此您必须考虑行尾以避免Alpha混合。这就是“像素边缘”与“像素中心”的关系。默认的线帽样式为kCGLineCapButt。 kCGLineCapButt表示行的结尾恰好在您开始绘制的位置开始。如果您希望它的行为更像笔-也就是说,如果您将一支尖的笔放下,打算在右边绘制10个单位的线,则会有少量墨水渗出到左边您将笔指向的确切点-您可以考虑使用kCGLineCapSquare(或kCGLineCapRound表示圆角,但是对于单像素级绘制,这只会使您对alpha混合感到恼火,因为它将把alpha计算为1.0 -0.5 / pi)。我在苹果的 Quartz 2D Programming Guide的插图上覆盖了假设的像素网格,以说明行尾如何与像素相关:

 

但是,我离题了。这是一个例子。考虑以下代码:

- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextSetStrokeColorWithColor(ctx, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(ctx, 0.5); //On device pixel at default scale
CGContextMoveToPoint(ctx, 2.0, 2.25); // Edge of pixel in X, Center of pixel in Y
CGContextAddLineToPoint(ctx, 7.0, 2.25); // Draw a line of 5 scaled, 10 device pixels
CGContextStrokePath(ctx); // Stroke it
CGContextRestoreGState(ctx);
}


请注意,在这里我没有移至2.25、2.25,而是移至2.0、2.25。这意味着我在X维度上处于像素的边缘,在Y维度上处于像素的中心。因此,我不会在行尾得到alpha混合。实际上,在橡子中放大到3200%时,我看到以下内容:



现在,是的,在某些时候,如果您要转换值或进行较长的计算,可能会超出护理的范围(对于大多数人而言),您可能会遇到累积的浮点错误。在非常复杂的情况下,我已经看到错误严重到0.000001,甚至在谈论诸如1.999999与2.000001之间的差异之类的情况时,即使像这样的错误也可能会咬住您,但那不是您在这里看到的。

如果您的目标只是绘制仅由轴对齐/直线元素组成的像素精确位图,则没有理由关闭上下文抗锯齿功能。在这种情况下,在我们讨论的缩放级别和位图密度上,您应该轻松摆脱几乎所有由1e-6或更小幅度浮点误差引起的问题。 (实际上,在这种情况下,任何小于一个像素的1/256的像素都不会在8位上下文中对alpha混合产生任何影响,因为当量化为8位时,这种误差实际上将为零。)

让我借此机会推荐一本书: Programming with Quartz: 2D and PDF Graphics in Mac OS X这是一本不错的书,并且详细介绍了所有此类内容。

关于iphone - iPhone编程中核心图形中坐标的定位规律是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8142468/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com