iOS繪圖指南-Core Graphics(三)-位圖圖形上下文

前言:

在第一篇文章中我們提到了位圖圖形上下文,今天我們就來(lái)詳細(xì)的介紹下位圖圖形上下文。
......第二篇指南還沒寫完,兩章一起寫的,先發(fā)這章吧......

一、概述

在上一篇中我們已經(jīng)說(shuō)過(guò)通過(guò)UIGraphicsBeginImageContextWithOptions函數(shù)創(chuàng)建位圖圖形上下文的方法,并且蘋果也建議我們使用這種方法來(lái)創(chuàng)建位圖上下文,那為什么我們還要說(shuō)另一種位圖上下文的創(chuàng)建呢?因?yàn)槠洳痪窒拊?code>drawRect里才能調(diào)用,最重要的是位圖圖形上下文對(duì)圖片的處理有著巨大的優(yōu)勢(shì),還有圖像蒙版的運(yùn)用也需要這方面的知識(shí)。
這篇文章我們通過(guò)實(shí)現(xiàn)取色器來(lái)簡(jiǎn)單的了解下位圖圖形上下文。

二、創(chuàng)建位圖圖形上下文

我們通過(guò)CGBitmapContextCreate創(chuàng)建位圖圖形上下文,此方法含有7個(gè)參數(shù),分別如下:

  • data:創(chuàng)建上下文的大小,此內(nèi)存塊的大小應(yīng)至少為(bytesPerRow* height)個(gè)字節(jié),有趣的是在iOS中我們指定NULL就可以了,系統(tǒng)會(huì)為我們自動(dòng)分配和釋放所需的內(nèi)存。
  • width:指定位圖上下文的寬度(以像素為單位)。
  • height:指定位圖上下文的高度(以像素為單位)。
  • bitsPerComponent:指定內(nèi)存中每個(gè)像素分量使用的位數(shù)。例如,對(duì)于32bpp的像素格式和RGB顏色空間,您可以指定8。
  • bytesPerRow:指定位圖中每行像素使用的內(nèi)存字節(jié)數(shù),有意思的是,當(dāng)我們指定 0 時(shí),系統(tǒng)不僅會(huì)為我們自動(dòng)計(jì)算,而且還會(huì)進(jìn)行 cache line alignment的優(yōu)化(與data形成16字節(jié)對(duì)齊,會(huì)獲得最佳性能)。
  • colorspace:用于位圖上下文的顏色空間。
  • bitmapInfo:位圖布局信息。

前面三個(gè)參數(shù)我們不多說(shuō),后面幾個(gè)參數(shù)有些同學(xué)可能一臉懵逼,下面來(lái)介紹一下這幾個(gè)參數(shù):

ColorSpace:顏色空間

Quartz中的顏色由一組值表示,沒有指示如何解釋顏色信息的顏色空間,這些值是沒有意義的,說(shuō)白了就是顏色空間就是告訴Quartz怎么解析這些值的,比如以下的四種顏色空間都表示藍(lán)色,圖摘自官方文檔:


顏色空間

如果不知道顏色空間,那么我們根本無(wú)法知道這些值所代表的顏色,如果提供錯(cuò)誤的色彩空間,則可能會(huì)出現(xiàn)相當(dāng)大的差異,如下圖所示,雖然BGR和RGB顏色空間中的綠色被解釋為相同,但紅色和藍(lán)色值會(huì)翻轉(zhuǎn)。


應(yīng)用不同顏色空間的同一張圖片

在iOS中我們常用的一般有三種:
  • kCGColorSpaceGenericGray:灰度顏色空間,范圍從絕對(duì)黑色(值0.0)到絕對(duì)白色(值1.0)的單個(gè)值。
  • kCGColorSpaceGenericRGB:最常用的RGB顏色空間,一個(gè)三分量顏色空間(紅色,綠色和藍(lán)色),用于模擬在彩色顯示器上組成單個(gè)像素的方式,RGB顏色空間的每個(gè)組件的范圍從0.0(零強(qiáng)度)到1.0(全強(qiáng)度)。
  • kCGColorSpaceGenericCMYK:CMYK顏色空間,這是一個(gè)四色組件顏色空間(青色,品紅色,黃色和黑色),用于模擬在打印過(guò)程中墨水堆積的方式,CMYK顏色空間的每個(gè)組件的范圍從0.0(不吸收顏色)到1.0(完全吸收顏色)。

