Transforms

Quartz 2D绘图模型定义了两个完全独立的坐标空间:表示文档页面的用户空间和表示设备本机分辨率的设备空间。 用户空间坐标是与设备空间中像素的分辨率无关的浮点数。 当您打印或显示文档时,Quartz会将用户空间坐标映射到设备空间坐标。 因此,您不必重写应用程序或编写额外的代码来调整应用程序的输出,以便在不同设备上实现最佳显示。

您可以通过对当前转换矩阵或CTM进行操作来修改默认用户空间。 创建图形上下文后,CTM是单位矩阵。 您可以使用Quartz变换函数修改CTM,因此,在用户空间中修改图形。
本章:

  • 提供可用于执行转换的功能的概述
  • 显示如何修改CTM
  • 描述如何创建仿射变换
  • 显示如何确定两个变换是否相等
  • 介绍如何获取用户到设备空间的转换
  • 讨论仿射变换后的数学

关于Quartz变换函数

您可以使用Quartz 2D内置转换函数轻松平移,缩放和旋转图形。 只需几行代码,就可以按任何顺序和任意组合应用这些转换。 图5-1说明了缩放和旋转图像的效果。 您应用的每个转换更新CTM。 CTM始终表示用户空间和设备空间之间的当前映射。 此映射确保您的应用程序的输出在任何显示屏幕或打印机上看起来不错。

5-1 应用缩放和旋转

Quartz 2D API提供了五个功能,允许您获取和修改CTM。 您可以旋转,平移和缩放CTM,您可以将仿射变换矩阵与CTM连接。 请参阅修改当前转换矩阵。

Quartz还允许创建不对用户空间进行操作的仿射变换,直到您决定将变换应用于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与仿射变换组合。 仿射变换和创建它们的函数在创建仿射变换中讨论。

实现累积效应的另一种方式是执行两个或多个变换,而不在变换调用之间恢复图形状态。 图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 旋转,缩放,然后平移的图像

创建仿射变换

Quartz中可用的仿射变换函数对矩阵而不是CTM进行操作。 您可以使用这些函数构建一个矩阵,稍后通过调用函数CGContextConcatCTM将其应用于CTM。 仿射变换函数操作或返回CGAffineTransform数据结构。 您可以构造可重用的简单或复杂仿射变换。

仿射变换函数执行与CTM函数相同的操作 - 平移,旋转,缩放和连接。 表5-1列出了执行这些操作的功能及其使用信息。 请注意,每个平移,旋转和缩放操作都有两个函数。

仿射变换函数用于平移,旋转和缩放

Quartz还提供了一个反转矩阵的仿射变换函数,CGAffineTransformInvert。反转通常用于提供变换对象内的点的逆变换。当需要恢复由矩阵变换的值时,反转可能很有用:反转矩阵,并将值乘以反转矩阵,结果为原始值。您通常不需要反转变换,因为您可以通过保存和恢复图形状态来逆转换CTM的效果。

在某些情况下,您可能不想变换整个空间,而只是一个点或大小。您通过调用CGPointApplyAffineTransform函数在CGPoint结构上操作。您通过调用CGSizeApplyAffineTransform函数在CGSize结构上操作。您可以通过调用CGRectApplyAffineTransform函数对CGRect结构进行操作。此函数返回包含传递给它的矩形的转换角点的最小矩形。如果对矩形操作的仿射变换仅执行缩放和平移操作,则返回的矩形与从四个变换的角构造的矩形重合。

您可以通过调用函数CGAffineTransformMake创建一个新的仿射变换,但与其他创建新仿射变换的函数不同,这需要您提供矩阵条目。要有效地使用此功能,您需要了解矩阵数学。见矩阵的数学。

评估仿射变换

您可以通过调用函数CGAffineTransformEqualToTransform来确定一个仿射变换是否等于另一个。 如果传递给它的两个变换相等,此函数返回true,否则返回false。

函数CGAffineTransformIsIdentity是用于检查变换是否是标识变换的有用函数。 身份转换不执行转换,缩放或旋转。 将此变换应用于输入坐标始终返回输入坐标。 Quartz常量CGAffineTransformIdentity表示身份转换。

使用户到设备空间转换

