有人知道为什么CGContextDrawImage
会倒着画我的图片吗?我正在从我的应用程序中加载一张图片:
UIImage *image = [UIImage imageNamed:@"testImage.png"];
然后只需请求核心图形将其绘制到我的上下文即可:
CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);
它在正确的位置和尺寸上呈现,但图像是倒置的。我肯定是漏掉了什么非常明显的东西吗?
有人知道为什么CGContextDrawImage
会倒着画我的图片吗?我正在从我的应用程序中加载一张图片:
UIImage *image = [UIImage imageNamed:@"testImage.png"];
然后只需请求核心图形将其绘制到我的上下文即可:
CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);
它在正确的位置和尺寸上呈现,但图像是倒置的。我肯定是漏掉了什么非常明显的东西吗?
替换为
CGContextDrawImage(context, CGRectMake(0, 0, 145, 15), image.CGImage);
[image drawInRect:CGRectMake(0, 0, 145, 15)];
CGcontext
方法的开始/结束中间。UIImage
保持方向知识有关,而CGContextDrawImage
方法获取底层原始图像数据而不理解方向。CGContextTranslateCTM(context, 0, image.size.height);
这意味着我们在x轴上将图像平移0个单位,在y轴上将其平移图像高度的距离。然而,仅仅这样做会导致图像仍然颠倒,只是被绘制在我们希望它被绘制的位置下方"image.size.height"。
Quartz2D编程指南建议使用ScaleCTM并传递负值来翻转图像。您可以使用以下代码实现-
CGContextScaleCTM(context, 1.0, -1.0);
在调用CGContextDrawImage
之前将两者结合起来,然后您应该可以正确地绘制图像。
UIImage *image = [UIImage imageNamed:@"testImage.png"];
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, imageRect, image.CGImage);
如果您的imageRect坐标与图像不匹配,就要小心了,因为可能会得到意想不到的结果。
要将坐标转换回去:
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0, -imageRect.size.height);
CGContextSaveGState()
和CGContextRestoreGState()
来保存和恢复变换矩阵? - Ja͢ck不必在UIImage上失去任何功能,您可以使用drawAtPoint:
或drawInRect:
方法,同时仍然指定您的自定义上下文:
UIGraphicsPushContext(context);
[image drawAtPoint:CGPointZero]; // UIImage will handle all especial cases!
UIGraphicsPopContext();
你还需要避免使用CGContextTranslateCTM
或者CGContextScaleCTM
函数来修改上下文,而第二个答案使用了这些函数。
UIGraphicsBeginImageContextWithOptions
而不仅仅是初始化一个新的CGContext
,它似乎就可以工作了。 - Luke Rogers我使用这个Swift 5,纯的Core Graphics扩展,可以正确处理图像矩形中的非零原点:
extension CGContext {
/// Draw `image` flipped vertically, positioned and scaled inside `rect`.
public func drawFlipped(_ image: CGImage, in rect: CGRect) {
self.saveGState()
self.translateBy(x: 0, y: rect.origin.y + rect.height)
self.scaleBy(x: 1.0, y: -1.0)
self.draw(image, in: CGRect(origin: CGPoint(x: rect.origin.x, y: 0), size: rect.size))
self.restoreGState()
}
}
CGContext
的常规draw(: in:)
方法一样使用它:ctx.drawFlipped(myImage, in: myRect)
翻转默认坐标系
在UIKit绘图中,翻转将修改支撑CALayer以将具有LLO坐标系统的绘图环境与UIKit的默认坐标系统对齐。如果您只使用UIKit方法和函数进行绘制,则不需要翻转CTM。但是,如果您将Core Graphics或Image I / O函数调用与UIKit调用混合使用,则可能需要翻转CTM。
具体而言,如果您通过直接调用Core Graphics函数来绘制图像或PDF文档,则对象在视图上下文中呈倒置状态。您必须翻转CTM才能正确显示图像和页面。
要翻转绘制到Core Graphics上下文的对象,以便在UIKit视图中正确显示,您必须分两步修改CTM。您将原点平移至绘图区域的左上角,然后应用比例平移,将y坐标修改为-1。执行此操作的代码类似于以下内容:
CGContextSaveGState(graphicsContext);
CGContextTranslateCTM(graphicsContext, 0.0, imageHeight);
CGContextScaleCTM(graphicsContext, 1.0, -1.0);
CGContextDrawImage(graphicsContext, image, CGRectMake(0, 0, imageWidth, imageHeight));
CGContextRestoreGState(graphicsContext);
func drawImage(image: UIImage, inRect rect: CGRect, context: CGContext!) {
//flip coords
let ty: CGFloat = (rect.origin.y + rect.size.height)
CGContextTranslateCTM(context, 0, ty)
CGContextScaleCTM(context, 1.0, -1.0)
//draw image
let rect__y_zero = CGRect(origin: CGPoint(x: rect.origin.x, y:0), size: rect.size)
CGContextDrawImage(context, rect__y_zero, image.CGImage)
//flip back
CGContextScaleCTM(context, 1.0, -1.0)
CGContextTranslateCTM(context, 0, -ty)
}
图像将被缩放以填充矩形。
我不确定对于UIImage
来说是否如此,但这种行为通常发生在坐标被翻转时。大多数OS X坐标系统的原点位于左下角,例如Postscript和PDF。但是CGImage
坐标系统的原点位于左上角。
可能的解决方案可能涉及isFlipped
属性或scaleYBy:-1
仿射变换。
这是因为QuartzCore有一个“底部-左侧”的坐标系统,而UIKit则有一个“顶部-左侧”的坐标系统。
如果需要,在此情况下可以扩展CGContext:
extension CGContext {
func changeToTopLeftCoordinateSystem() {
translateBy(x: 0, y: boundingBoxOfClipPath.size.height)
scaleBy(x: 1, y: -1)
}
}
// somewhere in render
ctx.saveGState()
ctx.changeToTopLeftCoordinateSystem()
ctx.draw(cgImage!, in: frame)
可以在以下两个自定义视图实例中看到这种现象,它们在其一些技术使用不同的默认坐标系设置其图形上下文,与 Quartz 使用的坐标系相比,这种坐标系是一个修改后的坐标系,在执行某些 Quartz 绘图操作时必须进行补偿。最常见的修改坐标系将原点放置在上左角,将 y 轴改为指向页面底部。
drawRect
方法中绘制图像。
在左侧,图像是倒置的,在右侧,坐标系已经被平移和缩放,使得原点位于左上角。
倒置的图像
override func drawRect(rect: CGRect) {
// image
let image = UIImage(named: "rocket")!
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
// context
let context = UIGraphicsGetCurrentContext()
// draw image in context
CGContextDrawImage(context, imageRect, image.CGImage)
}
修改后的坐标系
override func drawRect(rect: CGRect) {
// image
let image = UIImage(named: "rocket")!
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
// context
let context = UIGraphicsGetCurrentContext()
// save the context so that it can be undone later
CGContextSaveGState(context)
// put the origin of the coordinate system at the top left
CGContextTranslateCTM(context, 0, image.size.height)
CGContextScaleCTM(context, 1.0, -1.0)
// draw the image in the context
CGContextDrawImage(context, imageRect, image.CGImage)
// undo changes to the context
CGContextRestoreGState(context)
}
UIImage
包含一个 CGImage
作为其主要内容成员,以及缩放和方向因素。由于 CGImage
及其各种函数都源自 OSX,它期望的坐标系与 iPhone 相比是倒置的。当您创建一个 UIImage
时,默认为倒置方向来进行补偿(您可以更改此设置!)。使用 .CGImage
属性来访问非常强大的 CGImage
函数,但最好使用 UIImage
方法来绘制到 iPhone 屏幕等。