圖片的解壓縮
最近在看 SDWebimage 的源碼自点,順便補習下圖片的解壓縮原理
我們一班看到的圖片,都是如 png jpeg 之類的圖片疚漆,都是經(jīng)過編碼的,為什么要編碼蚊惯,如果不編碼將圖片的原始信息傳輸愿卸,那么他的大小會非常非常大,不利用傳輸截型,所以經(jīng)過壓縮編碼之后趴荸,效率更加
位圖其實就是一個像素點陣圖像,包含的一個一個像素點宦焦,將這些像素點組裝起來放到屏幕上就可以顯示了发钝。
平時我們的使用
UIImageView *imageView = ...;
UIImage *image = [UIImage imageWithContentsOfFile:@"/.../.../path.JPG"];
imageView.image = image;
我們這樣取用,UIImage *image = [UIImage imageWithContentsOfFile:@"/.../.../path.JPG"];
這樣其實并沒有對圖片進行解壓縮波闹,當我們賦值給imageview 的image時候酝豪,圖片馬上要渲染到屏幕上的時候,系統(tǒng)會為我們解壓縮精堕,并且是在主線程中進行的孵淘,是非常耗cpu性能的,所以歹篓,你想瘫证,我們的tableview中揉阎,有很多圖片,每次都這樣去設置背捌,那能不卡嗎毙籽?所以現(xiàn)在用到的第三方框架,都是提前給你在子線程解碼好了毡庆,顯示的時候拿來顯示就可以了
解碼 api
/* 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. */
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);
這個接口是創(chuàng)建一個 CGContextRef 上下文坑赡,來分析下每個參數(shù)的含義
bitsPerComponent:表示每一個成分有多少位組成,Component 就是指顏色分量么抗,例如 RGB 中毅否,指定 R/G/B 這些顏色分量由多少位來表示
bytesPerPixel:表示一個像素點有多少個字節(jié)組成,上面的方法注釋中提到了一個公式 (bitsPerComponent * number of components + 7)/8蝇刀,即一個像素點的字節(jié)數(shù)量與表示當前圖像的顏色的顏色分量數(shù)量和每個分量的位數(shù)有關
bytesPerRow:圖像一行有多少字節(jié)搀突,上面注釋中也提到了,它一般是width * bytes per pixel熊泵,很好理解仰迁,也就是圖像像素寬度與每個像素字節(jié)大小的乘積。所以我們可以想到最開始我們說的那個計算位圖大小的公式顽分,只要 bytesPerRow 再乘上圖像的像素高度 height 即可徐许。
space:即顏色空間,我們平常一直說的 RGB 就是一種顏色空間卒蘸,另外還有 CMYK 也都是顏色空間雌隅,每個像素的信息必須要在一個顏色空間中才有意義。也就是說缸沃,我們通過顏色空間告訴系統(tǒng)一個像素上面的顏色信息都是什么意思恰起。顯然同樣的一個位圖每一個像素的信息,在 RGB 顏色中間中表示的意思與在 CMYK 中是不同的趾牧,最后渲染出來的圖像一定是不一樣的检盼。
我們的手機一般支持 RGB 顏色空間,其 bitsPerComponent = 8翘单。那 bytesPerPixel 是多少呢吨枉,根據(jù)上面的公式,我們首先要知道 RGB 中有多少顏色分量呢哄芜?我們可能認為是 3貌亭,因為紅、綠认臊、藍就是 3 個分量圃庭,實際上一般是4?因為 RGB 顏色空間中,其實還要包含一個 alpha 通道剧腻,也就是透明度斟薇。所以實際上應該是 ARGB 或者 RGBA,兩者的區(qū)別就是 alpha 通道在哪里表示恕酸,這個下面還會再說。當然也可能是3胯陋,那就說明沒有 alpha 通道∪镂拢現(xiàn)在就是知道了 RGB 顏色空間中,實際上是有 4 個分量遏乔,所以我們可以算出 bytesPerPixel = (8 * 4 + 7)/8 = 4B义矛。所以我們現(xiàn)在知道為什么最開始的時候我們計算位圖的大小的時候,每個像素的大小我們使用的值是 4B 了盟萨。事實上不同的顏色空間下凉翻,上面這些值都是不同的,但是一般在手機上捻激,我們使用 RGB 顏色空間制轰,所以差不多就是上面的值。
現(xiàn)在我們回到上面那個方法的參數(shù)上面:
第一個參數(shù)data胞谭,根據(jù)注釋垃杖,我們可以賦值為 NULL,這樣系統(tǒng)會自動幫我們分配和釋放相應大小的空間丈屹。
第二個參數(shù)和第三個參數(shù)即上下文的寬和高调俘,因為我們根據(jù)原始的圖片來創(chuàng)建上下文進行渲染,所以這兩個值就是原始圖片的寬和高旺垒。
第四個參數(shù)bitsPerComponent我們知道了就是每個成分由多少位組成彩库,因為我們默認實在 RGB 顏色空間中,所以一般都是傳入 8先蒋。
第五個參數(shù)bytesPerRow骇钦,這個我之前也專門介紹過了,就是像素寬度與每個像素大小的乘積竞漾,因為這些值都是知道的司忱,我們可以這么傳入。當然這里我們還可以直接傳入0畴蹭,這樣系統(tǒng)會幫我們進行計算坦仍,而且這樣做系統(tǒng)還會幫我們做一些優(yōu)化,這樣更好叨襟。
第六個參數(shù)space即顏色空間繁扎,我們可以直接使用RGB,直接使用系統(tǒng)提供的 API 獲取: CGColorSpaceCreateDeviceRGB()
第七個參數(shù)bitmapInfo表示位圖的布局信息梳玫。這個參數(shù)實際上系統(tǒng)提供了一個枚舉值
顏色空間
顏色空間是對色彩的一種描述方式爹梁,主要有6種:RGB、CMY/CMYK提澎、HSV/HSB姚垃、HSI/HSL、Lab盼忌、YUV/YCbCr积糯。
比如RGB是通過紅綠藍三原色來描述顏色的顏色空間,R=Red谦纱、G=Green看成、B=Blue。RGB顏色空間下跨嘉,一個像素由R川慌、G、B三個顏色分量表示祠乃,每個分量使用的bit 數(shù)就是bpc梦重。若每個分量用8位,那么一個像素共用24位表示亮瓷,24就是像素的深度忍饰。
最常用的就是RGB和CMYK。同一個色值在不同的顏色空間下表現(xiàn)出來是不同的顏色寺庄。
比如我們拿一個RGB格式的圖片去打印艾蓝,會發(fā)現(xiàn)打印出來的顏色和我們在電腦上面看到的有色差,這就是因為顏色空間不同導致的斗塘,因為打印機的顏色空間是CMYK赢织。
PBC
然后這個的PBC就是一個像素中每個獨立的顏色分量使用的 bit 數(shù)。
顏色分量是什么馍盟?比如RGB是通過紅綠藍三原色來描述顏色的顏色空間于置,R=Red、G=Green贞岭、B=Blue八毯,也就是紅綠藍。RGB顏色空間下瞄桨,一個像素就由R话速、G、B三個顏色分量表示芯侥,這個就是顏色分量泊交。每個分量使用的bit 數(shù)就是bpc乳讥。
如果每個分量用8位,那么一個像素共用24位表示廓俭,24就是像素的深度云石。再加上如果有透明度信息,那就是8888研乒,一共有32位也就是4個字節(jié)汹忠,就是我們前面說的iOS中每個像素所占的字節(jié)數(shù)。
BitmapInfo
然后還有BitmapInfo雹熬。BitmapInfo就是用來說明每個像素中的bits包含了哪些信息宽菜。有以下三個方面:
是否包含Alpha通道,如果包含 alpha 橄唬,那么 alpha 信息所處的位置,在像素的最低有效位参歹,比如 RGBA 仰楚,還是最高有效位,比如 ARGB 犬庇;
如果包含 alpha 僧界,那么每個顏色分量是否已經(jīng)乘以 alpha 的值,這種做法可以加速圖片的渲染時間臭挽,因為它避免了渲染時的額外乘法運算捂襟。比如,對于 RGB 顏色空間欢峰,用已經(jīng)乘以 alpha 的數(shù)據(jù)來渲染圖片葬荷,每個像素都可以避免 3 次乘法運算,紅色乘以 alpha 纽帖,綠色乘以 alpha 和藍色乘以 alpha
顏色分量是否為浮點數(shù)
iOS中宠漩,alpha通道的布局信息是一個枚舉值 CGImageAlphaInfo ,有以下幾種情況:
typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {
kCGImageAlphaNone, /* For example, RGB. */
kCGImageAlphaPremultipliedLast, /* For example, premultiplied RGBA */
kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
kCGImageAlphaLast, /* For example, non-premultiplied RGBA */
kCGImageAlphaFirst, /* For example, non-premultiplied ARGB */
kCGImageAlphaNoneSkipLast, /* For example, RBGX. */
kCGImageAlphaNoneSkipFirst, /* For example, XRGB. */
kCGImageAlphaOnly /* No color data, alpha data only */
};
kCGImageAlphaNone : 無alpha通道
kCGImageAlphaOnly:無顏色數(shù)據(jù)懊直,只有alpha通道
kCGImageAlphaNoneSkipLast扒吁、kCGImageAlphaNoneSkipFirst :有alpha通道,但是忽略了alpha值室囊,即透明度不起作用雕崩。兩者的區(qū)別是alpha通道所在的位置
kCGImageAlphaLast、kCGImageAlphaFirst:有alpha通道融撞,且alpha通道起作用盼铁,兩者的區(qū)別是alpha通道所在的位置不同
kCGImageAlphaPremultipliedLast、kCGImageAlphaPremultipliedFirst :有alpha通道尝偎,且alpha通道起作用捉貌。這兩個值的區(qū)別是alpha通道坐在的位置不同。和kCGImageAlphaLast、kCGImageAlphaFirst的區(qū)別是:帶有Premultiplied趁窃,在解壓縮的時候就將透明度乘到每個顏色分量上牧挣,這樣渲染的時候就不用再處理alpha通道,提高了渲染的效率醒陆。
根據(jù)蘋果官方文檔的介紹瀑构,如果圖片無alpha通道,則應該使用kCGImageAlphaNoneSkipFirst刨摩,如果圖片含alpha通道寺晌,則應該使用kCGImageAlphaPremultipliedFirst。
然后就是bitmapInfo澡刹。這個參數(shù)除了要指定alpha的信息外呻征,就是前面提到的ARGB還是RGBA,另外還需要指定字節(jié)順序罢浇。
字節(jié)順序分為兩種:小端模式和大端模式陆赋。它是由枚舉值 CGImageByteOrderInfo 來表示的:
typedef CF_ENUM(uint32_t, CGImageByteOrderInfo) {
kCGImageByteOrderMask = 0x7000,
kCGImageByteOrderDefault = (0 << 12),
kCGImageByteOrder16Little = (1 << 12),
kCGImageByteOrder32Little = (2 << 12),
kCGImageByteOrder16Big = (3 << 12),
kCGImageByteOrder32Big = (4 << 12)
} CG_AVAILABLE_STARTING(10.0, 2.0);
在iOS中使用的是小端模式,在macOS中使用的是大端模式嚷闭,為了兼容攒岛,使用kCGBitmapByteOrder32Host
,32位字節(jié)順序胞锰,該宏在不同的平臺上面會自動組裝換成不同的模式灾锯。32是指數(shù)據(jù)以32bit為單位(字節(jié)順序)。字節(jié)順序也以32bit為單位排序嗅榕。
#ifdef __BIG_ENDIAN__
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Big
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Big
#else /* Little endian. */
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Little
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Little
#endif