Quartz 2D編程指南之十一:位圖與圖像遮罩

本文轉(zhuǎn)載自:http://southpeak.github.io/2015/01/05/quartz2d-11/

位圖與圖像遮罩和Quartz中的其它繪制元素一樣。這兩者在Quartz中都是用CGImageRef數(shù)據(jù)類型來表示。正如在本章后面看到的一樣,我們有一系列的方法來創(chuàng)建一個圖像。其中一些需要數(shù)據(jù)提供者或圖像源來提供位圖數(shù)據(jù)。另外一些函數(shù)則通過拷貝圖像或在圖像上應用操作來從已存在的圖像中創(chuàng)建圖像。不管我們是以何種方式來創(chuàng)建圖像,我們都可以將圖像繪制到任何類型的圖形上下文。記住,位圖是在指定分辨率下的一個字節(jié)數(shù)組。如果我們將位圖繪制到一個依賴于分辨率的圖形上下文中(如PDF圖形上下文),則位圖受限于創(chuàng)建它的圖形上下文的分辨率。

我們可以通過調(diào)用CGImageMaskCreate函數(shù)來創(chuàng)建一個Quartz圖像遮罩。我們將在“創(chuàng)建圖像遮罩”一節(jié)中看到如何創(chuàng)建遮罩。使用圖像遮罩不是繪制遮罩的唯一方法,具體的我們都會在下面看到。

位圖和圖像遮罩

一個位圖是一個像素數(shù)組。每一個像素表示圖像中的一個點。JPEG, TIFF和PNG圖像文件都是位圖。應用程序的icon也是位圖。位圖被限定在一個矩形內(nèi)。但是通過使用alpha分量,它們可以呈現(xiàn)不同的形式,也可以旋轉(zhuǎn)或被裁剪,如圖11-1所示:

Figure 11-1 Bitmap images

位圖中的每一個采樣包含特定顏色空間下的一個或更多顏色分量,以及一個額外的用于指定alpha值以表示透明度的分量。每一個分量可以是從1-32位。在Mac OS X中,Quartz支持浮點值分量。在Mac OS X和iOS中支持的格式將會在下文中介紹。ColorSync提供了位圖支持的顏色空間。

Quartz同樣支持圖像遮罩(image masks)。一個圖像遮罩也是一個位圖,它指定了一個繪制區(qū)域,而不是顏色。從效果上來說,一個圖像遮罩更像一個模塊,它指定在page中繪制顏色的位置。Quartz使用當前的填充顏色來繪制一個圖像遮罩。一個顏色遮罩可以有1-8位的深度。

位圖信息

Quartz提供了很多圖像格式并內(nèi)建了多種常用的格式。在iOS中,這些格式包括JPEG, GIF, PNG, TIF, ICO, GMP, XBM, 和CUR。其它的位圖格式或?qū)S懈袷叫枰覀冎付▓D像格式的詳細信息,以便Quartz能正確地解析圖像。我們提供給CGImageCreate函數(shù)的圖像數(shù)據(jù)必須是以像素為單位的,而不是基于掃描線的。Quartz不支持平面數(shù)據(jù)。

這一節(jié)描述了與位圖相關(guān)的信息。當我們創(chuàng)建并使用Quartz圖像時(使用CGImageRef數(shù)據(jù)類型),我們將看到一些Quartz圖像創(chuàng)建函數(shù)需要我們指定所有的信息,而其它函數(shù)只需要部分信息。我們所需要提供的信息依賴于位圖數(shù)據(jù)的編碼,以及位圖是表示一個圖像還是圖像遮罩。

注意:當使用原始圖像數(shù)據(jù)時,為了獲得更好的性能,我們可以使用vImage框架。我們可以使用vImageBuffer_InitWithCGImage函數(shù)從一個CGImageRef引用導入圖像數(shù)據(jù)到vImage中。

創(chuàng)建一個位圖(CGImageRef)時,Quartz使用以下信息:

位圖數(shù)據(jù)源:可以是一個Quartz數(shù)據(jù)提供者或者是一個Quartz圖像源。

可選的解碼數(shù)組。(Decode Array)

插值設(shè)置:這是一個布爾值,指定Quartz在重置圖像大小時是否使用插值算法。