通常当你使用Quartz 2D绘制时,你只能在用户空间中工作。 Quartz负责为用户和设备空间之间进行转换。 如果您的应用程序需要获得Quartz用于在用户和设备空间之间进行转换的仿射变换,则可以调用函数CGContextGetUserSpaceToDeviceSpaceTransform。

Quartz提供了许多方便的功能来转换用户空间和设备空间之间的以下几何。 您可能会发现这些函数比应用从函数CGContextGetUserSpaceToDeviceSpaceTransform返回的仿射变换更容易使用。

  • Points。 函数CGContextConvertPointToDeviceSpace和CGContextConvertPointToUserSpace将CGPoint数据类型从一个空间转换为另一个空间。
  • Sizes。 函数CGContextConvertSizeToDeviceSpace和CGContextConvertSizeToUserSpace将CGSize数据类型从一个空间转换为另一个空间。
  • Rectangles。 函数CGContextConvertRectToDeviceSpace和CGContextConvertRectToUserSpace将CGRect数据类型从一个空间转换为另一个空间。

矩阵后面的数学

您需要理解矩阵数学的唯一的Quartz 2D函数是函数CGAffineTransformMake,它从3 x 3矩阵中的6个关键条目进行仿射变换。 即使你从来没有计划从头构建仿射变换矩阵,你可能会发现变换函数背后的数学很有趣。 如果没有,您可以跳过本章的剩余部分。

3×3变换矩阵-a,b,c,d,tx和ty-的六个临界值在以下矩阵中示出:

注:矩阵的最右边的列总是包含常量值0,0,1。在数学上,这个第三列是允许级联的,这将在本节后面解释。 它出现在本节仅为了数学正确性。

给定上述3×3变换矩阵,Quartz使用该方程将点(x,y)变换为结果点(x',y'):

结果在不同的坐标系统中,由变换矩阵中的变量值变换的坐标系统。 以下等式是以前矩阵变换的定义:

下矩阵是单位矩阵。 它不执行转换,缩放或旋转。 将该矩阵乘以输入坐标总是返回输入坐标。

使用前面讨论的公式,您可以看到,该矩阵将生成与旧点(x,y)相同的新点(x',y'):

该矩阵描述了平移操作:

这些是Quartz用来应用翻译的结果方程:

该矩阵描述对点(x,y)的缩放操作:

这些是Quartz用来缩放坐标的结果方程:

该矩阵描述了旋转操作,将点(x,y)逆时针旋转角度α:

这些是Quartz用来应用旋转的结果方程:

这个等式将旋转操作与平移操作连接起来:

这些是Quartz用来应用变换的结果方程:

注意,连接矩阵的顺序是重要的 - 矩阵乘法不是可交换的。也就是说,将矩阵A乘以矩阵B的结果不一定等于矩阵B乘以矩阵A的结果。

如前所述,级联是仿射变换矩阵包含具有常数值0,0,1的第三列的原因。为了将一个矩阵与另一个矩阵相乘,一个矩阵的列数必须与另一个矩阵的行数相匹配。这意味着2×3矩阵不能与2×3矩阵相乘。因此,我们需要包含常量值的额外列。

反演操作从变换的坐标产生原始坐标。给定已经由给定矩阵A变换到新坐标(x',y')的坐标(x,y),通过矩阵A的逆变换坐标(x',y')产生原始坐标x,y)。当矩阵乘以其逆时,结果是单位矩阵。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容

  • Quartz 2D 绘制模型定义了两个完全不同的坐标系统,用户空间,它代表文档页。设备空间,代表设备原生的分辨率。...
    雪_晟阅读 214评论 0 1
  • 本文转载自:http://southpeak.github.io/2014/12/02/quartz2d-5/ Q...
    idiot_lin阅读 214评论 0 0
  • Quartz 2D绘图定义了两个完全独立的坐标空间,表示文档页面的用户空间和表示设备的本机解析的设备空间。用户空间...
    亭竹丶阅读 1,434评论 0 0
  • 如果存在判断的标准,那便是,上帝发给你的 牌你要如何进行,他永远高高在上的低声在耳边交 谈,无时无刻,此...
    心的字阅读 198评论 1 1
  • 文/A 幸运点 我其实一直清楚,有些文字,不是写给别人的,只是写给自己那依旧尚存的初心。 这许多年来,我一直很幸运...
    A幸运点阅读 241评论 12 5