Quartz2D --> 二維繪圖引擎(五-數(shù)據(jù)管理、位圖與遮罩)

一、Quartz 2D 中的數(shù)據(jù)管理

數(shù)據(jù)管理是每個圖形應(yīng)用程序必須執(zhí)行的任務(wù)。在 Quartz2D 中數(shù)據(jù)管理涉及到為Quartz2D 提供數(shù)據(jù)和從 Quartz 2D 中獲取數(shù)據(jù)。這里主要是對于image和PDF來進(jìn)行數(shù)據(jù)管理。需要注意的是:圖像數(shù)據(jù)讀寫的首選方法是使用Image I/O框架,請參考 Image I/O Programming Guide !?。?/strong>

Quartz 可識別三種類型的數(shù)據(jù)源(data sources)和目標(biāo)(destination)。

  • URL :通過 URL 指定的數(shù)據(jù)位置可以作為數(shù)據(jù)的提供者和接收者。我們使用Core Foundation 數(shù)據(jù)類型 CFURLRef 作為參數(shù)傳遞URL給 Quartz 函數(shù)。
  • CFData:CFDataRef 和 CFMutableDataRef 。
  • Raw data;可以為數(shù)據(jù)提供一個指向任何類型數(shù)據(jù)的指針,和一組處理這些數(shù)據(jù)基本內(nèi)存管理的回調(diào)函數(shù)。
1、向 Quartz 2D傳輸數(shù)據(jù)
/* Create an image source reading from the data provider `provider'. The
 * `options' dictionary may be used to request additional creation options;
 * see the list of keys above for more information. */
CGImageSourceRef __nullable CGImageSourceCreateWithDataProvider(CGDataProviderRef __nonnull provider, CFDictionaryRef __nullable options) 

/* Create an image source reading from `data'.  The `options' dictionary
 * may be used to request additional creation options; see the list of keys
 * above for more information. */
CGImageSourceRef __nullable CGImageSourceCreateWithData(CFDataRef __nonnull data, CFDictionaryRef __nullable options) 

/* Create an image source reading from `url'. The `options' dictionary may
 * be used to request additional creation options; see the list of keys
 * above for more information. */
CGImageSourceRef __nullable CGImageSourceCreateWithURL(CFURLRef __nonnull url, CFDictionaryRef __nullable options) 

/* Create an incremental image source. No data is provided at creation
 * time; it is assumed that data will eventually be provided using
 * "CGImageSourceUpdateDataProvider" or "CGImageSourceUpdateData".  The
 * `options' dictionary may be used to request additional creation options;
 * see the list of keys above for more information. */
CGImageSourceRef __nonnull CGImageSourceCreateIncremental(CFDictionaryRef __nullable options)

#pragma  --------------------------------

/* Create a PDF document, using `provider' to obtain the document's data. */
CGPDFDocumentRef __nullable CGPDFDocumentCreateWithProvider(CGDataProviderRef cg_nullable provider)
    
/* Create a PDF document from `url'. */
CGPDFDocumentRef __nullable CGPDFDocumentCreateWithURL(CFURLRef cg_nullable url)
  
#pragma  --------------------------------

/* Create a sequential-access data provider using `callbacks' to provide the
   data. `info' is passed to each of the callback functions. */
CGDataProviderRef __nullable CGDataProviderCreateSequential(void * __nullable info, const CGDataProviderSequentialCallbacks * cg_nullable callbacks)
    
/* Create a direct-access data provider using `callbacks' to supply `size'
   bytes of data. `info' is passed to each of the callback functions.
   The underlying data must not change for the life of the data provider. */
CGDataProviderRef __nullable CGDataProviderCreateDirect(void * __nullable info, off_t size, const CGDataProviderDirectCallbacks * cg_nullable callbacks) 

/* Create a direct-access data provider using `data', an array of `size'
   bytes. `releaseData' is called when the data provider is freed, and is
   passed `info' as its first argument. */
CGDataProviderRef __nullable CGDataProviderCreateWithData(void * __nullable info, const void * cg_nullable data, size_t size, CGDataProviderReleaseDataCallback cg_nullable releaseData) 