渲染意圖:指定如何映射位于圖形上下文中的目標顏色空間中的顏色。該值在圖像遮罩中不需要。

圖像尺寸

像素格式,包括每個分量中的位數(shù),每個像素的位數(shù)和每行中的字節(jié)數(shù)。

對于圖像來說,顏色空間和位圖布局信息描述了alpha的位置和位置是否使用浮點值。圖像遮罩不需要這個信息。

解碼數(shù)組

一個解碼數(shù)組將圖像顏色值映射到其它顏色值,這對于諸如對一個圖像做去飽和或者反轉(zhuǎn)顏色值非常有用。數(shù)組包含每個顏色分量的一個數(shù)值對。當Quartz渲染圖像時,它利用一個線性轉(zhuǎn)換將原始分量值映射到一個目標顏色空間中的指定范圍內(nèi)一個相關(guān)值。例如,在RGB顏色空間中的一個圖像的解碼數(shù)組包含6個輸入,分別用于紅、綠、藍顏色分量。

像素格式

像素格式包含以下信息:

每個分量的位數(shù),即在一個像素中每個獨立顏色分量的位數(shù)。對于一個圖像遮罩,這個值是源像素中遮罩bit的數(shù)目。例如,如果源圖片是8-bit的遮罩,則指定每個分量是8位。

每個像素的位數(shù),即一個源像素所占的總的位數(shù)。這個值必須至少是每個分量的位數(shù)乘以每個像素中分量的數(shù)目。

每行的字節(jié)數(shù),即圖像中水平行的字節(jié)數(shù)。

顏色空間和位圖布局

為了確保Quartz能正確的解析每個像素的位,我們必須指定:

一個位圖是否包含alpha通道。Quartz包含RGB,CMYK和灰度顏色空間。它也支持alpha,或者透明度,雖然并不是所有位圖圖像格式都支持alpha通道。當它可用時,alpha分量可以位于像素最顯著的位置,也可以是最不顯著的位置。

對于有alpha分量的位圖,指定顏色分量是否已經(jīng)乘以了alpha值。預乘alpha(Premultiplied alpha)表示一個已將顏色分量乘以了alpha值的源顏色。這種預處理通過消除每個顏色分量的額外的乘法運算來加速圖片的渲染。

采樣的數(shù)據(jù)格式–是整型還是浮點型。

當我們使用CGImageCreate函數(shù)來創(chuàng)建一個圖像時,我們提供一個類型為CGImageBitmapInfo的bitmapInfo參數(shù),來指定位置布局信息。以下的常量指定了alpha分量的位置及顏色分量是否做預處理:

kCGImageAlphaLast:alpha分量存儲在每個像素中最不顯著的位置,如RGBA。

kCGImageAlphaFirst:alpha分量存儲在每個像素中最顯著的位置,如ARGB。

kCGImageAlphaPremultipliedLast:alpha分量存儲在每個像素中最不顯著的位置,但顏色分量已經(jīng)乘以了alpha值。

kCGImageAlphaPremultipliedFirst:alpha分量存儲在每個像素中最顯著的位置,同時顏色分量已經(jīng)乘以了alpha值。

kCGImageAlphaNoneSkipLast:沒有alpha分量。如果像素的總大小大于顏色空間中顏色分量數(shù)目所需要的空間,則最不顯著位置的位將被忽略。

kCGImageAlphaNoneSkipFirst:沒有alpha分量。如果像素的總大小大于顏色空間中顏色分量數(shù)目所需要的空間,則最顯著位置的位將被忽略。

kCGImageAlphaNone:等于kCGImageAlphaNoneSkipLast。

我們使用常量kCGBitmapFloatComponents來標識一個位圖格式使用浮點值。對于浮點格式,我們將這個常量與上而描述的合適的常量進行邏輯OR操作。例如,對于每個像素有128位的使用預處理的浮點格式,同時alpha值位于像素中最不顯示位置,我們將以下信息提供給Quartz:

kCGImageAlphaPremultipliedLast | kCGBitmapFloatComponents

