先说说仿射变换CGAffineTransform这是一个数学概念,有兴趣的可以深入研究,在CoreGraphic中他长这样,跟数学定义一模一样,CGAffineTransform提供的接口都是对a,b,c,d,tx,ty进行操作的,具体往后看
struct CGAffineTransform {
CGFloat a, b, c, d;
CGFloat tx, ty;
};
可以看出他是一个结构体,他代表着一个3*3的矩阵。
下图中x',y'代表变换之后的图形中每个点的坐标,x,y代表图形每个点的原始坐标,那么根据矩阵的运算就是
x' =xa+yb+tx
,y' = yb+yd+ty
。Calc-CGAffineTransform.png.jpeg
举个栗子🌰
平移:
imageView.transform = CGAffineTransformTranslate(imageView.transform, 5, 5);
NSLog(@"%@",NSStringFromCGAffineTransform(imageView.transform));
2018-11-06 21:52:00.571268+0800 放射变换[896:14670] [1, 0, 0, 1, 5, 5]
正如打印的a=1、b=0、c=0、d=1、tx=5、ty=5;
x' = x+5;
y' = y+5;
1 = 1;
旋转:
imageView.transform = CGAffineTransformRotate(imageView.transform, M_PI/4);
NSLog(@"%@",NSStringFromCGAffineTransform(imageView.transform));
2018-11-06 22:05:03.236199+0800 放射变换[1104:22958] [0.70710678118654757, 0.70710678118654746, -0.70710678118654746, 0.70710678118654757, 0, 0]
旋转的公式有点复杂,涉及到平面向量的旋转,如果忘了可以从这里得到答案平面向量旋转2D旋转的公式如下x' = xcosa-ysina
y'=xsina+ycosa
注意
仿射变换中要注意的是所有的transform都是围绕锚点进行的,也就是说都是以锚点为参照变换的。默认的锚点在被变换空间的center。
下面我们改变image的锚点为(0,0)并旋转45度,看下跟原来的区别
UIView * view = [[UIView alloc] initWithFrame:CGRectMake(100, 50, 100, 100)];
view.layer.borderColor = UIColor.blackColor.CGColor;
view.layer.borderWidth = 1;
[self.view addSubview:view];
UIImageView * imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"thunderstorm.jpg"]];
[self.view addSubview:imageView];
imageView.layer.anchorPoint = CGPointZero;
imageView.frame = CGRectMake(100, 50, 100, 100);
imageView.transform = CGAffineTransformRotate(imageView.transform, M_PI/4);
UIView * view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 100, 100)];
view1.layer.borderColor = UIColor.blackColor.CGColor;
view1.layer.borderWidth = 1;
[self.view addSubview:view1];
UIImageView * imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"thunderstorm.jpg"]];
[self.view addSubview:imageView1];
imageView1.frame = CGRectMake(100, 300, 100, 100);
imageView1.transform = CGAffineTransformRotate(imageView1.transform, M_PI/4);
介绍了这么多,说正题吧😄
CTM(current transformation matrix)
Quartz 2D绘图模型有两种空间,用户空间(user space)和设备空间(device space)。用户空间表示当前需绘制的文档页(document page),设备空间表示原始分辨率的设备。Quartz 2D使用一个变换矩阵CTM(current transformation matrix)将用户空间映射到设备空间。CTM存储在图形上下文( graphics context)中,初始值为identity matrix。在绘制过程中可进行修改。
修改当前CTM的API有CGContextRotateCTM
CGContextScaleCTM
CGContextTranslateCTM
分别用于旋转,缩放,平移。Rotate是以原点为圆心旋转,Quartz创建的图形上下文旋转圆心为左下角,角度值正数为逆时针旋转,负数为顺时针旋转;而UIKit创建的图像上下文旋转圆心为左上角,角度值正数为顺时针旋转,负数为逆时针旋转。
使用方法为
CTM矩阵类型为仿射变换(CGAffineTransform),可使用CGContextGetCTM获取当前图形上下文的仿射变换,也可用CGContextConcatCTM将参数中的CGAffineTransform应用于图形上下文。
设备空间与用户空间的概念,可理解为两张纸,设备空间为一张纸,固定着不动,代表着屏幕;用户空间也是一张纸,实际绘图在用户空间这张纸上画,但最终需要贴到设备空间那张纸上,怎么贴就是CTM描述的问题,我可能将用户空间的纸平移一些距离再贴,也可能放大缩小一些再贴,也可能旋转一定的角度再贴。用户空间的纸对应与绘画过程中的每一page,不同的page可能用不同的用户空间,即每次绘制时的CTM可能都不一样。
CTM资料
这里用户空间的锚点始终为坐标原点,也就是右上角,这里我们举个例子,生成一张旋转45度的图片。
UIImage * image = [UIImage imageNamed:@"thunderstorm.jpg"];
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
UIGraphicsBeginImageContext(image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextRotateCTM(context, M_PI/4);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
UIImage * result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
image.png
上图可以看出重绘的图片以原点为中心旋转了45度,那么如果我们想让他以中心为原点旋转怎么办呢?先上代码
UIImage * image = [UIImage imageNamed:@"爱国方形的背景-34892636.jpg"];
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
UIGraphicsBeginImageContext(image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, imageWidth/2, imageHeight/2);
CGContextRotateCTM(context, M_PI/4);
CGContextDrawImage(context, CGRectMake(-imageWidth/2, -imageHeight/2, imageWidth, imageHeight), image.CGImage);
UIImage * result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
image.png
如上所示,我们将用户空间的原点移动到图片的中心位置,然后设置绘制的image的x为-imageWidth/2,y为-imageHeight/2,使得用户空间的原点在图片的中心,再进行旋转就可以了。由于Quartz2D没有提供设置锚点的api(反正我没有找到),只能做次处理。
网友评论