1.繪制像素到屏幕上
軟件的組成

GPU :圖形處理單元。更多圖像的繪制
CPU:圖形高迸發(fā)計(jì)算而量身定做的處理單元。更對(duì)為繪制圖像提供計(jì)算、數(shù)據(jù)。
GPU Driver: 是直接和 GPU 交流的代碼塊。不同的GPU是不同的性能怪獸,但是驅(qū)動(dòng)使他們?cè)谙乱粋€(gè)層級(jí)上顯示的更為統(tǒng)一,典型的下一層級(jí)有 OpenGL/OpenGL ES.
OpenGL(Open Graphics Library): 是一個(gè)提供了 2D 和 3D 圖形渲染的 API。GPU 是一塊非常特殊的硬件,OpenGL 和 GPU 密切的工作以提高GPU的能力,并實(shí)現(xiàn)硬件加速渲染。對(duì)大多數(shù)人來(lái)說(shuō),OpenGL 看起來(lái)非常底層,但是當(dāng)它在1992年第一次發(fā)布的時(shí)候(20多年前的事了)是第一個(gè)和圖形硬件(GPU)交流的標(biāo)準(zhǔn)化方式,這是一個(gè)重大的飛躍,程序員不再需要為每個(gè)GPU重寫(xiě)他們的應(yīng)用了。
不透明 VS 透明
當(dāng)源紋理是完全不透明的時(shí)候,目標(biāo)像素就等于源紋理。這可以省下 GPU 很大的工作量,這樣只需簡(jiǎn)單的拷貝源紋理而不需要合成所有的像素值。但是沒(méi)有方法能告訴 GPU 紋理上的像素是透明還是不透明的。只有當(dāng)你作為一名開(kāi)發(fā)者知道你放什么到 CALayer 上了。這也是為什么 CALayer 有一個(gè)叫做 opaque 的屬性了。如果這個(gè)屬性為 YES,GPU 將不會(huì)做任何合成,而是簡(jiǎn)單從這個(gè)層拷貝,不需要考慮它下方的任何東西(因?yàn)槎急凰趽踝×?。這節(jié)省了 GPU 相當(dāng)大的工作量。這也正是 Instruments 中 color blended layers 選項(xiàng)中所涉及的。(這在模擬器中的Debug菜單中也可用).它允許你看到哪一個(gè) layers(紋理) 被標(biāo)注為透明的,比如 GPU 正在為哪一個(gè) layers 做合成。合成不透明的 layers 因?yàn)樾枰俚臄?shù)學(xué)計(jì)算而更廉價(jià)。
所以如果你知道你的 layer 是不透明的,最好確定設(shè)置它的 opaque 為 YES。如果你加載一個(gè)沒(méi)有 alpha 通道的圖片,并且將它顯示在 UIImageView 上,這將會(huì)自動(dòng)發(fā)生。但是要記住如果一個(gè)圖片沒(méi)有 alpha 通道和一個(gè)圖片每個(gè)地方的 alpha 都是100%,這將會(huì)產(chǎn)生很大的不同。在后一種情況下,Core Animation 需要假定是否存在像素的 alpha 值不為100%。在 Finder 中,你可以使用 Get Info 并且檢查 More Info 部分。它將告訴你這張圖片是否擁有 alpha 通道。
像素對(duì)齊 VS 不重合在一起
到現(xiàn)在我們都在考慮像素完美重合在一起的 layers。當(dāng)所有的像素是對(duì)齊的時(shí)候我們得到相對(duì)簡(jiǎn)單的計(jì)算公式。每當(dāng) GPU 需要計(jì)算出屏幕上一個(gè)像素是什么顏色的時(shí)候,它只需要考慮在這個(gè)像素之上的所有 layer 中對(duì)應(yīng)的單個(gè)像素,并把這些像素合并到一起?;蛘?,如果最頂層的紋理是不透明的(即圖層樹(shù)的最底層),這時(shí)候 GPU 就可以簡(jiǎn)單的拷貝它的像素到屏幕上。
當(dāng)一個(gè) layer 上所有的像素和屏幕上的像素完美的對(duì)應(yīng)整齊,那這個(gè) layer 就是像素對(duì)齊的。主要有兩個(gè)原因可能會(huì)造成不對(duì)齊。第一個(gè)便是滾動(dòng);當(dāng)一個(gè)紋理上下滾動(dòng)的時(shí)候,紋理的像素便不會(huì)和屏幕的像素排列對(duì)齊。另一個(gè)原因便是當(dāng)紋理的起點(diǎn)不在一個(gè)像素的邊界上。
在這兩種情況下,GPU 需要再做額外的計(jì)算。它需要將源紋理上多個(gè)像素混合起來(lái),生成一個(gè)用來(lái)合成的值。當(dāng)所有的像素都是對(duì)齊的時(shí)候,GPU 只剩下很少的工作要做。
Core Animation 工具和模擬器有一個(gè)叫做 color misaligned images 的選項(xiàng),當(dāng)這些在你的 CALayer 實(shí)例中發(fā)生的時(shí)候,這個(gè)功能便可向你展示。
Masks
一個(gè)圖層可以有一個(gè)和它相關(guān)聯(lián)的 mask(蒙板),mask 是一個(gè)擁有 alpha 值的位圖,當(dāng)像素要和它下面包含的像素合并之前都會(huì)把 mask 應(yīng)用到圖層的像素上去。當(dāng)你要設(shè)置一個(gè)圖層的圓角半徑時(shí),你可以有效的在圖層上面設(shè)置一個(gè) mask。但是也可以指定任意一個(gè)蒙板。比如,一個(gè)字母 A 形狀的 mask。最終只有在 mask 中顯示出來(lái)的(即圖層中的部分)才會(huì)被渲染出來(lái)。
圖片格式
當(dāng)你在 iOS 或者 OS X 上處理圖片時(shí),他們大多數(shù)為 JPEG 和 PNG。讓我們更進(jìn)一步觀察。
JPEG
每個(gè)人都知道 JPEG。他是相機(jī)的產(chǎn)物。它代表這照片如何存儲(chǔ)在電腦上。甚至你嘛嘛都聽(tīng)說(shuō)過(guò) JPEG。
一個(gè)很好的理由,很多人都認(rèn)為 JPEG 文件僅是另一種像素?cái)?shù)據(jù)的格式,就像我們剛剛談到的 RGB 像素布局那樣。這樣理解離真像真是差十萬(wàn)八千里了。
將 JPEG 數(shù)據(jù)轉(zhuǎn)換成像素?cái)?shù)據(jù)是一個(gè)非常復(fù)雜的過(guò)程,你通過(guò)一個(gè)周末的計(jì)劃都不能完成,甚至是一個(gè)非常漫長(zhǎng)的周末(原文的意思好像就是為了表達(dá)這個(gè)過(guò)程非常復(fù)雜,不過(guò)老外的比喻總讓人拎不清)。對(duì)于每一個(gè)二維顏色,JPEG 使用一種基于離散余弦變換(簡(jiǎn)稱(chēng) DCT 變換)的算法,將空間信息轉(zhuǎn)變到頻域.這個(gè)信息然后被量子化,排好序,并且用一種哈夫曼編碼的變種來(lái)壓縮。很多時(shí)候,首先數(shù)據(jù)會(huì)被從 RGB 轉(zhuǎn)換到二維 YCbCr,當(dāng)解碼 JPEG 的時(shí)候,這一切都將變得可逆。
這也是為什么當(dāng)你通過(guò) JPEG 文件創(chuàng)建一個(gè) UIImage 并且繪制到屏幕上時(shí),將會(huì)有一個(gè)延時(shí),因?yàn)?CPU 這時(shí)候忙于解壓這個(gè) JPEG。如果你需要為每一個(gè) tableviewcell 解壓 JPEG,那么你的滾動(dòng)當(dāng)然不會(huì)平滑(原來(lái) tableviewcell 里面最要不要用 JPEG 的圖片)。
那究竟為什么我們還要用 JPEG 呢?答案就是 JPEG 可以非常非常好的壓縮圖片。一個(gè)通過(guò) iPhone5 拍攝的,未經(jīng)壓縮的圖片占用接近 24M。但是通過(guò)默認(rèn)壓縮設(shè)置,你的照片通常只會(huì)在 2-3M 左右。JPEG 壓縮這么好是因?yàn)樗鞘д娴?,它去除了人眼很難察覺(jué)的信息,并且這樣做可以超出像 gzip 這樣壓縮算法的限制。但這僅僅在圖片上有效的,因?yàn)?JPEG 依賴(lài)于圖片上有很多人類(lèi)不能察覺(jué)出的數(shù)據(jù)。如果你從一個(gè)基本顯示文本的網(wǎng)頁(yè)上截取一張圖,JPEG 將不會(huì)這么高效。壓縮效率將會(huì)變得低下,你甚至能看出來(lái)圖片已經(jīng)壓縮變形了。
PNG
PNG讀作”ping”。和 JPEG 相反,它的壓縮對(duì)格式是無(wú)損的。當(dāng)你將一張圖片保存為 PNG,并且打開(kāi)它(或解壓),所有的像素?cái)?shù)據(jù)會(huì)和最初一模一樣,因?yàn)檫@個(gè)限制,PNG 不能像 JPEG 一樣壓縮圖片,但是對(duì)于像程序中的原圖(如buttons,icons),它工作的非常好。更重要的是,解碼 PNG 數(shù)據(jù)比解碼 JPEG 簡(jiǎn)單的多。
在現(xiàn)實(shí)世界中,事情從來(lái)沒(méi)有那么簡(jiǎn)單,目前存在了大量不同的 PNG 格式??梢酝ㄟ^(guò)維基百科查看詳情。但是簡(jiǎn)言之,PNG 支持壓縮帶或不帶 alpha 通道的顏色像素(RGB),這也是為什么它在程序原圖中表現(xiàn)良好的另一個(gè)原因。
With –drawRect:
如果你的視圖類(lèi)實(shí)現(xiàn)了 -drawRect:,他們將像這樣工作:
當(dāng)你調(diào)用 -setNeedsDisplay,UIKit 將會(huì)在這個(gè)視圖的圖層上調(diào)用 -setNeedsDisplay。這為圖層設(shè)置了一個(gè)標(biāo)識(shí),標(biāo)記為 dirty(直譯是臟的意思,想不出用什么詞比較貼切,污染?),但還顯示原來(lái)的內(nèi)容。它實(shí)際上沒(méi)做任何工作,所以多次調(diào)用 -setNeedsDisplay并不會(huì)造成性能損失。
下面,當(dāng)渲染系統(tǒng)準(zhǔn)備好,它會(huì)調(diào)用視圖圖層的-display方法.此時(shí),圖層會(huì)裝配它的后備存儲(chǔ)。然后建立一個(gè) Core Graphics 上下文(CGContextRef),將后備存儲(chǔ)對(duì)應(yīng)內(nèi)存中的數(shù)據(jù)恢復(fù)出來(lái),繪圖會(huì)進(jìn)入對(duì)應(yīng)的內(nèi)存區(qū)域,并使用 CGContextRef 繪制。
當(dāng)你使用 UIKit 的繪制方法,例如: UIRectFill() 或者 -[UIBezierPath fill] 代替你的 -drawRect: 方法,他們將會(huì)使用這個(gè)上下文。使用方法是,UIKit 將后備存儲(chǔ)的 CGContextRef 推進(jìn)他的 graphics context stack,也就是說(shuō),它會(huì)將那個(gè)上下文設(shè)置為當(dāng)前的。因此 UIGraphicsGetCurrent() 將會(huì)返回那個(gè)對(duì)應(yīng)的上下文。既然 UIKit 使用 UIGraphicsGetCurrent() 繪制方法,繪圖將會(huì)進(jìn)入到圖層的后備存儲(chǔ)。如果你想直接使用 Core Graphics 方法,你可以自己調(diào)用 UIGraphicsGetCurrent() 得到相同的上下文,并且將這個(gè)上下文傳給 Core Graphics 方法。
從現(xiàn)在開(kāi)始,圖層的后備存儲(chǔ)將會(huì)被不斷的渲染到屏幕上。直到下次再次調(diào)用視圖的 -setNeedsDisplay ,將會(huì)依次將圖層的后備存儲(chǔ)更新到視圖上。
來(lái)自:http://objccn.io/issue-3-1/