Transforms

Quartz 2D繪圖模型定義了兩個完全獨(dú)立的坐標(biāo)空間:表示文檔頁面的用戶空間和表示設(shè)備本機(jī)分辨率的設(shè)備空間。 用戶空間坐標(biāo)是與設(shè)備空間中像素的分辨率無關(guān)的浮點(diǎn)數(shù)。 當(dāng)您打印或顯示文檔時,Quartz會將用戶空間坐標(biāo)映射到設(shè)備空間坐標(biāo)。 因此,您不必重寫應(yīng)用程序或編寫額外的代碼來調(diào)整應(yīng)用程序的輸出,以便在不同設(shè)備上實(shí)現(xiàn)最佳顯示。

您可以通過對當(dāng)前轉(zhuǎn)換矩陣或CTM進(jìn)行操作來修改默認(rèn)用戶空間。 創(chuàng)建圖形上下文后,CTM是單位矩陣。 您可以使用Quartz變換函數(shù)修改CTM,因此,在用戶空間中修改圖形。
本章:

  • 提供可用于執(zhí)行轉(zhuǎn)換的功能的概述
  • 顯示如何修改CTM
  • 描述如何創(chuàng)建仿射變換
  • 顯示如何確定兩個變換是否相等
  • 介紹如何獲取用戶到設(shè)備空間的轉(zhuǎn)換
  • 討論仿射變換后的數(shù)學(xué)

關(guān)于Quartz變換函數(shù)

您可以使用Quartz 2D內(nèi)置轉(zhuǎn)換函數(shù)輕松平移,縮放和旋轉(zhuǎn)圖形。 只需幾行代碼,就可以按任何順序和任意組合應(yīng)用這些轉(zhuǎn)換。 圖5-1說明了縮放和旋轉(zhuǎn)圖像的效果。 您應(yīng)用的每個轉(zhuǎn)換更新CTM。 CTM始終表示用戶空間和設(shè)備空間之間的當(dāng)前映射。 此映射確保您的應(yīng)用程序的輸出在任何顯示屏幕或打印機(jī)上看起來不錯。

5-1 應(yīng)用縮放和旋轉(zhuǎn)

Quartz 2D API提供了五個功能,允許您獲取和修改CTM。 您可以旋轉(zhuǎn),平移和縮放CTM,您可以將仿射變換矩陣與CTM連接。 請參閱修改當(dāng)前轉(zhuǎn)換矩陣。

Quartz還允許創(chuàng)建不對用戶空間進(jìn)行操作的仿射變換,直到您決定將變換應(yīng)用于CTM。 您使用另一組函數(shù)創(chuàng)建仿射變換,然后可以與CTM連接。 請參閱創(chuàng)建仿射變換。

你可以使用任何一組函數(shù),而不了解矩陣數(shù)學(xué)的任何東西。 然而,如果你想了解什么Quartz在調(diào)用一個轉(zhuǎn)換函數(shù)時,請閱讀矩陣后面的數(shù)學(xué)。

修改當(dāng)前轉(zhuǎn)換矩陣

在繪制圖像之前,您可以操縱CTM來旋轉(zhuǎn),縮放或平移頁面,從而轉(zhuǎn)換要繪制的對象。 在轉(zhuǎn)換CTM之前,您需要保存圖形狀態(tài),以便您可以在繪制后恢復(fù)它。 您還可以將CTM與仿射變換連接(請參閱創(chuàng)建仿射變換)。 這四個操作(平移,旋轉(zhuǎn),縮放和連接)中的每一個都與執(zhí)行每個操作的CTM函數(shù)一起在本節(jié)中描述。

以下代碼行繪制圖像,假設(shè)您提供了有效的圖形上下文,指向要繪制圖像的矩形的指針以及有效的CGImage對象。 代碼繪制圖像,如圖5-2所示的示例公雞圖像。 閱讀本部分的其余部分時,您將看到在應(yīng)用轉(zhuǎn)換時圖像如何更改。

CGContextDrawImage (myContext, rect, myImage);
5-2 未平移的圖像

平移將坐標(biāo)空間的原點(diǎn)移動您為x和y軸指定的量。 您調(diào)用函數(shù)CGContextTranslateCTM以指定的量修改每個點(diǎn)的x和y坐標(biāo)。 圖5-3顯示了使用以下代碼行在x軸上平移100個單位,在y軸上平移50個單位的圖像:

