图形上下文:
GraphicsContext定义了基本的绘制属性,如颜色、裁减区域、线条宽度和样式信息、字体信息、混合模式等.在iOS应用程序中,如果要在屏幕上进行绘制,需要创建一个UIView对象,并实现它的drawRect:方法。视图的drawRect:方法在视图显示在屏幕上及它的内容需要更新时被调用.在调用自定义的drawRect:后,视图对象自动配置绘图环境以便能立即执行绘图操作.作为配置的一部分,视图对象将为当前的绘图环境创建一个GraphicsContext。通过调用UIGraphicsGetCurrentContext()方法可以获取当前的GraphicsContext.
图形上下文的作用 保存绘图信息、绘图状态.
决定绘制的输出目标(绘制到什么地方去?(输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
Quartz2D提供了以下几种类型的Graphics Context:
BitmapGraphics ContextPDFGraphics ContextWindowGraphics ContextLayerGraphics ContextPrinterGraphicsContext
如图所示。当用Quartz绘图时,所有设备相关的特性都包含在我们所使用的Graphics Context中。换句话说,我们可以简单地给Quartz绘图序列指定不同的Graphics Context,就可将相同的图像绘制到不同的设备上。我们不需要任何设备相关的计算;这些都由Quartz替我们完成。
Quartz2D绘图顺序
Quartz2D上下文变换
我们知道在UIKit开发中UIView有一个transform属性用于控件的形变,其实在绘图中我们也经常用到图形形变,这个时候可以借助图形上下文的形变方法来完成。在弄清形变之前我们要清楚图形上下文的坐标原点,因为无论是位移还是旋转都是相对于坐标原点进行的。其实Quartz 2D的坐标系同UIKit并不一样,它的坐标原点在屏幕左下方,但是为了统一编程方式,UIKit对其进行了转换,坐标原点统一在屏幕左上角。注意在设置图形上下文形变之前一定要注意保存上下文的初始状态,在使用完之后进行恢复。否则在处理多个图形形变的时候很容易弄不清楚到底是基于怎样的坐标系进行绘图,容易找不到原点.
一、了解一些坐标变换(平移、缩放、旋转)
1. - void CGContextTranslateCTM ( CGContextRef c, CGFloat tx, CGFloat ty ):平移坐标系统。
该方法相当于把原来位于 (0, 0) 位置的坐标原点平移到 (tx, ty) 点。在平移后的坐标系统上绘制图形时,所有坐标点的 X 坐标都相当于增加了 tx,所有点的 Y 坐标都相当于增加了 ty。
2. - void CGContextScaleCTM ( CGContextRef c, CGFloat sx, CGFloat sy ):缩放坐标系统。
该方法控制坐标系统水平方向上缩放 sx,垂直方向上缩放 sy。在缩放后的坐标系统上绘制图形时,所有点的 X 坐标都相当于乘以 sx 因子,所有点的 Y 坐标都相当于乘以 sy 因子。
3. - void CGContextRotateCTM ( CGContextRef c, CGFloat angle ):旋转坐标系统。
该方法控制坐标系统旋转 angle 弧度。在缩放后的坐标系统上绘制图形时,所有坐标点的 X、Y 坐标都相当于旋转了 angle弧度之后的坐标。
为了让开发者在进行坐标变换时无须计算多次坐标变换后的累加结果,Quartz 2D还提供了如下两个方法来保存、恢复绘图状态。
- void CGContextSaveGState ( CGContextRef c ):保存当前上下文状态及相当于copy一份最初的上下文,用于后续的操作.
- void CGContextRestoreGState ( CGContextRef c ):可以恢复之前保存的上下文状态.将之前从栈中保持的上下文拿出来使用并替换当前上下文。
需要说明的是,CGContextSaveGState() 函数保存的绘图状态,不仅包括当前坐标系统的状态,也包括当前设置的填充风格、线条风格、阴影风格等各种绘图状态。但 CGContextSaveGState() 函数不会保存当前绘制的图形。
图形状态相关内容:
Quartz通过修改当前图形状态(current graphics state)来修改绘制操作的结果。图形状态包含用于绘制程序的参数。绘制程序根据这些绘图状态来决定如何渲染结果。例如,当你调用设置填充颜色的函数时,你将改变存储在当前绘图状态中的颜色值。
Graphics Context包含一个绘图状态栈。当Quartz创建一个Graphics Context时,栈为空。当保存图形状态时,Quartz将当前图形状态的一个副本压入栈中。当还原图形状态时,Quartz将栈顶的图形状态出栈。出栈的状态成为当前图形状态。
注意:并不是当前绘制环境的所有方面都是图形状态的元素。如,图形状态不包含当前路径(current path)。下面列出了图形状态相关的参数:
Current transformation matrix (CTM):当前转换矩阵
Clipping area:裁剪区域
Line: 线
Accuracy of curve estimation (flatness):曲线平滑度
Anti-aliasing setting:反锯齿设置
Color: 颜色
Alpha value (transparency):透明度
Rendering intent:渲染目标
Color space: 颜色空间
Text: 文本
Blend mode:混合模式
二、通用的坐标变换(通过变换矩阵进行变换)
除了以上3个坐标转换方法之外,Quartz 2D提供更通用的坐标转换方法。
void CGContextConcatCTM ( CGContextRef c, CGAffineTransform transform ):使用 transform 变换矩阵对 CGContextRef 的坐标系统执行变换,通过使用坐标矩阵可以对坐标系统执行任意变换。
CGAffineTransform CGContextGetCTM ( CGContextRef c ):获取CGContextRef的坐标系统的变换矩阵。
上述两个方法中都涉及一个关于矩阵的API:CGAffineTransform。
创建CGAffineTransform的4种方式:
1. CGAffineTransform CGAffineTransformMakeTranslation ( CGFloat tx, CGFloat ty ):创建进行位移变换的变换矩阵。该函数的两个参数与前面介绍的位移变换的两个参数的作用相同。
2. CGAffineTransform CGAffineTransformMakeScale ( CGFloat sx, CGFloat sy ):创建进行缩放变换的变换矩阵。该函数的两个参数与前面介绍的缩放变换的两个参数的作用相同。
3. CGAffineTransform CGAffineTransformMakeRotation ( CGFloat angle ):创建进行旋转变换的变换矩阵。该函数的参数与前面介绍的旋转变换的参数的作用相同。
4. CGAffineTransform CGAffineTransformMake ( CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty ):该函数使用自定义变换矩阵执行变换。
三、复合矩阵坐标变换
1. CGAffineTransform CGAffineTransformTranslate ( CGAffineTransform t, CGFloat tx, CGFloat, ty ):对已有的变换矩阵 t 额外增加位移变换。
2. CGAffineTransform CGAffineTransformScale ( CGAffineTransform t, CGFloat sx, CGFloat sy ):对已有的变换矩阵 t 额外增加缩放变换。
3. CGAffineTransform CGAffineTransformRotate ( CGAffineTransform t, CGFloat angle ):对已有的变换矩阵 t 额外增加旋转变换。
4. CGAffineTransform CGAffineTransformInvert ( CGAffineTransform t ):对已有的变换矩阵 t 进行反转。
5. CGAffineTransform CGAffineTransformConcat ( CGAffineTransform t1, CGAffineTransform t2 ):将两个变换矩阵进行叠加。
四、对CGPoint、CGSize和CGRect进行坐标变换
1. CGPoint CGPointApplyAffineTransform ( CGPoint point, CGAffineTransform t ):对指定的CGPoint执行变换,函数返回坐标变换后的CGPoint。
2. CGSize CGSizeApplyAffineTransform ( CGSize size, CGAffineTransform t ):对指定的CGSize执行变换,函数返回坐标变换后的CGSize。
3. CGRect CGRectApplyAffineTransform ( CGRect rect, CGAffineTransform t ):对指定的CGRect执行变换,函数返回坐标变换后的CGRect。
-(void)drawChangImage:(CGContextRef)context{
//保存初始状态
CGContextSaveGState(context);
//形变第一步:图形上下文向右平移50
CGContextTranslateCTM(context, 50, 0);
//形变第二步:缩放0.8
CGContextScaleCTM(context, 0.8, 0.8);
//形变第三步:旋转
CGContextRotateCTM(context, M_PI_4/4);
UIImage *image=[UIImage imageNamed:@"1.jpg"];
[image drawInRect:CGRectMake(0, 50, 240, 300)];
//恢复到初始状态
CGContextRestoreGState(context);
}
图片变化状态如下:依次为原图->平移->缩放->旋转.
扩展--使用Core Graphics绘制图像
在前面基本绘图部分,绘制图像时使用了UIKit中封装的方法进行了图像绘制,我们不妨看一下使用Quartz 2D内置方法绘制是什么效果。
-(void)drawImage2:(CGContextRef)context{
UIImage *image=[UIImage imageNamed:@"jt_03.png"];
//图像绘制
CGRect rect= CGRectMake(10, 50, 300, 450);
CGContextDrawImage(context, rect, image.CGImage);
}
效果图:
看起来整个图像是倒过来的,原因正是前面说的:在Core Graphics中坐标系的y轴正方向是向上的,坐标原点在屏幕左下角,y轴方向刚好和UIKit中y轴方向相反。而使用UIKit进行绘图之所以没有问题是因为UIKit中进行了处理,事实上对于其他图形即使使用Core Graphics绘制也没有问题,因为UIKit统一了编程方式。但是使用Core Graphics中内置方法绘制图像是存在这种问题的,如何解决呢?看一下坐标系就明白了:
其实图形上下文只要沿着x轴旋转180度,然后向上平移适当的高度即可(但是注意不要沿着z轴旋转,这样得不到想要的结果)。可是通过前面介绍的CGContextRotateCTM方法只能通过沿着z轴旋转,此时不妨使用另外一种方法,那就是在y轴方向缩放-1,同样可以达到想要的效果:
-(void)drawImage2Improve:(CGContextRef)context{效果图:
UIImage *image=[UIImage imageNamed:@"jt_03.png"];
CGSize size=[UIScreen mainScreen].bounds.size;
CGContextSaveGState(context);
CGFloat height=450,y=50;
//上下文形变
CGContextScaleCTM(context, 1.0, -1.0);//在y轴缩放-1相当于沿着x张旋转180
CGContextTranslateCTM(context, 0, -(size.height-(size.height-2*y-height)));//向上平移
//图像绘制
CGRect rect= CGRectMake(10, y, 300, height);
CGContextDrawImage(context, rect, image.CGImage);
CGContextRestoreGState(context);
}