- Quartz2D 編程指南(一)概覽周瞎、圖形上下文苗缩、路徑、顏色與顏色空間
- Quartz2D 編程指南(二)變換声诸、圖案酱讶、陰影
- Quartz2D 編程指南(三)漸變、透明層 彼乌、數(shù)據(jù)管理
- Quartz2D 編程指南(四)位圖與圖像遮罩泻肯、CoreGraphics 繪制 Layer
- 概覽
- 圖形上下文
- 路徑
- 顏色與顏色空間
- 變換
- 圖案
- 陰影
- 漸變
- 透明層
- Quartz 2D 中的數(shù)據(jù)管理
- 位圖與圖像遮罩
- CoreGraphics 繪制 Layer
位圖與圖像遮罩
簡介
- 位圖與圖像遮罩和 Quartz 中的其它繪制元素一樣渊迁。兩者在 Quartz 中都是用 CGImageRef 數(shù)據(jù)類型來表示。
位圖和圖像遮罩
一個位圖是一個像素數(shù)組灶挟。每一個像素表示圖像中的一個點琉朽。JPEG, TIFF 和 PNG 圖像文件都是位圖。應(yīng)用程序的 icon 也是位圖稚铣。
位圖中的每一個采樣包含特定顏色空間下的一個或更多顏色分量箱叁,以及一個額外的用于指定 alpha 值以表示透明度的分量。
Quartz同樣支持圖像遮罩惕医。一個圖像遮罩也是一個位圖耕漱,它指定了一個繪制區(qū)域,而不是顏色抬伺。一個顏色遮罩可以有 1 - 8 位的深度螟够。
位圖信息
- 創(chuàng)建 CGImageRef 時,Quartz 需要使用以下信息沛简。
CGImageRef __nullable CGImageCreate(size_t width, size_t height,
size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow,
CGColorSpaceRef __nullable space, CGBitmapInfo bitmapInfo,
CGDataProviderRef __nullable provider,
const CGFloat * __nullable decode, bool shouldInterpolate,
CGColorRenderingIntent intent);
- 位圖數(shù)據(jù)源:可以是一個 Quartz 數(shù)據(jù)提供者(provider)或者是一個 Quartz 圖像源(source)。
- 可選的解碼數(shù)組斥废。
- 插值設(shè)置:這是一個布爾值椒楣,指定 Quartz 在重置圖像大小時是否使用插值算法。
- 渲染意圖:指定如何映射位于圖形上下文中的目標(biāo)顏色空間中的顏色牡肉。該值在圖像遮罩中不需要捧灰。
- 圖像尺寸
- 像素格式:包括每個分量中的位數(shù),每個像素的位數(shù)和每行中的字節(jié)數(shù)统锤。
- 顏色空間和位圖布局信息:描述了 alpha 的位置和位置是否使用浮點值毛俏。圖像遮罩不需要這個信息。
一些 Quartz 圖像創(chuàng)建函數(shù)需要我們指定所有的信息饲窿,而其它函數(shù)只需要部分信息煌寇。我們所需要提供的信息依賴于位圖數(shù)據(jù)的編碼,以及位圖是表示一個圖像還是圖像遮罩逾雄。
Quartz提供了很多圖像格式并內(nèi)建了多種常用的格式阀溶。在 iOS 中,這些格式包括 JPEG鸦泳,GIF银锻,PNG,TIF做鹰,ICO击纬,GMP,XBM 和 CUR钾麸。其它的位圖格式或?qū)S懈袷叫枰覀冎付▓D像格式的詳細信息更振,以便 Quartz 能正確地解析圖像炕桨。
我們提供給 CGImageCreate 函數(shù)的圖像數(shù)據(jù)必須是位圖。Quartz不支持矢量圖殃饿。
解碼數(shù)組
解碼數(shù)組包含每個顏色分量的一對數(shù)值用來將圖像顏色值線性映射到其它顏色值谋作。對于諸如對一個圖像做去飽和或者反轉(zhuǎn)顏色值非常有用。
在 RGB 顏色空間中的一個圖像的解碼數(shù)組包含 6 個數(shù)值乎芳,分別用于紅遵蚜、綠、藍顏色分量奈惑。
像素格式
- 像素格式包含以下信息吭净。
- 每個分量的位數(shù):即在一個像素中每個獨立顏色分量的位數(shù)。對于一個圖像遮罩肴甸,這個值是源像素中遮罩 bit 的數(shù)目寂殉。例如,如果源圖片是 8-bit 的遮罩原在,則指定每個分量是 8 位友扰。
- 每個像素的位數(shù):即一個源像素所占的總的位數(shù)。這個值必須至少是每個分量的位數(shù)乘以每個像素中分量的數(shù)目庶柿。
- 每行的字節(jié)數(shù):即圖像中水平行的字節(jié)數(shù)村怪。
顏色空間和位圖布局
- 為了確保 Quartz 能正確的解析每個像素,我們必須指定:
- 位圖是否包含 alpha 通道浮庐。Quartz 支持 RGB甚负,CMYK 和灰度顏色空間。同時支持 alpha审残,或 transparency梭域,雖然并不是所有位圖圖像格式都支持 alpha 通道。alpha 通道可用時搅轿,alpha 分量可以位于像素的高位或地位病涨。
- 對于有 alpha 分量的位圖,指定顏色分量是否已經(jīng)乘以了 alpha 值璧坟。預(yù)乘 alpha(Premultiplied alpha)表示一個已將顏色分量乘以了 alpha 值的源顏色没宾。這種預(yù)處理通過消除每個顏色分量的額外的乘法運算來加速圖片的渲染。
- 采樣的數(shù)據(jù)格式是整型還是浮點型沸柔。
- 當(dāng)我們使用 CGImageCreate 函數(shù)來創(chuàng)建圖像時循衰,我們提供一個類型為 CGBitmapInfo(包含 CGImageAlphaInfo)的 bitmapInfo 參數(shù)來指定位置布局信息。CGImageAlphaInfo 的常量指定了 alpha 分量的位置及顏色分量是否做了預(yù)處理褐澎。
- kCGImageAlphaLast:alpha 分量存儲在每個像素中的低位会钝,如RGBA。
- kCGImageAlphaFirst:alpha 分量存儲在每個像素中的高位,如ARGB迁酸。
- kCGImageAlphaPremultipliedLast:alpha 分量存儲在每個像素中的低位先鱼,同時顏色分量已經(jīng)乘以了 alpha 值。
- kCGImageAlphaPremultipliedFirst:alpha 分量存儲在每個像素中的高位奸鬓,同時顏色分量已經(jīng)乘以了 alpha 值焙畔。
- kCGImageAlphaNoneSkipLast:沒有 alpha 分量。如果像素的總大小大于顏色空間中顏色分量數(shù)目所需要的空間串远,則低位將被忽略宏多。
- kCGImageAlphaNoneSkipFirst:沒有 alpha 分量。如果像素的總大小大于顏色空間中顏色分量數(shù)目所需要的空間澡罚,則高位將被忽略伸但。
- kCGImageAlphaNone:等于kCGImageAlphaNoneSkipLast。
- CGBitmapInfo 的常量則指定了采樣的數(shù)據(jù)格式是整型還是浮點型留搔。
typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {
kCGBitmapAlphaInfoMask = 0x1F,
kCGBitmapFloatInfoMask = 0xF00,
kCGBitmapFloatComponents = (1 << 8),
kCGBitmapByteOrderMask = 0x7000,
kCGBitmapByteOrderDefault = (0 << 12),
kCGBitmapByteOrder16Little = (1 << 12),
kCGBitmapByteOrder32Little = (2 << 12),
kCGBitmapByteOrder16Big = (3 << 12),
kCGBitmapByteOrder32Big = (4 << 12)
};
- 我們使用常量 kCGBitmapFloatComponents 來標(biāo)識一個位圖格式使用浮點值更胖。對于浮點格式,我們將這個常量與上述描述的合適的常量進行按位與操作來組成 bitmapInfo 的參數(shù)隔显。
kCGImageAlphaPremultipliedLast | kCGBitmapFloatComponents
- 下圖演示了一個像素在使用 16 或 32bit 整型像素格式的 CMYK 和 RGB 顏色空間中如何表示却妨。Quartz 同樣支持 128bit 浮點像素格式,每個分量占 32 位括眠。128bit 格式?jīng)]有顯示在下圖中彪标。