Pixel Format:像素格式

大家應(yīng)該都知道位圖其實(shí)就是一個(gè)像素?cái)?shù)組(不知道的面壁思過(guò)),像素從左到右從上到下有序排列,那像素又是怎么組成的呢?像素格式就是用來(lái)描述一個(gè)像素的組成,像素格式由以下信息組成:

  • bitsPerComponent:指定內(nèi)存中每個(gè)像素分量使用的位數(shù),就是像素中每個(gè)單獨(dú)顏色的位數(shù)。
  • 每像素的位數(shù),此值必須至少為每個(gè)像素分量的位數(shù)乘以每個(gè)像素的像素分量數(shù),通過(guò)顏色空間得到。
  • bytesPerRow:指定位圖中每行像素使用的內(nèi)存字節(jié)數(shù)。

在OS中位圖上下文支持以下17種像素格式

支持的像素格式

我們可以看到iOS支持其中的八種,像素格式的更深含義請(qǐng)參考官方文檔,這里不在贅述。
其中bpp代表每像素的位數(shù),bpc也就是bitsPerComponent。這又引申出另一個(gè)概念,像素分量-就是像素中的每個(gè)單種顏色,像素分量的位數(shù)代表這個(gè)像素能表示多少種顏色,下圖可以很清晰的看出一個(gè)像素的組成:

聯(lián)想到我們通過(guò)[UIColor colorWithRed:178 / 255.0 green:233 / 255.0 blue:89 / 255.0 alpha:0];這樣的函數(shù)生成顏色對(duì)象時(shí),是不是到這里恍然大悟?因?yàn)镽GB的每個(gè)像素分量都是8位,8位能表示的顏色正好是0~255共256種顏色。
所以看到這里是不是對(duì)實(shí)現(xiàn)取色器已經(jīng)有了自己想法呢?我們可以通過(guò)Quartz取到位圖的data,然后取到當(dāng)前所指的像素的data,通過(guò)像素分量和像素分量位數(shù)就能知道當(dāng)前點(diǎn)的色值。
甚至通過(guò)對(duì)像素的控制,我們能針對(duì)性找到特定顏色的像素,從而找出特定顏色在圖片中的位置范圍,通過(guò)指南(一)中的裁剪上下文函數(shù)我們就能實(shí)現(xiàn)簡(jiǎn)單的摳圖功能,amazing!
當(dāng)然實(shí)現(xiàn)這些功能前,我們還得了解最后一個(gè)參數(shù),位圖布局信息。

位圖布局信息:

為了確保Quartz正確解釋每個(gè)像素的位,您還必須指定:

  • 位圖是否包含Alpha通道,我們知道Quartz支持RGB,CMYK和灰色空間,同事它也支持alpha(透明度),但alpha信息并不適用于所有位圖圖像格式,當(dāng)它可用時(shí),alpha分量可以位于像素的最高有效位或最低有效位中。
  • 對(duì)于具有alpha分量的位圖,顏色分量是否已經(jīng)乘以alpha值,預(yù)乘alpha表示一個(gè)源顏色已經(jīng)乘以了一個(gè)alpha值,通過(guò)消除每個(gè)顏色分量的多余乘法運(yùn)算,預(yù)乘可以加快圖像的渲染速度。
  • 樣本的數(shù)據(jù)格式 - 整數(shù)或浮點(diǎn)值。

從上圖我們可以看到位圖布局信息的作用,像素格式用來(lái)描述像素的組成,位圖布局信息則用來(lái)描述像素分量的排列順序。

三、取色器的實(shí)現(xiàn)

了解了這些我們終于可以開始正式的編碼了,我們分兩步來(lái)做