/* Create a direct-access data provider which reads from `data'. */
CGDataProviderRef __nullable CGDataProviderCreateWithCFData(CFDataRef cg_nullable data) 

/* Create a data provider reading from `url'. */
CGDataProviderRef __nullable CGDataProviderCreateWithURL(CFURLRef cg_nullable url) 

/* Create a data provider reading from `filename'. */
CGDataProviderRef __nullable CGDataProviderCreateWithFilename(const char * cg_nullable filename) 
  • 以上方法除了CGPDFDocumentCreateWithProvider()CGPDFDocumentCreateWithURL()外,其他均返回一個圖像源(CGImageSourceRef)或者數(shù)據(jù)提供者(CGDataProviderRef)。
  • 圖像源(CGImageSourceRef)是將圖像數(shù)據(jù)移動到Quartz的首選方法。一個圖像源代表很多的圖像數(shù)據(jù),它能包含不止一個圖像,縮略圖,圖片屬性和圖像文件,當(dāng)獲取到圖像源后可以使用之完成很多工作,例如:
    • 創(chuàng)建image(CGImageRef類型):使用函數(shù)CGImageSourceCreateImageAtIndex()、CGImageSourceCreateThumbnailAtIndex(),一個CGImageRef類型代表一個單獨的Quartz圖片。
    • 為原圖像源添加內(nèi)容:使用函數(shù)CGImageSourceUpdateData()CGImageSourceUpdateDataProvider()
    • 從圖像源獲取信息。

具體可以參考ImageIO.frame中的CGImageSource.h文件!

  • 數(shù)據(jù)提供者(CGDataProviderRef)是有很多限制的比較老的機制。它們可用于獲取圖像或 PDF 數(shù)據(jù)。
2、獲取 Quartz 2D 的數(shù)據(jù)
/* Create an image destination writing to the data consumer `consumer'.
 * The parameter `type' specifies the type identifier of the resulting
 * image file.  The parameter `count' specifies number of images (not
 * including thumbnails) that the image file will contain. The `options'
 * dictionary is reserved for future use; currently, you should pass NULL
 * for this parameter. */
CGImageDestinationRef __nullable CGImageDestinationCreateWithDataConsumer(CGDataConsumerRef __nonnull consumer, CFStringRef __nonnull type, size_t count, CFDictionaryRef __nullable options)  

/* Create an image destination writing to `data'. The parameter `type'
 * specifies the type identifier of the resulting image file.  The
 * parameter `count' specifies number of images (not including thumbnails)
 * that the image file will contain. The `options' dictionary is reserved
 * for future use; currently, you should pass NULL for this parameter. */
CGImageDestinationRef __nullable CGImageDestinationCreateWithData(CFMutableDataRef __nonnull data, CFStringRef __nonnull type, size_t count, CFDictionaryRef __nullable options)

/* Create an image destination writing to `url'. The parameter `type'
 * specifies the type identifier of the resulting image file.  The
 * parameter `count' specifies number of images (not including thumbnails)
 * that the image file will contain. The `options' dictionary is reserved
 * for future use; currently, you should pass NULL for this parameter.
 * Note that if `url' already exists, it will be overwritten. */
CGImageDestinationRef __nullable CGImageDestinationCreateWithURL(CFURLRef __nonnull url, CFStringRef __nonnull type, size_t count, CFDictionaryRef __nullable options)

#pragma --------------------------------

/* Create a PDF context for writing to `url'. This function behaves in the
   same manner as the above function, except that the output data will be
   written to `url'. */
CGContextRef __nullable CGPDFContextCreateWithURL(CFURLRef cg_nullable url, const CGRect * __nullable mediaBox, CFDictionaryRef __nullable auxiliaryInfo)

#pragma --------------------------------

/* Create a data consumer using `callbacks' to handle the data. `info' is
   passed to each of the callback functions. */
CGDataConsumerRef __nullable CGDataConsumerCreate(void * __nullable info, const CGDataConsumerCallbacks * cg_nullable cbks)

/* Create a data consumer which writes data to `url'. */
CGDataConsumerRef __nullable CGDataConsumerCreateWithURL(CFURLRef cg_nullable url)