圖11-2演示了一個像素在使用16-或32-bit整型像素格式的CMYK和RGB顏色空間中如何表示。32-bit整型像素格式中,每個分量占8位。16-bit整型像素格式中每個分量占5位。Quartz同樣支持128-bit浮點像素格式,每個分量占32位。128-bit格式?jīng)]有顯示在下圖中。

Figure 11-2 32-bit and 16-bit pixel formats for CMYK and RGB color spaces in Quartz 2D

創(chuàng)建圖像

表11-1羅列了Quartz提供的用于創(chuàng)建CGImageRef對象的函數(shù)。函數(shù)的選擇依賴于圖像的數(shù)據(jù)源。最常用的函數(shù)是CGImageCreate。它可以從任何類型的位圖數(shù)據(jù)來創(chuàng)建一個圖像。然而,它是最復雜的函數(shù),因為需要提供所有的位圖信息。為了使用這個函數(shù),我們需要熟悉上面討論的位圖圖像信息的內(nèi)容。

如果我們想從一個標準的圖像格式,如PNG或JPEG,來創(chuàng)建一個CGImage對象,則最簡單的方法是調(diào)用函數(shù)CGImageSourceCreateWithURL來創(chuàng)建一個圖像源,然后調(diào)用CGImageSourceCreateImageAtIndex以使用從圖像源中索引index指定的圖像數(shù)據(jù)來創(chuàng)建一個圖像。如果源圖像文件只包含一個圖像,則索引為0。如果圖像文件格式支持包含多個圖像的文件,則需要提供所需要圖像的索引值,記住起始值是0。

如果我們已經(jīng)將內(nèi)容渲染到一個位圖圖形上下文,并想要從中獲取到CGImage對象,則調(diào)用CGBitmapContextCreateImage函數(shù)。

有幾個函數(shù)可以操作已有的圖像,如拷貝、創(chuàng)建一個縮略圖,或從一個大圖像中創(chuàng)建一個圖像。不管如何創(chuàng)建一個圖像對象,我們都使用函數(shù)CGContextDrawImage將圖像繪制到一個圖形上下文中。記住CGImage是不可變的。當不再需要一個CGImage對象時,調(diào)用CGImageRelease函數(shù)來釋放它。

接下來將討論如何創(chuàng)建:

從一個已存在圖像中創(chuàng)建一個子圖像

從一個圖像圖形上下文中創(chuàng)建一個圖像

從一個大圖片中創(chuàng)建一個圖像

我們可以使用CGImageCreateWithImageInRect函數(shù)從一個大圖像中創(chuàng)建一個圖像。圖11-3演示了這一情形。

Figure 11-3 A subimage created from a larger image

函數(shù)CGImageCreateWithImageInRect返回的圖像保留了源圖像的一個引用,這意味著我們在調(diào)用完這個函數(shù)后可以釋放源圖像。

圖11-4是另外一個例子。在這種情況下,公雞的頭部被從大圖中提取出來,然后繪制到一個大于子圖像的矩形中。

代碼清單11-1顯示了創(chuàng)建并繪制子圖像的過程。CGContextDrawImage函數(shù)繪制公雞頭部的矩形區(qū)域是所提取的子圖像的四倍大小。清單中的只是一個代碼片斷。我們需要聲明合適的變量,創(chuàng)建公雞頭像,并部署公雞圖像及公雞頭部子圖像。因為只是代碼片斷,所以沒有演示如何創(chuàng)建一個圖形上下文。我們可以使用任何我們所喜歡的圖形上下文。創(chuàng)建圖形上下文的例子可以查看“圖形上下文”一章。

Figure 11-4 An image, a subimage taken from it and drawn so it’s enlarged

Listing 11-1 Code that creates a subimage and draws it enlarged

myImageArea = CGRectMake (rooster_head_x_origin, rooster_head_y_origin,

myWidth, myHeight);

mySubimage = CGImageCreateWithImageInRect (myRoosterImage, myImageArea);

myRect = CGRectMake(0,0, myWidth*2, myHeight*2);

CGContextDrawImage(context, myRect, mySubimage);

從一個位圖圖形上下文創(chuàng)建一個圖像

為了從一個已存在的位圖圖形上下文創(chuàng)建一個圖像,我們可以調(diào)用函數(shù)CGBitmapContextCreateImage,如以下:

