【问题标题】:Rotate image using CGContextDrawImage使用 CGContextDrawImage 旋转图像
【发布时间】:2013-05-27 04:00:20
【问题描述】:

如何在中心旋转CGContextDrawImage() 绘制的图像?

drawRect:

CGContextSaveGState(c);

rect = CGRectOffset(rect, -rect.size.width / 2, -rect.size.height / 2);
CGContextRotateCTM(c, angle);
CGContextDrawImage(c, rect, doodad.CGImage);
CGContextRotateCTM(c, -angle);
CGContextTranslateCTM(c, pt.x, pt.y);

prevRect = rect;

CGContextRestoreGState(c);

我在原点绘制图像,并在其中心旋转它,然后平移到应该绘制的位置。不工作。

【问题讨论】:

  • 可以通过设置锚点使图像绘制视图居中旋转
  • @Tony:这假设图像本身就在视图中(例如图像视图),并且提问者不打算将旋转后的图像保存在任何地方。 (如果后一个假设成立,则将其放在图像视图中并旋转可能是正确的解决方案。)

标签: ios core-graphics cgcontext cgaffinetransform cgcontextdrawimage


【解决方案1】:

关于坐标变换要记住的一点——嗯,要记住的一点是——你不是在操作对象,比如图形编辑器中的变换命令;你正在操纵空间本身

想象一下,该设备通过鹅颈臂悬挂在桌子上,并固定在适当的位置。坐标变换使桌子四处移动(平移),向一侧或另一侧转动(旋转),甚至挤压和拉伸桌子(缩放)。

关于坐标变换要记住的另一件事是变换总是相对于原点。想象一下,你的办公桌从它的一个角开始——对应于你视图的一个角——就在你的视图在屏幕上结束的角的正下方。平移移动桌子的一角,桌子的其余部分也随之移动,在屏幕下方以一种或另一种方式滑动。旋转使桌子绕过那个角落。

还有一件事:转型影响的是未来,而不是过去。画一些东西然后试图改变它是行不通的,因为你没有改变你画的东西,你改变了你要画的空间。因此,我们需要先移动桌子,然后才能将图像放置在正确的位置。

(我提到最后一部分是因为您有几个转换命令会立即被您的CGContextRestoreGState 调用恢复。它们之间没有任何影响。)

所以,让我们从图片的未旋转矩形开始。

CGRect imageRect = { pointWhereYouWantTheImageCentered, doodad.size };

现在,CGContextDrawImage 需要一个矩形,但是,如您所知,它将从该矩形的左下角而不是中心绘制图像。 (因此整个练习。)

所以,这就是你要做的。最后,您要绘制的图像不是在上面显示的那个点,而是在零点——也就是在桌子的最角落。 (不是以角落为中心,而是图像的角落在桌子的角落。)

这将如何运作?这需要一些设置。你将不得不移动你的办公桌。

首先,您需要将办公桌向上向右移动,直到办公桌的原点角位于所需的中心点:

CGContextTranslateCTM(context, imageRect.origin.x, imageRect.origin.y);

然后您进行旋转(将桌子围绕其原点转角):

CGContextRotateCTM(context, angleInRadians);

然后您将桌子向后向下向左移动图像宽度和高度的一半:

CGContextTranslateCTM(context, imageRect.size.width * -0.5, imageRect.size.height * -0.5);

然后,最后,将图像放在桌角。

CGContextDrawImage(context, (CGRect){ CGPointZero, imageRect.size }, [doodad CGImage]);

随着您的办公桌如此移动,该矩形的中心位于屏幕上您希望图像居中的点正下方,因此图像如此居中。