/* Create a data consumer which writes to `data'. */
CGDataConsumerRef __nullable CGDataConsumerCreateWithCFData(CFMutableDataRef cg_nullable data)
  • 以上方法除了CGPDFContextCreateWithURL ()外,其他均返回一個圖像目標(biāo)(CGImageDestinationRef)或者數(shù)據(jù)消費者(CGDataComsumerRef)。
  • 圖像目標(biāo)(CGImageDestinationRef)是將圖像數(shù)據(jù)移出Quartz的首選方法,和圖片源相似,一個圖像目標(biāo)代表很多的圖像數(shù)據(jù),它能包含不止一個圖像,縮略圖,圖片屬性和圖像文件,當(dāng)獲取到圖像目標(biāo)(CGImageDestinationRef)后可以使用之完成很多工作,例如:
    • 添加圖片(CGImageRef)到圖像目標(biāo)中:使用函數(shù)CGImageDestinationAddImage()、CGImageDestinationAddImageFromSource()、CGImageDestinationAddImageAndMetadata()。
    • 設(shè)置屬性:使用函數(shù)CGImageDestinationSetProperties()。
    • 從圖像目標(biāo)獲取信息。

具體可以參考ImageIO.frame中的CGImageDestination.h文件!

二、Bitmap Images and Image Masks(位圖和圖像遮罩)

首先開始就先強調(diào)一下,這節(jié) 非常重要!!! 位圖與圖像遮罩在 Quartz 中都是用 CGImageRef 數(shù)據(jù)類型來表示。

1、About Bitmap Images and Image Masks

位圖(A bitmap image)是一個像素數(shù)組,每一個像素在圖像中代表一個獨立的點,JPEG, TIFF, PNG等文件都是位圖,程序的icon也是位圖,位圖必須是矩形形狀但是可以通過設(shè)置alpha(透明度)使之呈現(xiàn)各種形狀,同樣可以進(jìn)行旋轉(zhuǎn)、剪切等操作。
?位圖中的每一個采樣包含特定顏色空間下的一個或更多顏色組件,和一個額外的用于指定 alpha 值以表示透明度的組件。每個組件的bpc是1到32位的。
?Quartz同樣支持圖像遮罩。一個圖像遮罩也是一個位圖,它指定了一個沒有顏色的繪制區(qū)域。一個顏色遮罩可以有 1 - 8 位的深度(bpp)。
?關(guān)于bpc、bpp可以參考Quartz2D --> 二維繪圖引擎(二 - 路徑、顏色、形變)中關(guān)于顏色部分的最后一節(jié)。

2、Bitmap Image Information(位圖信息)

Quartz提供了很多圖像格式并內(nèi)建了多種常用的格式。在 iOS 中,這些格式包括 JPEG,GIF,PNG,TIF,ICO,GMP,XBM 和 CUR。其它的位圖格式或?qū)S懈袷叫枰覀冎付▓D像格式的詳細(xì)信息,以便 Quartz 能正確地解析圖像。