CGImageRef myImage;

myImage = CGBitmapContextCreateImage (myBitmapContext);

這個函數(shù)返回的CGImage對象是通過一個拷貝操作創(chuàng)建的。因此我們對位圖圖形上下文所做的修改都不會影響到已返回的CGImage對象。在一些情況下,這個拷貝操作實際上沿用了copy-on-write語義,即只有當位圖圖形上下文中的數(shù)據(jù)被修改時才會去實際拷貝這些數(shù)據(jù)。我們可能需要在繪制額外數(shù)據(jù)到位圖圖形上下文之前使用結(jié)果數(shù)據(jù)或者釋放它們,以便我們可以避免實際去拷貝這些數(shù)據(jù)。

如何創(chuàng)建一個位圖圖形上下文,可以參考”創(chuàng)建圖形上下文”相關(guān)的內(nèi)容。

創(chuàng)建一個圖像遮罩

一個Quartz位圖圖像遮罩如同藝術(shù)家使用絲網(wǎng)印刷品(silkscreen)一樣。一個位圖圖像遮罩定義了如何轉(zhuǎn)換顏色,而不是使用哪些顏色。圖像遮罩中的每個采樣值指定了在特定位置中,當前填充顏色值被遮罩的數(shù)量。采樣值指定了遮罩的不透明度。值越大,表示越不透明,Quartz在指定位置繪制的顏色越少。我們可以將采樣值當成alpha值的反轉(zhuǎn)。1表示透明的,而0表示不透明。

圖像遮罩的每個分量可能是1,2,4或者8位。對于1-bit的遮罩,采樣值1指定遮罩的區(qū)域掩蓋了當前的填充顏色。值為0表示當繪制遮罩時,顯示當前的填充顏色。我們可以將1-bit遮罩當成黑色和白色;要么完全遮擋,要么完全顯示。

每個分量中有2,4,8位的圖像遮罩代表灰度值。每個分量使用以下的公式將值映射到[0, 1]之間的值:

例如,一個4-bit的遮罩其值位于[0, 1]之間,且增長的步長為1/15。0和1這兩個值分別是最小和最大值–分別表示完全遮蓋或完全透明。0和1之間的值使用(1-MaskSampleValue)這個公式來處理局部繪制。例如,如果一個8-bit遮罩的采樣值設(shè)置為0.7,則那些alpha值為(1-0.7),即0.3的顏色將會被繪制。

函數(shù)CGImageMaskCreate從我們提供的位圖圖像信息中創(chuàng)建一個Quartz圖像遮罩。我們提供的信息與創(chuàng)建圖像所提供的信息是一樣的,只是不需要提供顏色空間信息,位圖信息常量或渲染意圖,我們可以從代碼清單11-2中看到這個函數(shù)原型:

Listing 11-2 The prototype for the function CGImageMaskCreate

CGImageRefCGImageMaskCreate(

size_twidth,

size_theight,

size_tbitsPerComponent,

size_tbitsPerPixel,

size_tbytesPerRow,

CGDataProviderRef provider,

constCGFloat decode[],

boolshouldInterpolate

);

遮罩圖像

遮罩技術(shù)可以讓我們通過控制圖片的哪一部分被繪制,以生成很多有趣的效果,我們可以:

在一個圖像上使用圖像遮罩。我們也可以把一個圖像作為遮罩圖,以獲取同使用圖像遮罩相反的效果。

使用顏色來遮罩圖像的一部分,其中包含被稱為顏色遮罩的技術(shù)

將圖形上下文剪切到一個圖像或圖像遮罩,當Quartz繪制內(nèi)容到剪切的圖形上下文時來遮罩一個圖像。

使用一個圖像遮罩來遮罩圖像

函數(shù)CGImageCreateWithMask通過將圖像遮罩使用到一個圖像上的方式來創(chuàng)建一個圖像。這個函數(shù)帶有兩個參數(shù):

原始圖像,遮罩將用于其上。這個圖像不能是圖像遮罩,也不能有與之相關(guān)的遮罩顏色。

一個圖像遮罩,通過調(diào)用CGImageMaskCreate函數(shù)創(chuàng)建的。也可以提供一個圖像來替代圖像遮罩,但這將給出非常不同的結(jié)果。這將在下面描述。

