在項(xiàng)目開(kāi)發(fā)中使用 SDWebImage
來(lái)做圖片緩存加載然低,但是遇到一個(gè)問(wèn)題就是服務(wù)器的某一張圖片始終無(wú)法加載出來(lái),通過(guò)URL拿到原圖之后可以正常查看逼争。于是追根溯源定位到了 SDWebImage
中的 decodedImageWithImage
方法中,也給自己科普了一下圖像處理的相關(guān)知識(shí)。
下面先了解圖像的一些基礎(chǔ)知識(shí)冕象,然后分析遇到的問(wèn)題。
基礎(chǔ)知識(shí)
圖像可以分為矢量圖和位圖酣衷,我們通常使用的圖像為位圖格式交惯,這種格式又分為幾種顏色模型,如 RGB,CMYK 等穿仪,分別適用于不同的場(chǎng)景席爽。
我們常用的 RGB 則是通過(guò)顏色發(fā)光原理來(lái)設(shè)計(jì)的。其分為紅(R)啊片、綠(G)只锻、藍(lán)(B)三個(gè)顏色通道,每個(gè)通道的數(shù)值表示該通道的明暗程度紫谷,根據(jù)單位像素所占空間不同齐饮,其又可以分為RGB1,RGB2,RGB4,RGB8,RGB16,RGB24,RGB32等多種格式捐寥,其中RGB1、RGB2,RGB4,RGB8為調(diào)色板類(lèi)型的RGB格式祖驱,即需要通過(guò)顏色索引表來(lái)描述顏色信息握恳,RGB16為高彩色(Hi Color),RGB24為真彩色(TRUE COLOR)捺僻,RGB32則帶Aphal通道(RGBA)乡洼。
對(duì)于 RGB16,其實(shí)也是通過(guò)調(diào)色板實(shí)現(xiàn)的匕坯,可以分為 RGB444,RGB565,RGB555三種方式束昵,后面的三個(gè)數(shù)字分別表示三個(gè)通道的數(shù)據(jù)位數(shù),RGB565 表示紅(R)、藍(lán)(B)各占5位,綠(G)占三位佣盒,即單像素的表示方式為:RRRRRGGGGGGBBBBB勇劣,之所以綠色使用6位是因?yàn)槿搜蹖?duì)綠色的辨識(shí)程度比較高。而RGB555表示為 XRRRRRGGGGGBBBBB ,其中 X 表示該位不使用,由于每個(gè)顏色使用5位表示,所以每個(gè)通道最多包含32種不同亮度值榛丢,相關(guān)的索引表也就需要把亮度從明到暗均分成32份,可以通過(guò)使用屏蔽字和移位操作來(lái)得到RGB各分量的值:
#define RGB555_MASK_RED 0x7C00
#define RGB555_MASK_GREEN 0x03E0
#define RGB555_MASK_BLUE 0x001F
R = (wPixel & RGB555_MASK_RED) >> 10; // 取值范圍0-31
G = (wPixel & RGB555_MASK_GREEN) >> 5; // 取值范圍0-31
B = wPixel & RGB555_MASK_BLUE; // 取值范圍0-31
然后通過(guò) RGB 的取值在索引表中得到真實(shí)的顏色挺庞。
索引顏色是一種位圖圖像的編碼方法晰赞。當(dāng)真彩色圖片轉(zhuǎn)換為索引顏色的圖片時(shí),如果原圖顏色不在索引顏色中选侨,計(jì)算機(jī)會(huì)從索引顏色中選出一個(gè)相近的顏色來(lái)模擬該顏色(抖動(dòng)到相近的顏色)掖鱼,這也是早期瀏覽器存在 web 安全色的原因。要了解 web 安全色 的歷史援制,可以戳這里
對(duì)于 RGB24 使用24位表示一個(gè)像素戏挡,RGB分量都用8位表示,取值范圍為0-255晨仑。RGB32 則多了一個(gè)8位的 alpha 通道來(lái)表示透明度褐墅,可以使用RGBQUAD數(shù)據(jù)結(jié)構(gòu)來(lái)操作一個(gè)像素:
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 藍(lán)色分量
BYTE rgbGreen; // 綠色分量
BYTE rgbRed; // 紅色分量
BYTE rgbReserved; // 保留字節(jié)(用作Alpha通道或忽略)
} RGBQUAD。
顯然洪己,對(duì)于 RGB24 和 RGB32 妥凳,每個(gè)通道都使用8位表示,即每個(gè)通道有256中不同的色彩深度答捕。那么逝钥,如果每個(gè)通道使用16位甚至32位來(lái)表示,那就用有更多的顏色深度了拱镐。包含 32 位/通道的圖像也稱(chēng)作高動(dòng)態(tài)范圍(HDR)圖像艘款。
查看 CGBitmapContextCreate 的函數(shù)定義:
CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(void * __nullable data,
size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,
CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)
CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
Create a bitmap context. The context draws into a bitmap which is width pixels wide and height pixels high. The number of components for each pixel is specified by space, which may also specify a destination color profile. The number of bits for each component of a pixel is specified by bitsPerComponent. The number of bytes per pixel is equal to (bitsPerComponent * number of components + 7)/8. Each row of the bitmap consists of bytesPerRow bytes, which must be at least width * bytes per pixel bytes; in addition, bytesPerRow must be an integer multiple of the number of bytes per pixel. data, if non-NULL, points to a block of memory at least bytesPerRow * height bytes. If data is NULL, the data for context is allocated automatically and freed when the context is deallocated. bitmapInfo specifies whether the bitmap should contain an alpha channel and how it’s to be generated, along with whether the components are floating-point or integer.
翻譯如下:
創(chuàng)建一個(gè)位圖 context持际,位圖的像素通過(guò) width、hight 指定哗咆。每一個(gè)像素的顏色個(gè)數(shù)(number of components 通道數(shù))通過(guò) space 指定蜘欲,也可以通過(guò)一個(gè)顏色描述文件來(lái)指定。每像素中每個(gè)顏色占用的位空間(bit)通過(guò) bitsPerComponent 參數(shù)指定岳枷。每個(gè)像素的字節(jié)數(shù)(bytes per pixel)通過(guò)公式 (bitsPerComponent * number of components + 7)/8 計(jì)算(這個(gè)加7應(yīng)該是為了位對(duì)齊)芒填。位圖的每一行包含 bytesPerRow 字節(jié),其最少需要 width * bytes per pixel 字節(jié)空繁;此外,bytesPerRow 必須是 bytes per pixel 的整數(shù)倍朱庆。data 如果不為空盛泡,其指向一個(gè)至少 bytesPerRow * height 字節(jié)的內(nèi)存空間。若 data 為空娱颊,context 的 data 會(huì)隨著該 context 自動(dòng)創(chuàng)建和銷(xiāo)毀傲诵。bitmapInfo 指出該位圖是否包含 alpha 通道和它是如何產(chǎn)生的(RGB/RGBA/RGBX…),還有每個(gè)通道應(yīng)該用整數(shù)標(biāo)識(shí)還是浮點(diǎn)數(shù)箱硕。
關(guān)于 decodedImageWithImage 方法
在我們使用 UIImage 的時(shí)候拴竹,創(chuàng)建的圖片通常不會(huì)直接加載到內(nèi)存,而是在渲染的時(shí)候再進(jìn)行解壓并加載到內(nèi)存剧罩。這就會(huì)導(dǎo)致 UIImage 在渲染的時(shí)候效率上不是那么高效栓拜。為了提高效率通過(guò) decodedImageWithImage 方法把圖片提前解壓加載到內(nèi)存,這樣這張新圖片就不再需要重復(fù)解壓了惠昔,提高了渲染效率幕与。這是一種空間換時(shí)間的做法。
在UI渲染的時(shí)候镇防,實(shí)際上是把多個(gè)圖層按像素疊加計(jì)算的過(guò)程啦鸣,需要對(duì)每一個(gè)像素進(jìn)行 RGBA 的疊加計(jì)算。當(dāng)某個(gè) layer 的是不透明的来氧,也就是 opaque 為 YES 時(shí)诫给,GPU 可以直接忽略掉其下方的圖層,這就減少了很多工作量啦扬。這也是調(diào)用 CGBitmapContextCreate 時(shí) bitmapInfo 參數(shù)設(shè)置為忽略掉 alpha 通道的原因中狂。
轉(zhuǎn)發(fā)自: http://honglu.me/2016/09/02/一張圖片引發(fā)的深思/?utm_source=tuicool&utm_medium=referral