(1、)當(dāng)Quartz創(chuàng)建一個位圖時可能會用到以下信息:
  • A bitmap data source:位圖數(shù)據(jù)源 --- 可以是一個 Quartz 數(shù)據(jù)提供者(provider)或者是一個 Quartz 圖像源(source)

  • An optional decode array:一個可選的解碼數(shù)組 --- 解碼數(shù)組包含每個顏色組件的 一對數(shù)值 用來將圖像顏色值線性映射到其它指定顏色空間的顏色值。這對一個圖像做去飽和效果處理或者反轉(zhuǎn)顏色值等非常有用。例如:在 RGB 顏色空間中的一個圖像的解碼數(shù)組包含 6 個數(shù)值,每一組分別對應(yīng)紅、綠、藍(lán)顏色組件。

  • An interpolation setting:插值設(shè)置 --- 這是一個布爾值,用于指定Quartz是否應(yīng)用插值算法調(diào)整圖像。

  • A rendering intent :渲染意圖 --- 詳細(xì)請參考Quartz2D --> 二維繪圖引擎(二 - 路徑、顏色、形變)中關(guān)于顏色部分的第四節(jié)。

  • The image dimensions : 圖像尺寸。

  • The pixel format : 像素格式 --- 包括:

    • Bits per component :即在一個像素中每個獨立顏色組件的位數(shù)。對于一個圖像遮罩,這個值源在源像素中屏蔽位的數(shù)目。例如,如果源圖片是 8-bit 的遮罩,則指定每個組件均是 8 位。

    • Bits per pixel:源像素的位的總數(shù),該數(shù)目是每個像素的組件數(shù) 乘以 Bits per component。例如 在RGBA顏色空間中,bitsPerComponent為8,則bitsPerPixel為32。

    • Bytes per row:圖像上一行的字節(jié)數(shù)。即bitsPerPixel 乘以 圖像的像素寬度。

  • Color Spaces and Bitmap Layout : 顏色空間和位圖布局 --- 描述了使用alpha 的位置和位圖是否使用浮點值。圖像遮罩不需要這個信息。為了確保Quartz正確解譯每個像素的二進(jìn)制位,必須指定一下幾點:

    • 位圖是否包含一個alpha通道:Quartz 支持 RGB,CMYK 和灰度顏色空間。同時支持 alpha或transparency,雖然并不是所有位圖圖像格式都支持 alpha 通道。但是當(dāng)alpha 通道可用時,alpha 組件可以位于像素的高位或低位位置上。

    • 對于存在alpha的位圖,顏色組件是否已經(jīng)乘以了alpha的值:預(yù)乘 alpha(Premultiplied alpha)描述源顏色已將顏色組件乘以了 alpha 值。這種預(yù)處理通過消除每個顏色組件額外的乘法運算來加速圖片的渲染。例如:在RGB顏色空間中,對于圖像中的每一個像素來說,在渲染圖片時使用預(yù)乘 alpha可以消除三個乘法操作 --- 紅色乘以α,綠色乘以α,藍(lán)色乘以α。

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

(2、)當(dāng)我們使用函數(shù)CGImageCreate()創(chuàng)建一個圖像時,需要提供一個CGImageBitmapInfo類型的參數(shù) --- bitmapInfo ,用于指定位圖布局信息。該類型是一個枚舉值:
typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {
    kCGBitmapAlphaInfoMask = 0x1F,

    kCGBitmapFloatInfoMask = 0xF00,
    kCGBitmapFloatComponents = (1 << 8),

    kCGBitmapByteOrderMask     = kCGImageByteOrderMask,
    kCGBitmapByteOrderDefault  = (0 << 12),
    kCGBitmapByteOrder16Little = kCGImageByteOrder16Little,
    kCGBitmapByteOrder32Little = kCGImageByteOrder32Little,
    kCGBitmapByteOrder16Big    = kCGImageByteOrder16Big,
    kCGBitmapByteOrder32Big    = kCGImageByteOrder32Big
} CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

它主要提供了三個方面的布局信息:

  • alpha 的信息;
  • 顏色組件是否為浮點數(shù);
  • 像素格式的字節(jié)順序。

I、其中關(guān)于alpha的信息主要會使用下面的枚舉定義:

typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {
    kCGImageAlphaNone,               /* For example, RGB. */ 相當(dāng)于kCGImageAlphaNoneSkipLast
    kCGImageAlphaPremultipliedLast,  /* For example, premultiplied RGBA */ alpha存在,在最低位,已預(yù)乘
    kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */ alpha存在,在最高位,已預(yù)乘
    kCGImageAlphaLast,               /* For example, non-premultiplied RGBA */ alpha存在,在最低位,無預(yù)乘
    kCGImageAlphaFirst,              /* For example, non-premultiplied ARGB */  alpha存在,在最高位,無預(yù)乘
    kCGImageAlphaNoneSkipLast,       /* For example, RBGX. */ 無alpha通道,如果總的像素大小超過了顏色空間中顏色組件的數(shù)量的需求空間,就會忽略最低有效位。
    kCGImageAlphaNoneSkipFirst,      /* For example, XRGB. */ 無alpha通道,如果總的像素大小超過了顏色空間中顏色組件的數(shù)量的需求空間,就會忽略最高有效位。
    kCGImageAlphaOnly                /* No color data, alpha data only */
};