創(chuàng)建圖像
- 我們可以使用如下函數(shù)來創(chuàng)建 CGImageRef。
CGImageRef imageRef = [UIImage imageNamed:@"image"].CGImage;
CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
CGImageRef image = CGImageCreate(80, 80, 8, 32, 320,
CGColorSpaceCreateDeviceRGB(),
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big,
dataProvider,
NULL, true,
kCGRenderingIntentPerceptual);
//A flexible function for creating an image. You must specify all the bitmap information that is discussed in Bitmap Image Information.
NSString *path = [[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"];
NSURL *url = [NSURL fileURLWithPath:path];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
// Creates an image from an image source. Image sources can contain more than one image. See Data Management in Quartz 2D for information on creating an image source.
CFStringRef string[] = {kCGImageSourceShouldCache};
CFBooleanRef boolean[] = {kCFBooleanTrue};
CFDictionaryRef dic = CFDictionaryCreate(kCFAllocatorDefault, (void *)string, (void *)boolean, 1, NULL, NULL);
image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, dic);
// Creates a thumbnail image of an image that is associated with an image source. See Data Management in Quartz 2D for information on creating an image source.
image = CGBitmapContextCreateImage(context);
// Creates an image by copying the bits from a bitmap graphics context.
image = CGImageCreateWithImageInRect(imageRef, CGRectMake(0, 0, 80, 80));
// Creates an image from the data contained within a sub-rectangle of an image.
image = CGImageCreateCopy(imageRef);
// A utility function that creates a copy of an image.
image = CGImageCreateCopyWithColorSpace(imageRef, CGColorSpaceCreateDeviceRGB());
// A utility function that creates a copy of an image and replaces its color space.
CGContextDrawImage(context, CGRectMake(0, 0, 80, 80), image);
image = CGBitmapContextCreateImage(context);
最常用的函數(shù)是 CGImageCreate哺窄。它可以從任何類型的位圖數(shù)據(jù)來創(chuàng)建圖像捐下。然而账锹,它是最復(fù)雜的函數(shù)萌业,因為需要提供所有的位圖信息。為了使用這個函數(shù)奸柬,我們需要熟悉上面討論的所有內(nèi)容生年。
在使用 CGImageCreate 創(chuàng)建圖像時必須提供正確的參數(shù),否則圖像將無法正確繪制廓奕。我們在測試時可以使用如下函數(shù)來獲取正確的圖像參數(shù)抱婉。
CGImageRef imageRef = [UIImage imageNamed:@"image"].CGImage;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
size_t bitsPerRow = CGImageGetBytesPerRow(imageRef);
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
CGFloat *decode = CGImageGetDecode(imageRef);
bool shouldInterpolate = CGImageGetShouldInterpolate(imageRef);
CGColorRenderingIntent renderingIntent = CGImageGetRenderingIntent(imageRef);
如果我們想從標(biāo)準(zhǔn)的圖像格式(PNG、JPEG 等)來創(chuàng)建圖片桌粉,則最簡單的方法是調(diào)用函數(shù) CGImageSourceCreateWithURL 來創(chuàng)建一個圖像源蒸绩,然后調(diào)用CGImageSourceCreateImageAtIndex 以使用從圖像源中索引 index 指定的圖像數(shù)據(jù)來創(chuàng)建圖像。如果圖像文件格式支持包含多個圖像的文件铃肯,則需要提供所需要圖像的索引值患亿。index 起始值為 0。
其中部分函數(shù)可以操作已有的圖像,如拷貝步藕、創(chuàng)建縮略圖惦界,或從大圖像中創(chuàng)建裁剪的圖像。
CGImage 是不可變的咙冗。當(dāng)不再需要 CGImage 對象時沾歪,調(diào)用 CGImageRelease 函數(shù)來釋放它。
從大圖像中創(chuàng)建裁剪的圖像
調(diào)用 CGImageCreateWithImageInRect(imageRef, CGRectMake(0, 0, 80, 80)) 從大圖像中創(chuàng)建裁剪的圖像雾消。
函數(shù) CGImageCreateWithImageInRect 返回的圖像保留了源圖像的引用灾搏,所以我們可以在調(diào)用完這個函數(shù)后可以釋放源圖像。
從上下文中獲取圖片
如果我們已經(jīng)將內(nèi)容渲染到圖圖形上下文仪或,可以調(diào)用 CGBitmapContextCreateImage(context)(或 UIKit 提供的便捷函數(shù)UIGraphicsGetImageFromCurrentImageContext)函數(shù)從上下文中獲取圖片确镊。
這個函數(shù)返回的 CGImage 對象是通過一個拷貝操作創(chuàng)建的。因此我們對位圖圖形上下文所做的修改都不會影響到已返回的CGImage對象范删。
在某些情況下蕾域,這個拷貝操作實際上沿用了 copy-on-write 語義,即只有當(dāng)位圖圖形上下文中的數(shù)據(jù)被修改時才會去實際拷貝這些數(shù)據(jù)到旦。我們可能需要在繪制額外數(shù)據(jù)到位圖圖形上下文之前使用結(jié)果數(shù)據(jù)或者釋放它們旨巷,以避免實際去拷貝這些數(shù)據(jù)。
創(chuàng)建圖像遮罩
位圖圖像遮罩定義了如何轉(zhuǎn)換顏色添忘,而不是使用哪些顏色采呐。圖像遮罩中的每個采樣值指定了在特定位置中,當(dāng)前填充顏色值被遮罩的數(shù)量搁骑。采樣值指定了遮罩的不透明度斧吐。值越大,表示越不透明仲器,Quartz 在指定位置繪制的顏色越少煤率。
圖像遮罩的每個分量可能是 1/2/4/8 位。1bit 的遮罩乏冀,要么完全遮擋蝶糯,要么完全顯示。2/4/8bit 的遮罩代表灰度值辆沦,每個分量使用以下的公式值映射到 [0, 1] 之間昼捍。

- 調(diào)用函數(shù) CGImageMaskCreate 創(chuàng)建 Quartz 圖像遮罩。我們提供的信息與創(chuàng)建圖像所提供的信息相同肢扯,只是不需要提供顏色空間信息妒茬,位圖信息常量或渲染意圖。
CG_EXTERN CGImageRef __nullable CGImageMaskCreate(
size_t width,
size_t height,
size_t bitsPerComponent,
size_t bitsPerPixel,
size_t bytesPerRow,
CGDataProviderRef __nullable provider,
const CGFloat * __nullable decode,
bool shouldInterpolate
);
遮罩圖像
遮罩技術(shù)可以讓我們通過控制圖片的哪一部分被繪制蔚晨,以生成很多有趣的效果乍钻,我們可以:
- 使用圖像遮罩來遮罩圖像。我們也可以把圖像作為遮罩圖,以獲取同使用圖像遮罩相反的效果团赁。
- 使用顏色來遮罩圖像育拨。其中包含被稱為顏色遮罩的技術(shù)。
- 使用圖像或圖像遮罩來裁剪上下文欢摄。實際是裁剪 Quartz 繪制內(nèi)容到裁剪的圖形上下文時的圖片熬丧。
使用圖像遮罩來遮罩圖像
- 函數(shù) CGImageCreateWithMask(CGImageRef image, CGImageRef mask) 通過將圖像遮罩使用到一個圖像上的方式來創(chuàng)建一個圖像。這個函數(shù)帶有兩個參數(shù):
- 原始圖像怀挠,遮罩將用于其上析蝴。這個圖像不能是圖像遮罩,也不能有與之相關(guān)的遮罩顏色绿淋。
- 圖像遮罩闷畸,通過調(diào)用 CGImageMaskCreate 函數(shù)創(chuàng)建。也可以提供一個圖像來替代圖像遮罩吞滞,但這將獲得同使用圖像遮罩相反的效果佑菩。
- 圖像遮罩采樣值 S = 1 時,則不會繪制對應(yīng)的圖像樣本裁赠。S = 0 時殿漠,則允許完全繪制對應(yīng)的圖像樣本。S = 0 ~ 1 時佩捞,則讓對應(yīng)的圖像樣本的 alpha 的值為(1-S)绞幌。
-
原圖
-
使用 CGImageMaskCreate 創(chuàng)建的圖像遮罩
-
原圖被遮罩后的圖像
使用圖像來遮罩圖像
- 假設(shè)上圖圖 2 是使用 CGImageCreate 創(chuàng)建的,而不是使用 CGImageMaskCreate 創(chuàng)建的一忱。當(dāng)其傳入 CGImageCreateWithMask(CGImageRef image, CGImageRef mask) 后則會獲得相反的效果莲蜘。

使用顏色來遮罩圖像
- 使用 CGImageCreateWithMaskingColors(CGImageRef image, const CGFloat * components) 通過遮罩一種顏色或一個顏色范圍內(nèi)的顏色來創(chuàng)建一個圖像。函數(shù) CGImageCreateWithMaskingColors 有兩個參數(shù):
- 一個圖像帘营,它不能是遮罩圖像票渠,也不能是使用過圖像遮罩或顏色遮罩的圖像。
- 一個顏色分量數(shù)組仪吧,指定了一個顏色或一組顏色值庄新,以用于遮罩圖像鞠眉。

顏色分量數(shù)組中元素的個數(shù)必須等于圖像所在顏色空間的顏色分量數(shù)目的兩倍薯鼠。如果圖像使用整型像素分量,則顏色分量數(shù)組中的每個值必須在 [0 ~ 2bitsPerComponent – 1] 范圍之內(nèi)械蹋。如果圖像使用浮點像素分量出皇,則值可以是表示任何有效的顏色分量值的浮點數(shù)。
通過如下代碼使用顏色范圍來遮罩圖像可以獲得如下圖片的效果哗戈。
const CGFloat myMaskingColors[6] = {124, 255, 68, 222, 0, 165};
CGImageRef myColorMaskedImage = CGImageCreateWithMaskingColors(image, myMaskingColors);
CGContextDrawImage(context, myContextRect, myColorMaskedImage);

- 我們同樣可以設(shè)置填充顏色來作為圖像的遮罩顏色郊艘,來得到如下圖片的效果,其中被遮罩區(qū)域使用了填充顏色。
const CGFloat myMaskingColors[6] = {0, 124, 0, 68, 0, 0};
CGImageRef myColorMaskedImage = CGImageCreateWithMaskingColors(image, myMaskingColors);
CGContextSetRGBFillColor (myContext, 0.6373,0.6373, 0, 1);
CGContextFillRect(context, rect);
CGContextDrawImage(context, rect, myColorMaskedImage);
通過裁減上下文來遮罩圖片
- 調(diào)用 CGContextClipToMask(CGContextRef c, CGRect rect, CGImageRef mask) 裁減上下文來遮罩圖片纱注。
- 需要裁減的圖形上下文
- 要使用遮罩的矩形區(qū)域
- 一個圖像遮罩畏浆,通過 CGImageMaskCreate 函數(shù)創(chuàng)建。我們可以使用圖像來替代圖像遮罩以達到相反的效果狞贱。圖像必須使用 Quartz 圖像創(chuàng)建函數(shù)來創(chuàng)建刻获,但不能是使用過圖像遮罩或顏色遮罩的圖像。
- 裁剪的效果如下圖所示瞎嬉。
-
通過 CGImageMaskCreate 創(chuàng)建的圖像遮罩蝎毡。
-
被裁剪后的繪制到圖形上下文的圖片。
-
當(dāng)遮罩圖像是使用 CGImageCreate 創(chuàng)建氧枣,而不是使用 CGImageMaskCreate 創(chuàng)建的時沐兵,可以得到相反的效果。
在圖像中使用混合模式
- 使用 CGContextSetBlendMode 來設(shè)置混合模式在“路徑”章節(jié)中的“混合模式”小節(jié)中已經(jīng)講過便监,不再重復(fù)講述扎谎。
CoreGraphics 繪制 Layer
簡介
- CGLayer 對象(CGLayerRef 數(shù)據(jù)類型)允許程序使用層來進行繪制。層適合于以下幾種情況:
- 高質(zhì)量離屏渲染烧董,以繪制我們想重用的圖形簿透。例如,我們可能要建立一個場景并重用相同的背景解藻。將背景場景繪制于一個層上老充,然后在需要的時候再繪制層。繪制層的一個額外的好處是我們不需要知道顏色空間或其它設(shè)備依賴的信息螟左。
- 重復(fù)繪制啡浊。例如,我們可能想創(chuàng)建一個由相同元素反復(fù)繪制而組成的圖案胶背。將元素繪制到一個層中巷嚣,然后重復(fù)繪制這個層。任何我們重復(fù)繪制的 Quartz 對象钳吟,包括 CGPath, CGShading 和 CGPDFPage廷粒,都可以通過將其繪制到 CGLayer 來優(yōu)化性能。需要注意的是層不僅僅是用于在屏幕上繪制红且;我們也可以將其用于那些不是面向屏幕的圖形上下文坝茎,如 PDF 圖形上下文。
- 緩存暇番。雖然我們可以將層用于此目的嗤放,但通常不需要這樣做,因為 Quartz Compositor 已經(jīng)做了此事壁酬。如果我們必須繪制一個緩存次酌,則使用層來代替位圖圖形上下文恨课。
- CGLayer 對象和透明層與 CGPath 和 CGContext 函數(shù)創(chuàng)建的 paths 是對等的。對于 CGLayer 或 CGPath 對象岳服,我們可以將其繪制到一個抽象目標(biāo)剂公,之后可以將其完整地繪制到另一個目標(biāo),如顯示器或 PDF 中吊宋。當(dāng)我們在透明層上繪制或使用繪制路徑的 CGContext 函數(shù)時诬留,可以直接繪制到圖形上下文表示的目標(biāo)上,而不需要負責(zé)組裝繪制的中間抽象目標(biāo)贫母。
Layer 繪制原理
層由 CGLayerRef 數(shù)據(jù)類型表示文兑,是為優(yōu)化性能而設(shè)計的。在可能的時候腺劣,Quartz 使用合適的機制將 CGLayer 對象緩存到與之相關(guān)的 Quartz 圖形上下文中绿贞。例如匠题,與顯卡相關(guān)的圖形上下文可能將層緩存到顯卡中匾七,這樣繪制在層中的內(nèi)容時狠半,就比渲染從位圖圖形上下文中構(gòu)造的類似圖像要快得多涩笤。基于這個原因匆浙,層比位圖圖形上下文更適用于離屏繪制中燥。
所有的 Quartz 繪制函數(shù)都是繪制到圖形上下文中顾彰。圖形上下文提供了一個抽象的渲染目標(biāo)芋酌,而將我們從目標(biāo)的細節(jié)中解放出來增显。我們使用用戶空間,Quartz 執(zhí)行必要的轉(zhuǎn)換來將繪圖正確地渲染到目標(biāo)脐帝。當(dāng)我們使用 CGLayer 對象來繪制時同云,我們也是繪制到圖形上下文中。
使用 CGLayer 對象進行繪制的原理如下所示堵腹。

調(diào)用 CGLayerCreateWithContext 從圖形上下文中創(chuàng)建 CGLayer炸站。這個 CGLayer 具有圖形上下文的所有特性(包括分辨率,顏色空間和圖形狀態(tài)設(shè)置)疚顷。如果我們不想使用圖形上下文的大小旱易,也可以指定 CGLayer 的小大。
繪制層之前需要調(diào)用 CGLayerGetContext 來獲取與層相關(guān)的圖形上下文腿堤。這個圖形上下文與用于創(chuàng)建 CGLayer 的圖形上下文差不多阀坏。只要用于創(chuàng)建層的圖形上下文是 Window 圖形上下文,則 CGLayer 圖形上下文會盡可能地被緩存到 GPU 中释液。
調(diào)用 CGContextDrawLayerInRect 或 CGContextDrawLayerAtPoint 將層繪制到圖形上下文全释。通常情況下装处,我們會將層繪制到創(chuàng)建層對象的圖形上下文中误债,但這不是必須的浸船。我們可以將層繪制到任意的圖形上下文,但層帶有創(chuàng)建層對象的圖形上下文的所有特性寝蹈,這會使原始圖形上下文的所有限制都會反映到我們的繪圖中(性能李命、分辨率)。例如箫老,與屏幕關(guān)聯(lián)的層可能會被緩存到顯卡中封字。如果目標(biāo)上下文是打印機或 PDF 上下文,則可能需要將層對象從顯卡中取出并放到內(nèi)存中耍鬓,從而導(dǎo)致性能很差阔籽。
- 我們可以在釋放 CGLayer 對象之前,任意地重復(fù)使用層中的繪圖牲蜀。
使用層來繪制
- 使用 CGLayer 對象進行繪制需要如下幾個步驟笆制。
- 創(chuàng)建從已存在的圖形上下文初始化的 CGLayer 對象
- 為 CGLayer 獲取圖形上下文
- 繪制到 CGLayer 圖形上下文
- 將 CGLayer 繪制到目標(biāo)圖形上下文
創(chuàng)建從已存在的圖形上下文初始化的 CGLayer 對象
- 函數(shù) CGLayerCreateWithContext 帶有三個參數(shù):
CGLayerRef CGLayerCreateWithContext(
CGContextRef context,
CGSize size,
CFDictionaryRef auxiliaryInfo)
- 用于創(chuàng)建 CGLayer 的圖形上下文。通常我們傳遞一個 Window 圖形上下文以便后面可以離屏繪制層涣达。
- 層相對于圖形上下文的大小在辆。層的大小可以和圖形上下文一樣,或者更小度苔。如果想要獲得層的大小匆篓,我們可以調(diào)用函數(shù) CGLayerGetSize(CGLayerRef layer)。
- 輔助字典寇窑。這個參數(shù)現(xiàn)在已經(jīng)不用了鸦概,所以傳遞 NULL 即可。
為 CGLayer 獲取圖形上下文
Quartz 總是在圖形上下文中進行繪制∷ィ現(xiàn)在我們有了 CGLayer 對象完残,還必須創(chuàng)建一個與 CGLayer 相關(guān)的圖形上下文。所有繪制到 CGLayer 圖形上下文的內(nèi)容都是 CGLayer 的一部分横漏。
使用 CGContextRef CGLayerGetContext(CGLayerRef layer) 獲取與 CGLayer 相關(guān)的圖形上下文谨设。
繪制到 CGLayer 圖形上下文
在獲取到與 CGLayer 相關(guān)的圖形上下文之后,我們可以在 CGLayer 圖形上下文中繪制任何東西缎浇。
為了在 CGLayer 圖形上下文中繪制一個填充矩形扎拣,我們調(diào)用函數(shù) CGContextFillRect,并提供從 CGLayerGetContext 函數(shù)中獲取到的圖形上下文作為參數(shù)素跺。
CGContextFillRect(myLayerContext, myRect)
將層繪制到目標(biāo)圖形上下文
- 當(dāng)我們已經(jīng)準(zhǔn)備好將 CGLayer 繪制到目標(biāo)圖形上下文時二蓝,我們可以使用以下函數(shù)來進行繪制。
- CGContextDrawLayerInRect:將層繪制到圖形上下文中指定的矩形內(nèi)指厌。
- CGContextDrawLayerAtPoint:將層繪制到圖形上下文中指定的點刊愚。
- 通常情況下,我們提供的目標(biāo)圖形上下文是 Window 圖形上下文踩验,這也是我們用于創(chuàng)建 CGLayer 對象所使用的圖形上下文鸥诽。為了達到圖案的效果商玫,我們可以使用上述方法,只是每次改變偏移量而已牡借。例如拳昌,我們每次繪制層時,可以調(diào)用函數(shù) CGContextTranslateCTM 來改變坐標(biāo)系統(tǒng)的原點钠龙。
使用多個 CGLayer 對象來繪制星條旗

- 從上圖可以看出炬藤,旗子主要分三部分。
- 紅色條紋和白色條紋的圖案碴里。我們可以將這個圖案分解為一個單一的紅色條紋沈矿,因為對于屏幕繪制來說,我們可以設(shè)置其背景顏色為白色咬腋。我們創(chuàng)建一個紅色矩形细睡,然后以變化的偏移量來重復(fù)繪制這個矩形,以創(chuàng)建美國國旗上的七條紅色條紋帝火。我們將紅色矩形繪制到一個層溜徙,然后將其繪制到屏幕上七次。
- 一個藍色矩形犀填。我們只需要一個藍色矩形蠢壹,所以沒有必要使用層。當(dāng)繪制藍色矩形時九巡,直接將其繪制到屏幕上图贸。
- 50 個白色星星的圖案。與紅色條紋一樣冕广,可以使用層來繪制星星疏日。我們創(chuàng)建星星邊框的一個路徑,然后使用白條來填充撒汉。將一個星星繪制到層沟优,然后重復(fù) 50 次繪制這個層,每次繪制時適當(dāng)調(diào)整偏移量睬辐。
void myDrawFlag (CGContextRef context, CGRect* contextRect) {
int i, j, num_six_star_rows = 5, num_five_star_rows = 4;
CGFloat start_x = 5.0, start_y = 108.0, red_stripe_spacing = 34.0, h_spacing = 26.0, v_spacing = 22.0;
CGContextRef myLayerContext1, myLayerContext2;
CGLayerRef stripeLayer, starLayer;
CGRect myBoundingBox, stripeRect, starField;
// ***** Setting up the primitives *****
CGPoint point1 = {5, 5}, point2 = {10, 15}, point3 = {10, 15}, point4 = {15, 5};
CGPoint point5 = {15, 5}, point6 = {2.5, 11}, point7 = {2.5, 11}, point8 = {16.5, 11};
CGPoint point9 = {16.5, 11}, point10 = {5, 5};
const CGPoint myStarPoints[] = {point1, point2,
point3, point4,
point5, point6,
point7, point8,
point9, point10};
stripeRect = CGRectMake(0, 0, 400, 17); // stripe
starField = CGRectMake(0, 102, 160, 119); // star field
myBoundingBox = CGRectMake (0, 0, contextRect->size.width, contextRect->size.height);
// ***** Creating layers and drawing to them *****
stripeLayer = CGLayerCreateWithContext(context, stripeRect.size, NULL);
myLayerContext1 = CGLayerGetContext(stripeLayer);
CGContextSetRGBFillColor(myLayerContext1, 1, 0 , 0, 1);
CGContextFillRect(myLayerContext1, stripeRect);
starLayer = CGLayerCreateWithContext(context, starField.size, NULL);
myLayerContext2 = CGLayerGetContext(starLayer);
CGContextSetRGBFillColor(myLayerContext2, 1.0, 1.0, 1.0, 1);
CGContextAddLines(myLayerContext2, myStarPoints, 10);
CGContextFillPath(myLayerContext2);
// ***** Drawing to the window graphics context *****
CGContextSaveGState(context);
for (i = 0; i < 7; i++) {
CGContextDrawLayerAtPoint(context, CGPointZero, stripeLayer);
CGContextTranslateCTM(context, 0.0, red_stripe_spacing);
}
CGContextRestoreGState(context);
CGContextSetRGBFillColor(context, 0, 0, 0.329, 1.0);
CGContextFillRect(context, starField);
CGContextSaveGState (context);
CGContextTranslateCTM (context, start_x, start_y);
for (j = 0; j < num_six_star_rows; j++) {
for (i = 0; i < 6; i++) {
CGContextDrawLayerAtPoint(context,CGPointZero, starLayer);
CGContextTranslateCTM(context, h_spacing, 0);
}
CGContextTranslateCTM(context, (-i * h_spacing), v_spacing);
}
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, start_x + h_spacing/2, start_y + v_spacing/2);
for (j = 0; j < num_five_star_rows; j++) {
for (i = 0; i < 5; i++) {
CGContextDrawLayerAtPoint(context, CGPointZero, tarLayer);
CGContextTranslateCTM(context, h_spacing, 0);
}
CGContextTranslateCTM(context, (-i * h_spacing), v_spacing);
}
CGContextRestoreGState(context);
CGLayerRelease(stripeLayer);
CGLayerRelease(starLayer);
}
博客:xuyafei.cn
簡書:jianshu.com/users/2555924d8c6e
微博:weibo.com/xuyafei86
Github:github.com/xiaofei86