一個圖像遮罩的采樣如同一個反轉(zhuǎn)的alpha值。一個圖像遮罩采樣值(S):

為1時,則不會繪制對應的圖像樣本。

為0時,則允許完全繪制對應的圖像樣本。

0和1之間的值,則讓對應的圖像樣本的alpha的值為(1-S)。

圖11-5顯示了一個由Quartz圖像創(chuàng)建函數(shù)創(chuàng)建的圖像,而圖11-6顯示了一個使用CGImageMaskCreate函數(shù)創(chuàng)建的圖像遮罩。圖11-7則顯示了一個使用CGImageCreateWithMask函數(shù)將圖像遮罩應用于一個圖像的效果。

Figure 11-5 The original image

Figure 11-6 An image mask

注意,源圖像中與遮罩黑色區(qū)域?qū)膮^(qū)域繪制出來,而與白色區(qū)域?qū)牟糠謩t沒有繪制出來。而與遮罩灰色區(qū)域?qū)膮^(qū)域則使用一個與(1-圖像遮罩采樣值)相同的alpha值來繪制。

Figure 11-7 The image that results from applying the image mask to the original image

使用一個圖像來遮罩一個圖像

我們可以使用函數(shù)CGImageCreateWithMask來用一個圖像遮罩另一個圖像,而不是使用一個圖像遮罩。我們可以使用這種方式來達到與使用圖像遮罩相反的效果。那此時我們傳遞給CGImageCreateWithMask函數(shù)的就不是一個圖像遮罩了,而是傳遞一個通過Quartz圖像創(chuàng)建函數(shù)創(chuàng)建的圖像。

用于遮罩的圖像的采樣也是操作alpha值。一個圖像采樣值(S):

為1時,則允許完全繪制對應的圖像樣本。

為0時,則不會繪制對應的圖像樣本。

0和1之間的值,則讓對應的圖像樣本的alpha的值為S。

圖11-8顯示了調(diào)用CGImageCreateWithMask函數(shù)將圖11-6中的圖像作為遮罩應用于圖11-5中的圖像上的效果。在這個例子中,我們假定圖11-6中的圖像是使用Quartz圖像創(chuàng)建函數(shù)(如CGImageCreate)創(chuàng)建的。比較圖11-8與圖11-7,可以看出使用圖像采樣時,可以獲取與使用圖像遮罩采樣相反的效果。

在圖11-8的結(jié)果圖像中,源圖像中與圖像的黑色區(qū)域?qū)膮^(qū)域沒有繪制出來。與白色區(qū)域?qū)膮^(qū)域則繪制出來了。在遮罩中與灰色區(qū)域?qū)膮^(qū)域則使用與遮罩圖像采樣值相同的alpha值來繪制。

Figure 11-8 The image that results from masking the original image with an image

使用顏色來遮罩圖像

函數(shù)CGImageCreateWithMaskingColors通過遮罩一種顏色或一個顏色范圍內(nèi)的顏色來創(chuàng)建一個圖像。使用這個函數(shù),我們可以執(zhí)行如圖11-9所示的顏色遮罩,當然也可以遮罩一個范圍內(nèi)的顏色,如圖11-11、11-12和11-13所示的效果。

函數(shù)CGImageCreateWithMaskingColors有兩個參數(shù):

一個圖像,它不能是遮罩圖像,也不能是使用過圖像遮罩或顏色遮罩的圖像。

一個顏色分量數(shù)組,指定了一個顏色或一組顏色值,以用于遮罩圖像。

Figure 11-9 Chroma key masking

顏色分量數(shù)組中元素的個數(shù)必須等于圖像所在顏色空間的顏色分量數(shù)目的兩倍。對于顏色空間中的每一個顏色分量,提供一個最小值和一個最大值來限定遮罩顏色的范圍。如果只使用一個顏色,則設(shè)置最大值等于最小值即可。顏色分量數(shù)組中的值按以下順序來提供:

{min[1], max[1], ... min[N], max[N]},其中N是分量的數(shù)目