它同樣也提供了三個方面的 alpha 信息:

  • 是否包含 alpha ;
  • 如果包含 alpha ,那么 alpha 信息所處的位置,在像素的最低有效位,比如 RGBA ,還是最高有效位,比如 ARGB ;
  • 如果包含 alpha ,那么每個顏色分量是否已經(jīng)乘以 alpha 的值,這種做法可以加速圖片的渲染時間,因為它避免了渲染時的額外乘法運算。

II、關(guān)于顏色組件是否為浮點型
?我們使用常量 kCGBitmapFloatComponents來標(biāo)識一個位圖格式使用浮點值。對于浮點格式,我們將這個常量與上述描述的合適的常量進(jìn)行按位與操作來組成 bitmapInfo 的參數(shù)例:

kCGImageAlphaPremultipliedLast | kCGBitmapFloatComponents

III、關(guān)于像素格式的字節(jié)順序,主要會使用下面的枚舉定義:

typedef CF_ENUM(uint32_t, CGImageByteOrderInfo) {
    kCGImageByteOrderMask     = 0x7000,
    kCGImageByteOrder16Little = (1 << 12),
    kCGImageByteOrder32Little = (2 << 12),
    kCGImageByteOrder16Big    = (3 << 12),
    kCGImageByteOrder32Big    = (4 << 12)
} CG_AVAILABLE_STARTING(__MAC_10_12, __IPHONE_10_0);

但是,在CGBitmapInfo枚舉中可以看到,CGBitmapInfo是完全兼容CGImageByteOrderInfo的,所以可以直接使用CGBitmapInfo的常量。它主要提供了兩個方面的字節(jié)順序信息:

  • 小端模式還是大端模式,詳情請參考這里。
  • 數(shù)據(jù)以 16 位還是 32 位為單位。

對于 iPhone 來說,采用的是小端模式,但是為了保證應(yīng)用的向后兼容性,我們可以使用系統(tǒng)提供的宏:

#ifdef __BIG_ENDIAN__
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Big
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Big
#else    /* Little endian. */
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Little
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Little
#endif

在這里我們最常用的一般是 --- kCGBitmapByteOrder32Host。

(3、)一些關(guān)于不同參數(shù)的不同位圖布局。
3、Creating Images

下表中(表一)羅列了常用創(chuàng)建CGImage對象的函數(shù),及關(guān)于函數(shù)的描述。圖像創(chuàng)建函數(shù)的選擇取決于圖像數(shù)據(jù)的來源。

函數(shù) 描述
CGImageCreate() 最靈活的函數(shù),它可以從任何類型的位圖數(shù)據(jù)來創(chuàng)建圖像。但是它也是最復(fù)雜的函數(shù)使用,因為你必須指定所有位圖信息。要使用這個功能,您需要熟悉所有的位圖圖像信息
CGImageCreateCopy() 創(chuàng)建圖像的副本,只復(fù)制圖像結(jié)構(gòu)本身不復(fù)制基礎(chǔ)數(shù)據(jù)
CGImageCreateWithImageInRect() 根據(jù)提供的rect截取原始圖片的一部分生成新的圖片
CGImageCreateWithMask() 基于mask截取原始圖片的一部分生成新的圖片
CGImageCreateCopyWithColorSpace() 使用新的顏色空間去取代圖片原始的顏色空間。如果參數(shù)image是一個遮罩或者顏色空間組件數(shù)量不一致則會返回NULL
*** ***
CGBitmapContextCreateImage() 根據(jù)上下文生成圖片,如果該上下文不是位圖上下文或者其他原因無法創(chuàng)建圖像,則返回NULL。這是一個“復(fù)制”操作——后續(xù)對上下文的更改不會影響返回的圖像內(nèi)容。
*** ***
CGImageSourceCreateImageAtIndex() 根據(jù)圖像源創(chuàng)建一個圖像。圖像來源可以包含多個圖像。
CGImageSourceCreateThumbnailAtIndex() 創(chuàng)建與圖像源中指定圖片相關(guān)聯(lián)的縮略圖
  • 如果想從一個使用標(biāo)準(zhǔn)的圖像格式如PNG和JPEG的圖像文件中創(chuàng)建一個CGImage對象,最簡單的解決方案是:調(diào)用函數(shù)CGImageSourceCreateWithURL()來創(chuàng)建一個圖像源,然后調(diào)用函數(shù)CGImageSourceCreateImageAtIndex()從含有一個特定索引的圖像源的圖像數(shù)據(jù)中創(chuàng)建圖像。如果原始圖像文件只包含一個圖像,提供0作為索引。如果圖像包含多個圖像,需要提供索引以得到適當(dāng)?shù)膱D像。記住,索引值從0開始。
  • 不管如何創(chuàng)建一個CGImage對象,都可以使用函數(shù)CGContextDrawImage()畫圖形上下文的圖像。CGImage對象是不可變的,當(dāng)不再需要一個CGImage對象,通過調(diào)用函數(shù)CGImageRelease()釋放它。