【讨论】:

  • 对我迟到的回复深表歉意,并感谢您的全面回答。您的代码产生了预期的结果,但我不明白的一件事:为什么桌面原点不在图像中心,而是在它的原点?这段代码(我认为可行)似乎有点不对劲:CGContextTranslateCTM(c, rect.origin.x + rect.size.width * 0.5, rect.origin.y + rect.size.height * 0.5); CGContextRotateCTM(c, angle); CGContextDrawImage(c, (CGRect){CGPointZero, rect.size}, doodad.CGImage);
  • 继续 翻译将桌面原点带到图像中心,因此围绕图像中心执行旋转(或者我的想法是这样)。但显然这是错误的。
  • @Plenilune: CGContextDrawImage 用图像填充你给它的矩形(根据需要拉伸它)。那个矩形的原点——在我的版本中,零点——是图像的一个角所在的位置。我通过了零点因为我翻译到了那个角落;另一种方法是将我传递给translate 的相同数字作为图像矩形的原点传递,在这种情况下,图像的中心(其原点悬挂在桌子上)位于角落(原点)桌子。
  • Fab 答案。您可能需要将其包装在 CGContextSaveGState/CGContextRestoreGState 中。
  • 这是一个不错的解决方案。但是有没有人注意到旋转的东西在生成的图像上看起来比原来更大?我在彼此之上有多个图层(imageview),当我从所有图层生成图像时,所有旋转的部分都会稍微变大(我猜那是因为当我旋转画布绘制时框架会发生变化)。有没有办法解决这个问题,并保持原来的大小?
【解决方案2】:

此函数可以在现有图像上以弧度为单位绘制图像。

斯威夫特 3:

extension UIImage {
    func putImage(image: UIImage, on rect: CGRect, angle: CGFloat = 0.0) -> UIImage{

        let drawRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        UIGraphicsBeginImageContextWithOptions(drawRect.size, false, 1.0)

        // Start drawing self
        self.draw(in: drawRect)

        // Drawing new image on top
        let context = UIGraphicsGetCurrentContext()!

        // Get the center of new image
        let center = CGPoint(x: rect.midX, y: rect.midY)

        // Set center of image as context action point, so rotation works right
        context.translateBy(x: center.x, y: center.y)
        context.saveGState()

        // Rotate the context
        context.rotate(by: angle)

        // Context origin is image's center. So should draw image on point on origin
        image.draw(in: CGRect(origin: CGPoint(x: -rect.size.width/2, y: -rect.size.height/2), size: rect.size), blendMode: .normal, alpha:
1.0)

        // Go back to context original state.
        context.restoreGState()

        // Get new image
        let newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return newImage
    }

}

如果您需要创建并清空具有大小和颜色的图像,请使用:

extension UIImage {
    convenience init?(size: CGSize, color: UIColor) {
        let rect = CGRect(origin: .zero, size: size)
        UIGraphicsBeginImageContextWithOptions(rect.size, true, 1.0)
        color.setFill()
        UIRectFill(rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        guard let cgImage = image?.cgImage else { return nil }
        self.init(cgImage: cgImage)
    }
}

【讨论】:

    【解决方案3】:

    您可以使用以下方法旋转图像。

    -(UIImage*)rotateImage:(UIImage*)img forAngle:(CGFloat)radian   {
    
        UIView *rotatedViewBox = [[UIView alloc]initWithFrame:CGRectMake(0, 0, img.size.width, img.size.height)];
        CGAffineTransform t = CGAffineTransformMakeRotation(radian);
        rotatedViewBox.transform = t;
        CGSize rotatedSize = rotatedViewBox.frame.size;
    
        UIGraphicsBeginImageContext(rotatedSize);
    
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(context, rotatedSize.width, rotatedSize.height);
        CGContextRotateCTM(context, radian);
        CGContextScaleCTM(context, 1.0, -1.0);
    
        CGContextDrawImage(context, CGRectMake(-img.size.width, -img.size.height, img.size.width, img.size.height), img.CGImage);
        UIImage *returnImg = UIGraphicsGetImageFromCurrentImageContext();
    
        UIGraphicsEndImageContext();
        return returnImg;
    }
    

    如果你使用角度,那么这里是角度转换为弧度的宏

    #define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
    

    【讨论】:

      【解决方案4】:

      如果您最终要绘制到屏幕上(而不是生成要保存到文件的图像),您可能会考虑根本不使用 CGContext,而是将图像放入 CALayer 并围绕其旋转图层锚点:

      [layer setValue:@(rotationInRadians) forKeyPath:@"transform.rotation.z"];
      

      如果图像是自己生成的(而不是来自你的包的静态资源或用户提供的东西),你可能会考虑将其从层中制作出来并设置其父/共同祖先层的sublayerTransform

      [layerContainingTheConstituentLayers setValue:@(rotationInRadians) forKeyPath:@"sublayerTransform.rotation.z"];
      

      (可能有办法用视图而不是层来做到这一点;我是 Mac 人,所以我对 UIView 的了解不是很深。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-15
        • 2016-04-08
        • 2011-04-26
        • 2010-12-04
        相关资源
        最近更新 更多