如果圖像使用整型像素分量,則顏色分量數(shù)組中的每個值必須在[0 .. 2^bitsPerComponent - 1]范圍之內(nèi)。如果圖像使用浮點像素分量,則值可以是表示任何有效的顏色分量值的浮點數(shù)。

一個圖像采樣如果其顏色值在以下范圍內(nèi),則不會被繪制:

{c[1], ... c[N]},其中min[i] <= c[i] <= max[i] for 1 <= i <= N

圖11-10中兩只老虎的圖像使用了每個分量有8位的RGB顏色空間。為了在這個圖像上屏蔽一組顏色,我們提供一組在[0, 255]區(qū)間內(nèi)的最小和最大顏色分量值。

Figure 11-10 The original image

代碼清單11-3演示了如何設(shè)置顏色分量數(shù)組,并將其提供給CGImageCreateWithMaskingColors函數(shù)以達到圖11-11的效果。

Listing 11-3 Masking light to mid-range brown colors in an image

CGImageRef myColorMaskedImage;

constCGFloat myMaskingColors[6] = {124,255,68,222,0,165};

myColorMaskedImage = CGImageCreateWithMaskingColors (image,

myMaskingColors);

CGContextDrawImage (context, myContextRect, myColorMaskedImage);

Figure 11-11 An image with light to midrange brown colors masked out

代碼清單11-14同樣操作圖11-10并得到圖11-12的效果。這個例子遮罩了一組暗色。

Listing 11-4 Masking shades of brown to black

CGImageRef myMaskedImage;

constCGFloat myMaskingColors[6] = {0,124,0,68,0,0};

myColorMaskedImage = CGImageCreateWithMaskingColors (image,

myMaskingColors);

CGContextDrawImage (context, myContextRect, myColorMaskedImage);

Figure 11-12 A image after masking colors from dark brown to black

我們同樣可以設(shè)置一個填充顏色來作為圖像的遮罩顏色,以達到圖11-13的效果,其中被遮罩區(qū)域使用了填充顏色。代碼清單11-15演示了這一過程

Listing 11-5 Masking a range of colors and setting a fill color and

CGImageRef myMaskedImage;

constCGFloat myMaskingColors[6] = {0,124,0,68,0,0};

myColorMaskedImage = CGImageCreateWithMaskingColors (image,

myMaskingColors);

CGContextSetRGBFillColor (myContext,0.6373,0.6373,0,1);

CGContextFillRect(context, rect);

CGContextDrawImage(context, rect, myColorMaskedImage);

Figure 11-13 An image drawn after masking a range of colors and setting a fill color

通過裁減上下文來遮罩一個圖片

函數(shù)CGContextClipToMask將遮罩映射為一個矩形并將其與圖形上下文的當前裁減區(qū)域求個交集。我們提供以下參數(shù):

需要裁減的圖形上下文

要使用遮罩的矩形區(qū)域

一個圖像遮罩,其通過CGImageMaskCreate函數(shù)創(chuàng)建。我們可以使用圖像來替代圖像遮罩以達到相反的效果。但圖像必須使用Quartz圖像創(chuàng)建函數(shù)來創(chuàng)建,但不能是使用過圖像遮罩或顏色遮罩的圖像。

裁減區(qū)域的結(jié)果依賴于是否提供了一個圖像遮罩或圖像給CGContextClipToMask函數(shù)。

我們看看圖11-14.假設(shè)它是通過調(diào)用CGImageMaskCreate函數(shù)創(chuàng)建的一個圖像遮罩,然后將其作為CGContextClipToMask函數(shù)的參數(shù)。結(jié)果上下文允許繪制黑色區(qū)域,而不繪制白色區(qū)域,并使用(1-S)的alpha值來繪制灰色區(qū)域,其中S是圖像遮罩的采樣值。如果使用CGContextDrawImage函數(shù)來將一個圖像繪制到裁減上下文,則可以獲得圖11-15所示的結(jié)果。

Figure 11-14 A masking image

Figure 11-15 An image drawn to a context after clipping the content with an image mask

當遮罩圖像被當成一個圖像時,可以獲得相反的結(jié)果,如圖11-16所示:

Figure 11-16 An image drawn to a context after clipping the content with an image

在圖像中使用混合模式

此處略,類似于在顏色中使用混合模式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容