CGContextTranslateCTM (myContext, 100, 50);
5-3 平移的圖像

旋轉(zhuǎn)以您指定的角度移動坐標(biāo)空間。 您調(diào)用函數(shù)CGContextRotateCTM來指定旋轉(zhuǎn)角度(以弧度表示)。 圖5-4顯示了使用以下代碼行繞原點(diǎn)旋轉(zhuǎn)-45度的圖像,該原點(diǎn)是窗口的左下角:

CGContextRotateCTM (myContext, radians(–45.));

圖像被裁剪,因?yàn)樾D(zhuǎn)將圖像的一部分移動到上下文之外的位置。 您需要指定以弧度表示的旋轉(zhuǎn)角度。

如果你計(jì)劃執(zhí)行多次旋轉(zhuǎn),寫一個弧度例程是有用的。

#include <math.h>
static inline double radians (double degrees) {return degrees * M_PI/180;}
5-4 一個旋轉(zhuǎn)的圖像

縮放通過您指定的x和y因子更改坐標(biāo)空間的比例,有效地拉伸或縮小圖像。 x和y因子的大小決定新坐標(biāo)是大于還是小于原始坐標(biāo)。 此外,通過使x因子為負(fù),可以沿著x軸翻轉(zhuǎn)坐標(biāo); 類似地,通過使y因子為負(fù),可以沿著y軸水平地翻轉(zhuǎn)坐標(biāo)。 您調(diào)用函數(shù)CGContextScaleCTM以指定x和y縮放因子。 圖5-5顯示了一個圖像,其x值由.5縮放,其y值由.75縮放,使用以下代碼行:

CGContextScaleCTM (myContext, .5, .75);
5-5 一個縮放的圖像

連接通過將兩個矩陣相乘在一起來組合它們。 您可以連接幾個矩陣以形成包含矩陣的累積效應(yīng)的單個矩陣。 您調(diào)用函數(shù)CGContextConcatCTM將CTM與仿射變換組合。 仿射變換和創(chuàng)建它們的函數(shù)在創(chuàng)建仿射變換中討論。

實(shí)現(xiàn)累積效應(yīng)的另一種方式是執(zhí)行兩個或多個變換,而不在變換調(diào)用之間恢復(fù)圖形狀態(tài)。 圖5-6顯示了通過平移圖像然后旋轉(zhuǎn)圖像而產(chǎn)生的圖像,使用以下代碼行:

CGContextTranslateCTM (myContext, w,h);
CGContextRotateCTM (myContext, radians(-180.));
5-6 平移和旋轉(zhuǎn)的圖像

圖5-7顯示了使用以下代碼行平移,縮放和旋轉(zhuǎn)的圖像:

CGContextTranslateCTM (myContext, w/4, 0);
CGContextScaleCTM (myContext, .25,  .5);
CGContextRotateCTM (myContext, radians ( 22.));
5-7 平移,縮放,然后旋轉(zhuǎn)的圖像

執(zhí)行多個轉(zhuǎn)換的順序很重要; 如果逆轉(zhuǎn)順序,您會得到不同的結(jié)果。 反轉(zhuǎn)用于創(chuàng)建圖5-7的轉(zhuǎn)換順序,您將得到如圖5-8所示的結(jié)果,該結(jié)果是使用以下代碼生成的:

CGContextRotateCTM (myContext, radians ( 22.));
CGContextScaleCTM (myContext, .25,  .5);
CGContextTranslateCTM (myContext, w/4, 0);
5-8 旋轉(zhuǎn),縮放,然后平移的圖像

創(chuàng)建仿射變換

Quartz中可用的仿射變換函數(shù)對矩陣而不是CTM進(jìn)行操作。 您可以使用這些函數(shù)構(gòu)建一個矩陣,稍后通過調(diào)用函數(shù)CGContextConcatCTM將其應(yīng)用于CTM。 仿射變換函數(shù)操作或返回CGAffineTransform數(shù)據(jù)結(jié)構(gòu)。 您可以構(gòu)造可重用的簡單或復(fù)雜仿射變換。

仿射變換函數(shù)執(zhí)行與CTM函數(shù)相同的操作 - 平移,旋轉(zhuǎn),縮放和連接。 表5-1列出了執(zhí)行這些操作的功能及其使用信息。 請注意,每個平移,旋轉(zhuǎn)和縮放操作都有兩個函數(shù)。

