一. 圖像從文件到屏幕過程

接下來我們了解一下CPU和GPU在渲染的過程中的分工是什么?
CPU(中央處理器)
1. 計算frame.
2.解壓縮圖片.
3. 將需要繪制的紋理圖片通過數(shù)據(jù)總線交給GPU.
GPU(圖形處理器)
1. 頂點的計算和變換.
2. 像素點的填充計算和紋理混合.
3. 渲染到幀緩沖區(qū).
圖片顯示到屏幕是CPU和GPU共同完成的
二. 圖片加載的工作流程
1. 假設(shè)我們是用[UIImage imageWithContensOfFile:@"xxx.png"] 方法創(chuàng)建對象來加載一張圖片, 這個時候只是創(chuàng)建一個管理圖片壓縮二進制數(shù)據(jù)的image對象, 并沒有對圖片進行解碼.
2. 把UIImage對象賦值給UIImageView, 此時一個隱式的CATransaction捕獲到的UIImageView圖形樹的變化.
3. 在線程的下一個運行循環(huán)(Runloop)到來時, Core Animation提交了這個隱式的transaction, 這個過程會對圖片進行copy操作.這個copy操作可能會涉及以下部分或全部步驟:
分配內(nèi)存緩沖區(qū)用于管理文件 IO 和解壓縮操作;
將文件數(shù)據(jù)從磁盤讀到內(nèi)存中;
將壓縮的圖片數(shù)據(jù)解碼成未壓縮的位圖形式,這是一個非常耗時的 CPU 操作;
最后Core Animation中CALayer使用未壓縮的位圖數(shù)據(jù)渲染UIImageView的圖層。
CPU計算好圖片的Frame,對圖片解壓之后.就會交給GPU來做圖片渲染.
4.渲染流程
GPU獲取到圖片的坐標.
將坐標交給頂點著色器.(頂點計算)
將圖片光柵化.(根據(jù)頂點坐標轉(zhuǎn)化成片元, 片元的每一個元素對應(yīng)幀緩沖區(qū)的一個像素點, 并且分配顏色值和深度值到各個區(qū)域, 光柵化是一個將模擬信號轉(zhuǎn)化為離散信號的過程.)
片元著色器計算.(計算屏幕上每個像素點最終顯示的顏色值)
從幀緩沖區(qū)渲染到屏幕上
我們提到了圖片解碼是一個非常好使的CPU操作, 并且默認是在主線程進行的, 所以當圖片較多時對性能會造成很大的影響. 特別是快速滑動的列表上.
三. 為什么要解壓縮圖片
既然是耗時操作, 是否可以避免解壓縮直接將圖片顯示到屏幕上呢? 答案是否定的. 要想弄明白這個問題, 我們應(yīng)該先了解一下位圖, 什么是位圖呢?
其實, 位圖是一個像素數(shù)組, 每一個元素代表圖片中的一個點. 我們在程序中使用的PNG和JPEG圖片就是位圖.
事實上,不管是 JPEG 還是 PNG 圖片,都是一種壓縮的位圖圖形格式。只不過 PNG 圖片是無損壓縮,并且支持 alpha 通道,而 JPEG 圖片則是有損壓縮,可以指定 0-100% 的壓縮比.
因此,在將磁盤中的圖片渲染到屏幕之前,必須先要得到圖片的原始像素數(shù)據(jù),才能執(zhí)行后續(xù)的繪制操作,這就是為什么需要對圖片解壓縮的原因。
四.解壓縮原理
既然圖片的解壓縮不可避免,而我們也不想讓它在主線程執(zhí)行,影響我們應(yīng)用的響應(yīng)性,那么是否有比較好的解決方案呢?
我們前面已經(jīng)提到了,當未解壓縮的圖片將要渲染到屏幕時,系統(tǒng)會在主線程對圖片進行解壓縮,而如果圖片已經(jīng)解壓縮了,系統(tǒng)就不會再對圖片進行解壓縮。因此,也就有了業(yè)內(nèi)的解決方案,在子線程提前對圖片進行強制解壓縮。
而強制解壓縮的原理就是對圖片進行重新繪制,得到一張新的解壓縮后的位圖。其中,用到的最核心的函數(shù)是 CGBitmapContextCreate:
解壓縮代碼步驟如下(YYImage實現(xiàn)):
CGBitmapInfobitmapInfo = kCGBitmapByteOrder32Host;
CGContextRef context =CGBitmapContextCreate(NULL, width, height,8,0, YYCGColorSpaceGetDeviceRGB(), bitmapInfo);//創(chuàng)建一個位圖上下文
if(!context)returnNULL;
CGContextDrawImage(context,CGRectMake(0,0, width, height), imageRef);//decode 將原始位圖繪制在上下文中
CGImageRef newImage =CGBitmapContextCreateImage(context);//創(chuàng)建一個新的解壓縮后的位圖
CFRelease(context);
它接受了一個原始位圖imageRef, 最終返回了一個新的解壓縮位圖newImage.
事實上,SDWebImage 中對圖片的解壓縮過程與上述完全一致,只是傳遞給?CGBitmapContextCreate?函數(shù)的部分參數(shù)存在細微的差別.
性能對比:
在解壓PNG圖片,SDWebImage>YYImage
在解壓JPEG圖片,SDWebImage<YYImage
結(jié)論:
1. 圖片文件只有在確認要顯示時才進行解壓縮操作, 因為是一個耗時的CPU操作, 解壓縮后會進行緩存, 不會對其重復(fù)解壓縮.
2. 圖片渲染的過程: 讀取文件 -> 計算frame ->圖片解碼 ->解碼后通過數(shù)據(jù)總線交給GPU ->GPU獲取圖片frame后進行頂點變換計算 ->光柵化 ->根據(jù)紋理坐標獲取每一個像素點的顏色值 -> 交給幀緩沖區(qū) ->渲染到屏幕上.