(1)Creating an Image From Part of a Larger Image
?主要是使用CGImageCreateWithImageInRect()這一函數(shù)去截取已知圖片上的一部分。
e.g:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    UIImage * image = [UIImage imageNamed:@"poem"];
    [image drawInRect:self.bounds];
    CGImageRef newImage = CGImageCreateWithImageInRect(image.CGImage, CGRectMake(375, 130, 285, 520));
//    UIImage * newUIImage = [UIImage imageWithCGImage:newImage];
//    [newUIImage drawInRect:CGRectMake(50, 70, 100, 150)];
    
    CGContextSaveGState(currentContext);
    //調(diào)整坐標(biāo)系
    CGContextTranslateCTM(currentContext, 0.0, self.bounds.size.height);
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    CGContextDrawImage(currentContext, CGRectMake(50, self.bounds.size.height-70-150, 100, 150), newImage);
    CGContextRestoreGState(currentContext);
    CGImageRelease(newImage);
}

上面注釋部分代碼與下方的是等價的,得到的結(jié)果相同?。?!

效果圖

(2)Creating an Image from a Bitmap Graphics Context
?主要是使用CGBitmapContextCreateImage()這一函數(shù)去獲取上下文生成圖片。
e.g:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    UIImage * image = [UIImage imageNamed:@"poem"];
    [image drawInRect:self.bounds];
    
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 1.0);
    
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(currentContext, 0.0, self.bounds.size.height);
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    CGContextDrawImage(currentContext, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height), image.CGImage);
//    UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRef imageRef = CGBitmapContextCreateImage(currentContext);
    UIImage * newImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    UIGraphicsEndImageContext();
    
    UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 70, 150, 250)];
    imageView.image = newImage;
    imageView.layer.borderWidth = 1.0;
    imageView.layer.borderColor = [UIColor blackColor].CGColor;
    [self addSubview:imageView];
}
效果圖
4、Creating an Image Mask

位圖圖像遮罩決定了如何轉(zhuǎn)換顏色,而不是使用哪些顏色。圖像遮罩中的每個采樣值指定了在特定位置中,當(dāng)前填充顏色值被遮罩的數(shù)量。采樣值指定了遮罩的不透明程度。值越大,表示越不透明,Quartz 在指定位置繪制的顏色越少。1是透明,0是不透明。
?圖像遮罩的每個組件可能是 1/2/4/8 位。1bit 的遮罩,要么完全遮擋,要么完全顯示。2/4/8bit 的遮罩代表灰度值,每個組件使用以下的公式值映射到 [0, 1] 之間。

公式

創(chuàng)建位圖遮罩一般使用函數(shù):

CG_EXTERN CGImageRef __nullable CGImageMaskCreate(size_t width, size_t height,
    size_t bitsPerComponent, size_t bitsPerPixel, size_t bytesPerRow,
    CGDataProviderRef cg_nullable provider, const CGFloat * __nullable decode,
    bool shouldInterpolate)

5、Masking Images(使用遮罩的圖像)

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

  • 對圖像使用圖像遮罩(image mask)。我們也可以把圖像(image)作為遮罩圖,以獲取同使用圖像遮罩相反的效果。
  • 使用顏色來遮罩部分圖像。其中包含被稱為色鍵遮罩的技術(shù)。
  • 當(dāng)Quartz在被剪切的上下文上繪制時,對圖像或者圖像遮罩剪切上下文以有效的屏蔽圖像。

