CGContextDrawImage 传递 UIImage.CGImage 时颠倒绘制图像
有谁知道为什么 CGContextDrawImage 会颠倒绘制我的图像? 我正在从我的应用程序加载图像:
UIImage *image = [UIImage imageNamed:@"testImage.png"];
然后简单地要求核心图形将其绘制到我的上下文中:
CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);
它呈现在正确的位置和尺寸,但图像是颠倒的。 我一定在这里遗漏了一些非常明显的东西?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
两全其美,使用 UIImage 的
drawAtPoint:
或drawInRect:
,同时仍指定自定义上下文:此外,您还可以避免使用
CGContextTranslateCTM
或 < code>CGContextScaleCTM 第二个答案就是这样做的。Best of both worlds, use UIImage's
drawAtPoint:
ordrawInRect:
while still specifying your custom context:Also you avoid modifying your context with
CGContextTranslateCTM
orCGContextScaleCTM
which the second answer does.相关 Quartz2D 文档: https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW4
Relevant Quartz2D docs: https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW4
即使在应用了我提到的所有内容之后,我仍然对图像产生戏剧性的感觉。 最后,我刚刚使用 Gimp 创建了所有图像的“垂直翻转”版本。 现在我不需要使用任何变换。 希望这不会造成进一步的问题。
Quartz2d 使用不同的坐标系,原点位于左下角。 因此,当 Quartz 绘制 100 * 100 图像的像素 x[5]、y[10] 时,该像素将绘制在左下角而不是左上角。 从而导致图像“翻转”。
x 坐标系匹配,因此您需要翻转 y 坐标。
这意味着我们已经将图像在 x 轴上平移了 0 个单位,并在 y 轴上平移了图像高度。 然而,仅此一点就意味着我们的图像仍然是颠倒的,只是被绘制在我们希望绘制的位置下方的“image.size.height”。
Quartz2D 编程指南建议使用 ScaleCTM 并传递负值来翻转图像。 您可以使用以下代码来执行此操作 -
在 CGContextDrawImage 调用之前将两者结合起来,您应该正确绘制图像。
如果您的 imageRect 坐标与图像的坐标不匹配,请小心,因为您可能会得到意想不到的结果。
要转换回坐标:
Even after applying everything I have mentioned, I've still had dramas with the images. In the end, i've just used Gimp to create a 'flipped vertical' version of all my images. Now I don't need to use any Transforms. Hopefully this won't cause further problems down the track.
Quartz2d uses a different co-ordinate system, where the origin is in the lower left corner. So when Quartz draws pixel x[5], y[10] of a 100 * 100 image, that pixel is being drawn in the lower left corner instead of the upper left. Thus causing the 'flipped' image.
The x co-ordinate system matches, so you will need to flip the y co-ordinates.
This means we have translated the image by 0 units on the x axis and by the images height on the y axis. However, this alone will mean our image is still upside down, just being drawn "image.size.height" below where we wish it to be drawn.
The Quartz2D programming guide recommends using ScaleCTM and passing negative values to flip the image. You can use the following code to do this -
Combine the two just before your
CGContextDrawImage
call and you should have the image drawn correctly.Just be careful if your imageRect co-ordinates do not match those of your image, as you can get unintended results.
To convert back the coordinates:
而不是
使用
在开始/结束 CGcontext 方法中间
。 这将以正确的方向将图像绘制到当前图像上下文中 - 我很确定这与
UIImage
在CGContextDrawImage
时掌握方向知识有关> 方法获取底层原始图像数据,但不了解方向。Instead of
Use
In the middle of your begin/end
CGcontext
methods.This will draw the image with the correct orientation into your current image context - I'm pretty sure this has something to do with the
UIImage
holding onto knowledge of the orientation while theCGContextDrawImage
method gets the underlying raw image data with no understanding of orientation.我使用这个 Swift 5,纯 Core Graphics 扩展,它可以正确处理图像矩形中的非零原点:
您可以像
CGContext
' 一样使用它常规draw(: in:)
方法:I use this Swift 5, pure Core Graphics extension that correctly handles non-zero origins in image rects:
You can use it exactly like
CGContext
's regulardraw(: in:)
method:UIImage
包含一个CGImage
作为其主要内容成员以及缩放和方向因子。 由于 CGImage 及其各种函数源自 OSX,因此与 iPhone 相比,它需要一个颠倒的坐标系。 当您创建UIImage
时,它默认采用颠倒方向进行补偿(您可以更改此设置!)。 使用 .CGImage
属性可以访问非常强大的CGImage
函数,但在 iPhone 屏幕等上绘图最好使用UIImage
方法。UIImage
contains aCGImage
as its main content member as well as scaling and orientation factors. SinceCGImage
and its various functions are derived from OSX, it expects a coordinate system that is upside down compared to the iPhone. When you create aUIImage
, it defaults to an upside-down orientation to compensate (you can change this!). Use the .CGImage
property to access the very powerfulCGImage
functions, but drawing onto the iPhone screen etc. is best done with theUIImage
methods.用 Swift 代码补充答案
Quartz 2D 图形使用原点位于左下角的坐标系,而 iOS 中的 UIKit 使用原点位于左上角的坐标系。 通常一切都很好,但是在进行一些图形操作时,您必须自己修改坐标系。 文档指出:
在以下两个在其
drawRect
方法中绘制图像的自定义视图实例中可以看到这种现象。在左侧,图像是上下颠倒的,而在右侧,坐标系已被平移和缩放,以便原点位于左上角。
颠倒图像
修改坐标系
Supplemental answer with Swift code
Quartz 2D graphics use a coordinate system with the origin in the bottom left while UIKit in iOS uses a coordinate system with the origin at the top left. Everything usually works fine but when doing some graphics operations, you have to modify the coordinate system yourself. The documentation states:
This phenomenon can be seen in the following two instances of custom views that draw an image in their
drawRect
methods.On the left side, the image is upside-down and on the right side the coordinate system has been translated and scaled so that the origin is in the top left.
Upside-down image
Modified coordinate system
发生这种情况是因为 QuartzCore 的坐标系是“左下”,而 UIKit 的坐标系是“左上”。
在这种情况下,您可以扩展CGContext:
It happens because QuartzCore has the coordinate system "bottom-left", while UIKit – "top-left".
In the case you can extend CGContext:
我不确定
UIImage
,但这种行为通常在翻转坐标时发生。 大多数 OS X 坐标系的原点位于左下角,如 Postscript 和 PDF 中。 但是CGImage坐标系的原点在左上角。可能的解决方案可能涉及
isFlipped
属性或scaleYBy:-1
仿射变换。I'm not sure for
UIImage
, but this kind of behaviour usually occurs when coordinates are flipped. Most of OS X coordinate systems have their origin at the lower left corner, as in Postscript and PDF. ButCGImage
coordinate system has its origin at the upper left corner.Possible solutions may involve an
isFlipped
property or ascaleYBy:-1
affine transform.如果有人对在上下文中的自定义矩形中绘制图像的简单解决方案感兴趣:
图像将缩放以填充矩形。
If anyone is interested in a no-brainer solution for drawing an image in a custom rect in a context:
The image will be scaled to fill the rect.
Swift 3.0 和 4.0
无需修改
Swift 3.0 & 4.0
No Alteration needed
drawInRect
无疑是正确的选择。 这是另一件在执行此操作时会很有用的小事情。 通常图片和它要进入的矩形不相符。 在这种情况下,drawInRect
将拉伸图片。 这是一种快速而酷的方法,可以通过反转转换(这将适合整个事物)来确保图片的长宽比不会改变:drawInRect
is certainly the way to go. Here's another little thing that will come in way useful when doing this. Usually the picture and the rectangle into which it is going to go don't conform. In that casedrawInRect
will stretch the picture. Here's a quick and cool way to make sure that the picture's aspect ration isn't changed, by reversing the transformation (which will be to fit the whole thing in):我们可以使用相同的函数来解决这个问题:
We can solve this problem using the same function:
Swift 3 CoreGraphics 解决方案
如果您出于任何原因想要使用 CG,而不是 UIImage,这个基于之前答案的 Swift 3 构造确实为我解决了这个问题:
Swift 3 CoreGraphics Solution
If you want to use CG for whatever your reason might be, instead of UIImage, this Swift 3 construction based on previous answers did solve the issue for me:
在我的项目过程中,我从 Kendall 的回答跳到了Cliff 的回答解决了从手机本身加载的图像的问题。
最后我最终使用
CGImageCreateWithPNGDataProvider
相反:这不会遇到从
获取
并且它可以毫无障碍地用作CGImage
时遇到的方向问题UIImageCALayer
的内容。During the course of my project I jumped from Kendall's answer to Cliff's answer to solve this problem for images that are loaded from the phone itself.
In the end I ended up using
CGImageCreateWithPNGDataProvider
instead:This doesn't suffer from the orientation issues that you would get from getting the
CGImage
from aUIImage
and it can be used as the contents of aCALayer
without a hitch.Swift 5答案基于@ZpaceZombor的优秀答案
如果你有一个UIImage,只需使用
如果你有一个CGImage使用我下面的类别
注意:与其他一些答案不同,这个考虑到了你想要的矩形绘制可能有 y!= 0。那些没有考虑到这一点的答案是不正确的,并且在一般情况下不起作用。
像这样使用:
Swift 5 answer based on @ZpaceZombor's excellent answer
If you have a UIImage, just use
If you have a CGImage use my category below
Note: Unlike some other answers, this one takes into account that the rect you want to draw in might have y != 0. Those answers that don't take that into account are incorrect and won't work in the general case.
Use like this:
您还可以通过执行以下操作来解决此问题:
You can also solve this problem by doing this: