Quartz2D 图像处理

[转] 原文地址: http://www.cnblogs.com/smileEvday/archive/2013/05/25/IOSImageEdit.html

 

一、图像旋转

  图像旋转是图像处理过程中一中常见操作,按照旋转的角度不同,可以分为以下两种:

  1、特殊角度旋转

  特殊角度旋转是指对图像做90°,180°,270°等这一类旋转,这一类旋转操作通常是最频繁的,如看照片时偶尔会碰到一些方向有问题,我们只需要进行简单的左转90°,右转90°就可以装好。这种方法由于没有牵扯到具体的绘制操作,因此速度很快,在IOS和Mac系统中都可以正确显示,但是如果将图片倒到windows系统中,方向可能依然是错的。

  2、任意角度旋转

  任意角度旋转顾名思义即对图像做任意角度的旋转,可能是30°也可能是35°等等。很显然这一种旋转是没法通过imageOrientaion来完成的,因此我们得想点儿别的办法。我们知道UIView有一个transform属性,通过设置transform可以实现偏移,缩放,旋转的效果。在quartz2D中我们也同样可以通过对context设置不同的transform来完成相应的功能,下面我们要介绍的任意角度旋转的方法就是基于对context的一系列操作来完成的。

  这块儿你可能有个疑问,问什么让UIView旋转只需要设置一个旋转的transform就可以了,而context则需要通过“一系列”的transform操作才能完成相应的功能?

  原因是UIView中我们通过transform进行的所有操作都是基于view的中心点的,而context中我们进行的操作是基于context的坐标原点。下面我们首先看一下UIView进行旋转时的图示:

  由于旋转时绕着中心点转动,所以我们只需要一步就可以从原位置(黑色表示)转到目标位置(蓝色表示),其中黑色虚线和蓝色虚线之间的夹角就是转过的角度。我们想一下如果转动时绕着左上角的原点转动,完成同样角度转动后会是怎么一种情况呢?请看下图

  

  如上图所示,由于旋转是绕着原点进行的,虽然我们转过了相同的角度,但是得到的结果却相差甚远。因此context中如果想把一幅图片旋转任意角度的话,至少得进行两步:旋转和平移。

  第一步旋转很好做,问题是第二部如何从旋转过后图片的中心移动到原图中心,这个计算还不是那么直观。于是我们想着去模拟UIView的旋转,我们分如下三步走:

  ->  -> 

  我们设图片的宽度为width,高度为height,旋转的三个步骤依次如上图所示:

  a、将context进行平移,将原点移动到原图的中心位置,x,y方向的平移距离分别为width / 2,height / 2。

  b、对context进行旋转操作。

  c、将旋转后的图像的中心点重新移回原图的中心点,即x,y方向的平移距离分别是-width / 2,-height / 2。

  进过这三步我们就可以很方便的实现图片的任意角度旋转了。你可能会发现步骤a中向下移动了半个图片宽高,步骤c中又向相反方向移动了半个图片宽高。这两个操作不会抵消吗?答案是NO,步骤a中我们的移动是基于原坐标系统进行移动的,到了步骤c时我们的移动是基于这个时候的坐标系移动的,两个坐标系是不一样的,所以才能通过一来一回完成对图片的旋转。

  图片旋转的代码如下:

复制代码
//
//  UIImage+Rotate_Flip.m
//  SvImageEdit
//
//  Created by  maple on 5/14/13.
//  Copyright (c) 2013 smileEvday. All rights reserved.
//

#import "UIImage+Rotate_Flip.h"

/*
 * @brief rotate image with radian
 */
- (UIImage*)rotateImageWithRadian:(CGFloat)radian cropMode:(SvCropMode)cropMode
{
    CGSize imgSize = CGSizeMake(self.size.width * self.scale, self.size.height * self.scale);
    CGSize outputSize = imgSize;
    if (cropMode == enSvCropExpand) {
//转载者注释: 图像经过旋转后,其尺寸通常与原来的那个矩形尺寸相比,超过了限度(自己画一个平躺着的矩形,绕远点转90度,就很容看到了<图形看不到了>.如果要保证旋转后还能看到完整的图形,需要调用函数
CGRectApplyAffineTransform,I该函数的作用是,返回一个最小的矩形尺寸,且该尺寸可以包含经过旋转的图形
)
        CGRect rect = CGRectMake(0, 0, imgSize.width, imgSize.height); //原尺寸
        rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeRotation(radian)); //获得最小的包含尺寸
        outputSize = CGSizeMake(CGRectGetWidth(rect), CGRectGetHeight(rect)); //新尺寸
    }
    
    UIGraphicsBeginImageContext(outputSize); //开始绘图模式, 和 end 配对使用,注意这里使用的outputSize,而不是原image的size
    CGContextRef context = UIGraphicsGetCurrentContext(); //获得当前的图形环境
    
    CGContextTranslateCTM(context, image.width / 2, image.height / 2); //在当前的图形环境里执行一个平移
    CGContextRotateCTM(context, radian); //执行一个旋转
    CGContextTranslateCTM(context, -imgSize.width / 2, -imgSize.height / 2); //执行一个反平移,需要注意的是,如果把 变换的矩阵写出来,就会是TRT(-1),其中T代表平移,R代表旋转,如果向量横着写,那么变换矩阵的每一行代表一个基向量,TR之后,基向量就变成了旋转后的基向量,所以之后的T(-1)所执行的逆平移,是在旋转后的基向量上执行了,和R之前的那个T不同.
    
  //变换完了,画出来 [self drawInRect:CGRectMake(0, 0, outputSize.width, outputSize.height)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }
复制代码

  其中的CropMode定义如下:

复制代码
enum {
    enSvCropClip,               // the image size will be equal to orignal image, some part of image may be cliped
    enSvCropExpand,             // the image size will expand to contain the whole image, remain area will be transparent
};
typedef NSInteger SvCropMode;
复制代码

  clip模式下,旋转后的图片和原图一样大,部分图片区域会被裁剪掉;expand模式下,旋转后的图片可能会比原图大,所有的图片信息都会保留,剩下的区域会是全透明的。

posted on 2014-09-11 09:57  大漠石头  阅读(126)  评论(0)    收藏  举报