(1)Masking an Image with an Image Mask - 使用圖像遮罩掩蔽圖像
?通過使用函數(shù)CGImageRef __nullable CGImageCreateWithMask(CGImageRef cg_nullable image, CGImageRef cg_nullable mask)返回一個應(yīng)用了圖像遮罩的圖像,該函數(shù)含有兩個參數(shù):

  • image:想要加遮罩的圖像,該圖像不能是一個遮罩圖像( image mask)或者存在有與之關(guān)聯(lián)的遮罩顏色。
  • mask:通過調(diào)用函數(shù)CGImageMaskCreate ()來創(chuàng)建一個圖像遮罩。也可以通過提供一個圖像來代替圖像遮罩,但是這將獲得同使用圖像遮罩相反的效果。

一個圖像遮罩的源樣品充當(dāng)一個相反的的透明度值;圖像遮罩采樣值 S = 1 時,則不會繪制對應(yīng)的圖像樣本。S = 0 時,則允許完全繪制對應(yīng)的圖像樣本。S = 0 ~ 1 時,則讓對應(yīng)的圖像樣本的 alpha 的值為(1-S)。

示例:

想要添加mask的image
由于沒有合適的圖片自己生成的一張maskImage

示例代碼:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    UIImage * image = [UIImage imageNamed:@"loli"];
    // 生成能湊合著用的maskImage
    UIImage * maskImage = [UIImage imageNamed:@"poem"];
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 1.0);
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(currentContext, 0.0, self.bounds.size.height);
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    CGContextDrawImage(currentContext, CGRectMake(0, self.bounds.size.height-240, 120, 240), maskImage.CGImage);
    UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRef newImageRef = newImage.CGImage;
//    CGImageRef newImageRef = CGBitmapContextCreateImage(currentContext);
    UIGraphicsEndImageContext();
    CGImageRef imageMask = CGImageMaskCreate(CGImageGetWidth(newImageRef), CGImageGetHeight(newImageRef), CGImageGetBitsPerComponent(newImageRef), CGImageGetBitsPerPixel(newImageRef), CGImageGetBytesPerRow(newImageRef), CGImageGetDataProvider(newImageRef), NULL, NO);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    // 對無alpha通道的圖片添加alpha通道
    CGImageRef sourceImageRef = image.CGImage;
    CGImageRef imageWithAlphaRef = sourceImageRef;
    if (CGImageGetAlphaInfo(sourceImageRef) == kCGImageAlphaNone | CGImageGetAlphaInfo(sourceImageRef) == kCGImageAlphaNoneSkipLast | CGImageGetAlphaInfo(sourceImageRef) == kCGImageAlphaNoneSkipFirst) {
        CGContextRef bitmapContext = CGBitmapContextCreate(NULL, CGImageGetWidth(sourceImageRef), CGImageGetHeight(sourceImageRef), CGImageGetBitsPerComponent(sourceImageRef), CGImageGetBytesPerRow(sourceImageRef), colorSpace, kCGImageAlphaPremultipliedLast);
        if (bitmapContext != NULL) {
            CGContextDrawImage(bitmapContext, CGRectMake(0, 0, CGImageGetWidth(sourceImageRef), CGImageGetHeight(sourceImageRef)), sourceImageRef);
            imageWithAlphaRef = CGBitmapContextCreateImage(bitmapContext);
        }
        CGColorSpaceRelease(colorSpace);
        CGContextRelease(bitmapContext);
    }
    // 生成maskedImage
    CGImageRef maskedImage = CGImageCreateWithMask(imageWithAlphaRef, imageMask);
    UIImage * resultImage = [UIImage imageWithCGImage:maskedImage];
    CGImageRelease(maskedImage);
    
    // 顯示
    UIImageView * imageView = [[UIImageView alloc] initWithFrame:self.bounds];
    imageView.image = resultImage;
    imageView.layer.borderWidth = 1.0;
    imageView.layer.borderColor = [UIColor blackColor].CGColor;
    [self addSubview:imageView];
}
效果圖

