Quartz 2D官方文档翻译(持续更新中)
转换
核心绘图模型定义了两个完全独立的坐标空间:用户空间,一个是代表文档页,和设备空间,另外一个代表本机设备的分辨率。用户空间坐标是与设备空间中像素分辨率无关的浮点数字。当你想要打印或者显示你的文档时,Quartz maps当中用户空间坐标会自动转换为设备空间坐标。因此,你永远不必重写你的应用程序,或者写下额外的代码来调整你在不同设备上的应用程序的输出。
你可以通过在当前转换矩阵上修改默认的用户空间或者 CTM,你创建一个图形上下文后,CTM 是单位矩阵。你可以通过Quartz 转换功能去修改 CTM,得到一个结果,修改用户空间绘图。
这章:
* 提供一个可以用来执行转换功能的概述
* 说明如何去修改 CTM
* 描述如何创建一个仿射变换
* 说明如何确定两个转换是等价的
* 描述如何获得用户到设备空间的转换
* 讨论数学当中的仿射转换
关于核心绘图转换功能
你能够很容易的知道,用Quartz 2D内置的转换功能能够缩放和旋转你的绘图,只需要几行代码,你可以将这些转换功能应用于任何顺序和任何组合当中。图5-1说明缩放和旋转图像的效果。每个转换你都应用于更新 CTM。CTM总是代表用户空间和设备空间之间的映射,此映射确保了在任何显示屏幕或打印机上的应用程序的输出都很好。
图5-1 应用缩放和旋转
核心绘图 API 提供了五个功能允许你获取并修改 CTM,你可以旋转,平移和缩放 CTM。并且你也可以连接一个仿射变换矩阵和CTM,查看修改当前转换矩阵。
还可以让你创建一个仿射转换,直到你决定应用变换 CTM,才可以在用户空间操作,你用另外一个函数创建仿射变化,然后可以与 CTM 级联。见创建仿射变化。
你可以使用任何一组函数,而不用知道关于数学矩阵相关的任何事情。但是如果你想要知道当你用 Quartz 进行转换功能时,它内部做了什么,此时,需要具备数学中矩阵的知识。
修改当前变换矩阵
你操作 CTM 进行旋转、缩放或者转换前绘制图像,从而改变你所要绘制的对象。在你改变 CTM 前,你需要保存图形状态,以便可以在绘制后还原它。你也可以将 CTM 与仿射变换结合起来(请参见创建仿射变换,这四个操作平移,旋转,缩放,和连接是本节中描述的随着CTM,执行每个操作的功能。
下面这行代码绘制的图像,如果你提供一个有效的图形上下文,一个指向矩形绘制的图像,和一个有效的CGImage对象。代码绘制的图像,如图5-2所示的图像样本的公鸡。当你阅读本节的其他部分时,你会看到如何应用转换来改变图像的变化。
CGContextDrawImage (myContext, rect, myImage);
图5-2 图像不改变
转换移动原点坐标空间当中 x轴和 y 轴的量,CGContextTranslateCTM的功能能够修改 x 轴和 y 轴坐标指定的值。图5-3显示图像示例在 x 轴的100个点和 y 轴的50个点,见如下代码:
CGContextTranslateCTM (myContext, 100, 50);
图5-3 示例图
旋转移动你指定的坐标空间的角度,CGContextRotateCTM功能能够指定旋转角度和弧度。图5-4显示图像的原点旋转45度,这是窗口的左下方,使用以下代码行:
CGContextRotateCTM (myContext, radians(–45.));
图像被剪裁,因为旋转移动图片的一部分到上下文外的一个位置,需要指定旋转角度或者弧度。
如果你计划执行许多旋转,写一个弧度程序很有用。
#include <math.h> static inline double radians (double degrees) {return degrees * M_PI/180;}
图片5-4 旋转示意图
缩放比例变化的坐标控件的规模,由指定的 x 和 y决定,有效的拉伸或者缩小图片。x和 y 值的大小决定新的坐标是大于或者小于原来的坐标,另外,让 x 是负值,你可以沿着 x 轴进行水平坐标翻转,沿着 y 轴,使 y 值变负值,调用函数CGContextScaleCTM去指定 x 和 y 缩放值,图5-5示例图显示 x 缩放5,y 缩放75,示例代码如下:
CGContextScaleCTM (myContext, 5, 75);
图5-5 示例图
结合两个矩阵级联一起,你可以将几个矩阵形成一个矩阵,包含了矩阵的累积效应,你可以调用CGContextConcatCTM函数去结合 CTM 和仿射变换,仿射变换和创建的函数是在讨论创建仿射变换。实现一个累积效应的另一种方法是执行2个或多个转换,而不需要恢复转换调用之间的图形状态,图5-6示例图像是旋转后的结果展示,示例代码如下:
CGContextTranslateCTM (myContext, w,h); CGContextRotateCTM (myContext, radians(-180.));
图5-6 展示旋转后的图像
图5-7 展示图像的翻转、旋转角度、缩放,示例代码如下:
CGContextTranslateCTM (myContext, w/4, 0); CGContextScaleCTM (myContext, .25, .5); CGContextRotateCTM (myContext, radians ( 22.));
图5-7 展示图像的翻转、旋转角度、缩放
执行多个转换的顺序问题;如果你颠倒了顺序,会得到不同的结果,颠倒创建图形5-7的顺序,你会得到5-8图形的结果,示例代码如下:
CGContextRotateCTM (myContext, radians ( 22.)); CGContextScaleCTM (myContext, .25, .5); CGContextTranslateCTM (myContext, w/4, 0);
图 5-8 一个图形的旋转、角度和翻转
创建仿射变换
仿射变换操作在矩阵上,而不是在CTM上。我们可以使用这些函数来构造一个之后用于CTM(调用函数CGContextConcatCTM)的矩阵。仿射变换函数使用或者返回一个CGAffineTransform数据对象。我们可以构建简单或复杂的仿射变换。
仿射变换函数能实现与CTM函数相同的操作--平移、旋转、缩放、联合。表5-1列出了仿射变换函数及其用途。注意每种变换都有两个函数。
表5-1 仿射变换函数
Quartz同样提供了一个仿射变换函数(CGAffineTransformInvert)来倒置矩阵。倒置操作通常用于在变换对象中提供点的倒置变换。当我们需要恢复一个被矩阵变换的值时,可以使用倒置操作。将值与倒置矩阵相乘,就可得到原先的值。我们通常不需要倒置操作,因为我们可以通过保存和恢复图形状态来倒置CTM的效果。
在一些情况下,我们可能不需要变换整修空间,而只是一个点或一个大小。我们通过调用CGPointApplyAffineTransform在CGPoint结构上执行变换操作。调用CGSizeApplyAffineTransform在CGSize结构上执行变换操作。调用CGRectApplyAffineTransform在CGRect结构上执行变换操作。CGRectApplyAffineTransform返回一个最小的矩形,该矩形包含了被传递给CGRectApplyAffineTransform的矩形对象的角点。如果矩形上的仿射变换操作只有缩放和平移操作,则返回的矩形与四个变换后的角组成的矩形是一致的。
可以通过调用函数CGAffineTransformMake来创建一个新的仿射变换,但与其它函数不同的是,它需要提供一个矩阵实体。
评价仿射变换
我们可以通过调用CGAffineTransformEqualToTransform函数来决定一个仿射变换是否与另一个相同。如果两个变换相同,则返回true;否则返回false。
函数CGAffineTransformIsIdentity用于确认一个变换是否是单位变换。单位变换没有平移、缩放和旋转操作。Quartz常量CGAffineTransformIdentity表示一个单位变换。
获取用户空间到设备空间的变换
当使用Quartz 2D时,我们只是在用户空间下工作。Quartz为我们处理用户空间和设备空间的转换。如果我们的应用程序需要获取Quartz转换用户空间和设备空间的仿射变换,我们可以调用函数CGContextGetUserSpaceToDeviceSpaceTransform。
Quartz提供了一系列的函数来转换用户空间和设备空间的几何体。我们会发现这些函数使用起来比使用CGContextGetUserSpaceToDeviceSpaceTransform函数返回的仿射变换更好用。
* 点: 函数CGContextConvertPointToDeviceSpace和CGContextConvertPointToUserSpace将一个CGPoint数据结构从一个空间变换到另一个空间。
* 大小:函数CGContextConvertSizeToDeviceSpace和CGContextConvertSizeToUserSpace将一个CGSize数据结构从一个空间变换到另一个空间。
* 矩形:函数CGContextConvertRectToDeviceSpace和CGContextConvertRectToUserSpace将一个CGPoint数据结构从一个空间变换到另一个空间。