仿射變換函數(shù)用于平移,旋轉(zhuǎn)和縮放

Quartz還提供了一個反轉(zhuǎn)矩陣的仿射變換函數(shù),CGAffineTransformInvert。反轉(zhuǎn)通常用于提供變換對象內(nèi)的點(diǎn)的逆變換。當(dāng)需要恢復(fù)由矩陣變換的值時,反轉(zhuǎn)可能很有用:反轉(zhuǎn)矩陣,并將值乘以反轉(zhuǎn)矩陣,結(jié)果為原始值。您通常不需要反轉(zhuǎn)變換,因?yàn)槟梢酝ㄟ^保存和恢復(fù)圖形狀態(tài)來逆轉(zhuǎn)換CTM的效果。

在某些情況下,您可能不想變換整個空間,而只是一個點(diǎn)或大小。您通過調(diào)用CGPointApplyAffineTransform函數(shù)在CGPoint結(jié)構(gòu)上操作。您通過調(diào)用CGSizeApplyAffineTransform函數(shù)在CGSize結(jié)構(gòu)上操作。您可以通過調(diào)用CGRectApplyAffineTransform函數(shù)對CGRect結(jié)構(gòu)進(jìn)行操作。此函數(shù)返回包含傳遞給它的矩形的轉(zhuǎn)換角點(diǎn)的最小矩形。如果對矩形操作的仿射變換僅執(zhí)行縮放和平移操作,則返回的矩形與從四個變換的角構(gòu)造的矩形重合。

您可以通過調(diào)用函數(shù)CGAffineTransformMake創(chuàng)建一個新的仿射變換,但與其他創(chuàng)建新仿射變換的函數(shù)不同,這需要您提供矩陣條目。要有效地使用此功能,您需要了解矩陣數(shù)學(xué)。見矩陣的數(shù)學(xué)。

評估仿射變換

您可以通過調(diào)用函數(shù)CGAffineTransformEqualToTransform來確定一個仿射變換是否等于另一個。 如果傳遞給它的兩個變換相等,此函數(shù)返回true,否則返回false。

函數(shù)CGAffineTransformIsIdentity是用于檢查變換是否是標(biāo)識變換的有用函數(shù)。 身份轉(zhuǎn)換不執(zhí)行轉(zhuǎn)換,縮放或旋轉(zhuǎn)。 將此變換應(yīng)用于輸入坐標(biāo)始終返回輸入坐標(biāo)。 Quartz常量CGAffineTransformIdentity表示身份轉(zhuǎn)換。

使用戶到設(shè)備空間轉(zhuǎn)換

通常當(dāng)你使用Quartz 2D繪制時,你只能在用戶空間中工作。 Quartz負(fù)責(zé)為用戶和設(shè)備空間之間進(jìn)行轉(zhuǎn)換。 如果您的應(yīng)用程序需要獲得Quartz用于在用戶和設(shè)備空間之間進(jìn)行轉(zhuǎn)換的仿射變換,則可以調(diào)用函數(shù)CGContextGetUserSpaceToDeviceSpaceTransform。

Quartz提供了許多方便的功能來轉(zhuǎn)換用戶空間和設(shè)備空間之間的以下幾何。 您可能會發(fā)現(xiàn)這些函數(shù)比應(yīng)用從函數(shù)CGContextGetUserSpaceToDeviceSpaceTransform返回的仿射變換更容易使用。

  • Points。 函數(shù)CGContextConvertPointToDeviceSpace和CGContextConvertPointToUserSpace將CGPoint數(shù)據(jù)類型從一個空間轉(zhuǎn)換為另一個空間。
  • Sizes。 函數(shù)CGContextConvertSizeToDeviceSpace和CGContextConvertSizeToUserSpace將CGSize數(shù)據(jù)類型從一個空間轉(zhuǎn)換為另一個空間。
  • Rectangles。 函數(shù)CGContextConvertRectToDeviceSpace和CGContextConvertRectToUserSpace將CGRect數(shù)據(jù)類型從一個空間轉(zhuǎn)換為另一個空間。

矩陣后面的數(shù)學(xué)