因為沒有合適的圖片,所以就成了這么low的樣子~~湊合著看吧??????

(2)Masking an Image with an Image - 使用圖像充當(dāng)遮罩
?即CGImageCreateWithMask(CGImageRef cg_nullable image, CGImageRef cg_nullable mask)的參數(shù)中mask不是由函數(shù)CGImageMaskCreate()生成,而是CGImageCreate()或者其他image生成函數(shù)生成的。會取得與使用圖像遮罩相反的效果。

(3)Masking an Image with Color - 使用顏色來遮罩圖像
?通過使用函數(shù)CGImageCreateWithMaskingColors(CGImageRef cg_nullable image, const CGFloat * cg_nullable components)給圖像添加顏色遮罩。這里要特別注意參數(shù)components!

  • 是一個含有2N個元素的字符數(shù)組,N是所在顏色空間的組件數(shù)量。
  • 每兩個數(shù)規(guī)定了一個顏色組件的范圍。如果image有整型元素組件則每個組件必須在范圍[1 - (2^bitsPerComponent-1)]內(nèi),若是浮點型組件則必須是有效的元素組件值。

示例代碼:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    
    UIImage * image = [UIImage imageNamed:@"loli"];
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 1.0);
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    const CGFloat myMaskingColors[6] = {0, 55,  0, 55, 0, 55};
    CGImageRef maskedImage = CGImageCreateWithMaskingColors(image.CGImage, myMaskingColors);
    CGContextSetRGBFillColor (currentContext, 1.0, 0, 0, 1);
    CGContextFillRect(currentContext, self.bounds);
    CGContextTranslateCTM(currentContext, 0.0, self.bounds.size.height);
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    CGContextDrawImage(currentContext, self.bounds, maskedImage);
    UIImage * resultImage = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRelease(maskedImage);
    UIGraphicsEndImageContext();
    // 顯示
    UIImageView * imageView = [[UIImageView alloc] initWithFrame:self.bounds];
    imageView.image = resultImage;
    imageView.layer.borderWidth = 1.0;
    imageView.layer.borderColor = [UIColor blackColor].CGColor;
    [self addSubview:imageView];
}
效果圖

(4)Masking an Image by Clipping the Context - 通過剪切上下文進(jìn)行遮罩的設(shè)置
?通過函數(shù)CGContextClipToMask(CGContextRef cg_nullable c, CGRect rect, CGImageRef cg_nullable mask)將遮罩映射到一個矩形與上下文相交的剪切區(qū)域。關(guān)于參數(shù)mask與上面遮罩使用情況大概相似,既可以是imageMask也可以是image,但是值得注意的是如果mask是image那么它必須是 設(shè)備灰度顏色空間(DeviceGray color space)!!!

示例代碼:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    UIImage * image = [UIImage imageNamed:@"loli"];
    
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 1.0);
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    [[UIColor whiteColor] setFill];
    UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(150, 200, 150, 220)cornerRadius:10];
    CGContextAddPath(currentContext, path.CGPath);
    CGContextDrawPath(currentContext, kCGPathFill);
    CGImageRef newImageRef = CGBitmapContextCreateImage(currentContext);
    UIGraphicsEndImageContext();
    CGImageRef imageMask = CGImageMaskCreate(CGImageGetWidth(newImageRef), CGImageGetHeight(newImageRef), CGImageGetBitsPerComponent(newImageRef), CGImageGetBitsPerPixel(newImageRef), CGImageGetBytesPerRow(newImageRef), CGImageGetDataProvider(newImageRef), NULL, NO);
//    CGImageRef maskImage = CGImageCreateCopy(newImageRef);
    CGImageRelease(newImageRef);
    
    CGContextRef currentContext_next = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(currentContext_next, 0.0, self.bounds.size.height);
    CGContextScaleCTM(currentContext_next, 1.0, -1.0);
    CGContextClipToMask(currentContext_next, self.bounds, imageMask);
    CGContextDrawImage(currentContext_next, self.bounds, image.CGImage);
}
遮罩圖樣式
使用imageMask作為mask的效果圖
使用image作為mask的效果圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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