轉[談談 iOS 中圖片的解壓縮]

原文

圖片加載的工作流

  • 1.假設我們使用 +imageWithContentsOfFile: 方法從磁盤中加載一張圖片,這個時候的圖片并沒有解壓縮;
  • 2.然后將生成的 UIImage 賦值給 UIImageView ;
  • 3.接著一個隱式的 CATransaction 捕獲到了 UIImageView 圖層樹的變化;
  • 4.在主線程的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操作,而受圖片是否字節對齊等因素的影響,這個 copy 操作可能會涉及以下部分或全部步驟:
    a.分配內存緩沖區用于管理文件 IO 和解壓縮操作;
    b.將文件數據從磁盤讀到內存中;
    c.將壓縮的圖片數據解碼成未壓縮的位圖形式,這是一個非常耗時的 CPU 操作;
    d.最后 Core Animation 使用未壓縮的位圖數據渲染 UIImageView 的圖層

在上面的步驟中,我們提到了圖片的解壓縮是一個非常耗時的 CPU 操作,并且它默認是在主線程中執行的。那么當需要加載的圖片比較多時,就會對我們應用的響應性造成嚴重的影響,尤其是在快速滑動的列表上,這個問題會表現得更加突出。

為什么需要解壓縮

位圖:位圖就是一個像素數組,數組中的每個像素就代表著圖片中的一個點。我們應用中經常用的png和jpeg就是位圖,都是一種壓縮的位圖圖形格式。

解壓縮后的圖片大小 = 圖片的像素寬 30 * 圖片的像素高 30 * 每個像素所占的字節數 4

在磁盤中的圖片渲染到屏幕之前,必須得到原始的圖片像素數據,這就是為什么要對圖片進行解壓縮操作。

強制解壓縮的原理

/* 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);

看看 CGBitmapContextCreate
函數中每個參數所代表的具體含義:

  • data :如果不為 NULL,那么它應該指向一塊大小至少為 bytesPerRow * height 字節的內存;如果 為 NULL
    ,那么系統就會為我們自動分配和釋放所需的內存,所以一般指定 NULL 即可;
  • width 和 height:位圖的寬度和高度,分別賦值為圖片的像素寬度和像素高度即可;
  • bitsPerComponent :像素的每個顏色分量使用的 bit 數,在 RGB 顏色空間下指定 8 即可;
  • bytesPerRow:位圖的每一行使用的字節數,大小至少為 width * bytes per pixel 字節。有意思的是,當我們指定 0 時,系統不僅會為我們自動計算,而且還會進行 cache line alignment 的優化,更多信息可以查看 what is byte alignment (cache line alignment) for Core Animation? Why it matters?Why is my image’s Bytes per Row more than its Bytes per Pixel times its Width? ,親測可用;
  • space :就是我們前面提到的顏色空間,一般使用 RGB 即可;
  • bitmapInfo :就是我們前面提到的位圖的布局信息。

開源庫的實現

首先,我們來看看 YYKit 中的相關代碼,用于解壓縮圖片的函數 YYCGImageCreateDecodedCopy
存在于 YYImageCoder 類中,核心代碼如下:

CGImageRef YYCGImageCreateDecodedCopy(CGImageRef imageRef, BOOL decodeForDisplay) {
    ...

    if (decodeForDisplay) { // decode with redraw (may lose some precision)
        CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef) & kCGBitmapAlphaInfoMask;

        BOOL hasAlpha = NO;
        if (alphaInfo == kCGImageAlphaPremultipliedLast ||
            alphaInfo == kCGImageAlphaPremultipliedFirst ||
            alphaInfo == kCGImageAlphaLast ||
            alphaInfo == kCGImageAlphaFirst) {
            hasAlpha = YES;
        }

        // BGRA8888 (premultiplied) or BGRX8888
        // same as UIGraphicsBeginImageContext() and -[UIView drawRect:]
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
        bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
//使用 CGBitmapContextCreate 函數創建一個位圖上下文
        CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, YYCGColorSpaceGetDeviceRGB(), bitmapInfo);
        if (!context) return NULL;
//使用 CGContextDrawImage 函數將原始位圖繪制到上下文中;
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); // decode
//使用 CGBitmapContextCreateImage 函數創建一張新的解壓縮后的位圖
        CGImageRef newImage = CGBitmapContextCreateImage(context);
        CFRelease(context);

        return newImage;
    } else {
        ...
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,468評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內容

  • 知道了那么多關于iOS上界面渲染的理論知識后,終于可以回歸最開始的問題,將一張 png/jpg 格式的圖片渲染到頁...
    巫師學徒閱讀 735評論 0 2
  • 圖片加載的工作流 概括來說,從磁盤中加載一張圖片,并將它顯示到屏幕上,中間的主要工作流如下: 假設我們使用 +im...
    Crazy2015閱讀 1,108評論 0 3
  • 誰吃掉我們的CPU: 方法CA::Render::create_image_from_provider 圖片預解碼...
    神采飛揚_2015閱讀 3,289評論 2 6
  • 繪制像素到屏幕上 answer-huang22 Mar 2014 分享文章 一個像素是如何繪制到屏幕上去的?有很多...
    阿貍旅途T恤閱讀 1,648評論 0 7
  • 卷首語 歡迎來到 objc.io 的第三期! 這一期都是關于視圖層的。當然視圖層有很多方面,我們需要把它們縮小到幾...
    評評分分閱讀 1,793評論 0 18