您需要理解矩陣數(shù)學(xué)的唯一的Quartz 2D函數(shù)是函數(shù)CGAffineTransformMake,它從3 x 3矩陣中的6個關(guān)鍵條目進(jìn)行仿射變換。 即使你從來沒有計(jì)劃從頭構(gòu)建仿射變換矩陣,你可能會發(fā)現(xiàn)變換函數(shù)背后的數(shù)學(xué)很有趣。 如果沒有,您可以跳過本章的剩余部分。

3×3變換矩陣-a,b,c,d,tx和ty-的六個臨界值在以下矩陣中示出:

注:矩陣的最右邊的列總是包含常量值0,0,1。在數(shù)學(xué)上,這個第三列是允許級聯(lián)的,這將在本節(jié)后面解釋。 它出現(xiàn)在本節(jié)僅為了數(shù)學(xué)正確性。

給定上述3×3變換矩陣,Quartz使用該方程將點(diǎn)(x,y)變換為結(jié)果點(diǎn)(x',y'):

結(jié)果在不同的坐標(biāo)系統(tǒng)中,由變換矩陣中的變量值變換的坐標(biāo)系統(tǒng)。 以下等式是以前矩陣變換的定義:

下矩陣是單位矩陣。 它不執(zhí)行轉(zhuǎn)換,縮放或旋轉(zhuǎn)。 將該矩陣乘以輸入坐標(biāo)總是返回輸入坐標(biāo)。

使用前面討論的公式,您可以看到,該矩陣將生成與舊點(diǎn)(x,y)相同的新點(diǎn)(x',y'):

該矩陣描述了平移操作:

這些是Quartz用來應(yīng)用翻譯的結(jié)果方程:

該矩陣描述對點(diǎn)(x,y)的縮放操作:

這些是Quartz用來縮放坐標(biāo)的結(jié)果方程:

該矩陣描述了旋轉(zhuǎn)操作,將點(diǎn)(x,y)逆時針旋轉(zhuǎn)角度α:

這些是Quartz用來應(yīng)用旋轉(zhuǎn)的結(jié)果方程:

這個等式將旋轉(zhuǎn)操作與平移操作連接起來:

這些是Quartz用來應(yīng)用變換的結(jié)果方程:

注意,連接矩陣的順序是重要的 - 矩陣乘法不是可交換的。也就是說,將矩陣A乘以矩陣B的結(jié)果不一定等于矩陣B乘以矩陣A的結(jié)果。

如前所述,級聯(lián)是仿射變換矩陣包含具有常數(shù)值0,0,1的第三列的原因。為了將一個矩陣與另一個矩陣相乘,一個矩陣的列數(shù)必須與另一個矩陣的行數(shù)相匹配。這意味著2×3矩陣不能與2×3矩陣相乘。因此,我們需要包含常量值的額外列。

反演操作從變換的坐標(biāo)產(chǎn)生原始坐標(biāo)。給定已經(jīng)由給定矩陣A變換到新坐標(biāo)(x',y')的坐標(biāo)(x,y),通過矩陣A的逆變換坐標(biāo)(x',y')產(chǎn)生原始坐標(biāo)x,y)。當(dāng)矩陣乘以其逆時,結(jié)果是單位矩陣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Quartz 2D 繪制模型定義了兩個完全不同的坐標(biāo)系統(tǒng),用戶空間,它代表文檔頁。設(shè)備空間,代表設(shè)備原生的分辨率。...
    雪_晟閱讀 290評論 0 1
  • 本文轉(zhuǎn)載自:http://southpeak.github.io/2014/12/02/quartz2d-5/ Q...
    idiot_lin閱讀 300評論 0 0
  • Quartz 2D繪圖定義了兩個完全獨(dú)立的坐標(biāo)空間,表示文檔頁面的用戶空間和表示設(shè)備的本機(jī)解析的設(shè)備空間。用戶空間...
    亭竹丶閱讀 1,542評論 0 0
  • 如果存在判斷的標(biāo)準(zhǔn),那便是,上帝發(fā)給你的 牌你要如何進(jìn)行,他永遠(yuǎn)高高在上的低聲在耳邊交 談,無時無刻,此...
    心的字閱讀 291評論 1 1
  • 文/A 幸運(yùn)點(diǎn) 我其實(shí)一直清楚,有些文字,不是寫給別人的,只是寫給自己那依舊尚存的初心。 這許多年來,我一直很幸運(yùn)...
    A幸運(yùn)點(diǎn)閱讀 327評論 12 5

友情鏈接更多精彩內(nèi)容