1、把要取色的圖片轉(zhuǎn)換成位圖
- (CGContextRef) createRGBABitmapContext:(CGImageRef) image{
    
    size_t imageWidth = CGImageGetWidth(image);
    size_t imageHeight = CGImageGetHeight(image);
    //使用設(shè)備顏色空間,和mac OS不同,iOS只能使用設(shè)備相關(guān)顏色空間
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    //位圖布局信息
    CGImageAlphaInfo bitmapInfo = kCGImageAlphaPremultipliedFirst;
    //創(chuàng)建位圖上下文
    CGContextRef context = CGBitmapContextCreate(NULL, imageWidth, imageHeight, 8, 0, colorSpace, bitmapInfo);
    //繪制bitmap到上下文中
    CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image);
    if (context == NULL){
        printf("Context not created!");
    }
    
    CGColorSpaceRelease(colorSpace);
    
    return context;
}
2、取出位圖中的像素?cái)?shù)據(jù)
#pragma mark - 獲取觸摸圖片的位置
- (void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch * touch = touches.anyObject;
    CGPoint currentP = [touch locationInView:self.imageView];
    
    UIColor * color = [[JMColorPicker colorPicker] pickerColorInPoint:currentP fromImage:self.imageView.image size:self.imageView.frame.size];
    self.showColorView.backgroundColor = color;
    
    const CGFloat * colorString = CGColorGetComponents(color.CGColor);
    self.colorLabel.text = [NSString stringWithFormat:@"R:%.1f\n G:%.1f\n B:%.1f\n", colorString[0] * 255, colorString[1] * 255, colorString[2] * 255];
}
#pragma mark - 從一個(gè)點(diǎn)取顏色
- (UIColor *) pickerColorInPoint:(CGPoint) point fromImage:(UIImage *) image size:(CGSize) imageViewSize{

    CGImageRef cgImage = image.CGImage;
    if (!self.context) {
        self.context = [self createRGBABitmapContext:cgImage];
    }
    
    if (self.context == NULL) {
        return nil;
    }
    
    size_t w = CGImageGetWidth(cgImage);
    size_t h = CGImageGetHeight(cgImage);
    //傳入imageView的size主要是為了得到當(dāng)前坐標(biāo)在位圖上下文上的坐標(biāo)
    CGPoint finalPoint = CGPointMake(point.x / imageViewSize.width * w, point.y / imageViewSize.height * h);
    
    UIColor * color = nil;
    unsigned char * data = CGBitmapContextGetData(self.context);
    if (data != NULL) {
        //我們選用的顏色空間為RGB,像素格式為32bpp,8bpc,別忘了每個(gè)像素占4個(gè)字節(jié),由此可以計(jì)算出當(dāng)前觸摸點(diǎn)在data數(shù)組中的位置
        int offset = 4 * ((w * round(finalPoint.y)) + round(finalPoint.x));
        int alpha =  data[offset];
        int red = data[offset + 1];
        int green = data[offset + 2];
        int blue = data[offset + 3];
        NSLog(@"offset: %i colors: RGB A %i %i %i  %i", offset, red, green, blue, alpha);
        color = [UIColor colorWithRed:(red / 255.0f) green:(green / 255.0f) blue:(blue / 255.0f) alpha:(alpha / 255.0f)];
    }
    
    return color;
}

效果圖

關(guān)于BMP圖像數(shù)據(jù)格式請(qǐng)看這篇文章 BMP圖像數(shù)據(jù)格式詳解(侵刪)

總結(jié):

清明節(jié)之前這篇文章已經(jīng)編輯了一大半,就剩最后的代碼沒上,剛才加完班回來(lái)把代碼給補(bǔ)上了,關(guān)于里面各個(gè)函數(shù)以及位圖的圖像數(shù)據(jù)格式更詳細(xì)的介紹,我會(huì)抽個(gè)時(shí)間補(bǔ)上,代碼我在整理一下也會(huì)放在GitHub上。

PS:不是我懶,是因?yàn)樽罱?xiàng)目排期很緊!?_?又要回去畫界面了...
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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