iOS-關(guān)于圖像的性能優(yōu)化方案

性能優(yōu)化是軟件開(kāi)發(fā)中必備的一個(gè)技能,,而圖像又是其中影響性能最大的一個(gè)方面.早該記錄整理一下在iOS開(kāi)發(fā)中對(duì)圖像的優(yōu)化技巧了.

使用模擬器的測(cè)試工具可以查看當(dāng)前項(xiàng)目中圖片的性能情況.

image.png
  • Color Blended Layers
    這個(gè)選項(xiàng)基于渲染程度對(duì)屏幕中的混合區(qū)域進(jìn)行綠到紅的高度(也就是多個(gè)半透明圖層的疊加)
  • Color Misaligned images
    會(huì)高亮那些被縮放或者拉伸以及沒(méi)有正確對(duì)齊到像素邊界的圖片(也就是非整型坐標(biāo))
    這些中的大多數(shù)通常會(huì)導(dǎo)致圖片的不正常縮放,如把一張大圖當(dāng)作縮略圖顯示,或者不正確地模糊圖像
  • Color Copied Images
    有時(shí)候 寄宿圖片(layer.content)的生成是由Core Animation被強(qiáng)制生成一些圖片,然后發(fā)送到渲染服務(wù)器,而不是簡(jiǎn)單的指向原始指針.
    這個(gè)選項(xiàng)把這些圖片高亮為藍(lán)色
    復(fù)制圖片對(duì)內(nèi)存和CPU來(lái)說(shuō)都是一項(xiàng)昂貴的操作,所以應(yīng)該盡可能的避免
  • Color Offscreen-Rendered(離屏渲染)
    開(kāi)啟后會(huì)把離屏渲染的圖層高亮成黃色
` 以上性能優(yōu)化中, 有效的檢測(cè) 混合模式 和 拉伸圖像 在開(kāi)發(fā)中能夠提升圖像的性能
` 離屏渲染主要用于cell的性能優(yōu)化

直入主題.開(kāi)啟Color Misaligned images 圖片變黃需要拉伸的情況.

如圖:

黃高亮代表需要拉伸.png

這個(gè)列表中cell的圖片都是從網(wǎng)絡(luò)獲取,大小不定,都放在這個(gè)大小為100 * 100的ImageView里,圖片會(huì)因?yàn)槌叽绮灰欢M(jìn)行拉伸.所以為黃色...并且快速滑動(dòng)列表的時(shí)候FPS會(huì)降低到42左右.
解決辦法呢就是,在獲取到照片之后,利用(Core Graphics)繪制一張與將要填充的UIImageView尺寸相等的圖片,然后再賦值,尺寸相等后就免去了GPU拉伸消耗

注: Core Graphics的繪制過(guò)程是完全由CPU完成的,因此也叫軟件繪圖,雖然會(huì)消耗些CPU,但是之后這些圖片出現(xiàn)在屏幕上時(shí)不會(huì)再有拉伸操作了.
// 重繪過(guò)程
extension UIImage {
  func compressImage(size: CGSize) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        let rect = CGRect(origin: CGPoint(), size: size)
        draw(in: rect)
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }
}

UIGraphicsBeginImageContextWithOptions(size, true, 0) //第二個(gè)參數(shù)是設(shè)置圖片是否是不透明的,,透明的話(huà)會(huì)有性能問(wèn)題

這個(gè)函數(shù)需要傳入一個(gè)圖片顯示時(shí)候的size..但是有時(shí)候圖片顯示的大小并不是一開(kāi)始就知道的.比如瀑布流中的圖片...這是這個(gè)優(yōu)化的局限性.
這個(gè)新繪制出來(lái)的UIImage不僅可以解決圖片拉伸的消耗問(wèn)題,還能降低內(nèi)存的占用,因?yàn)橐话闱闆r下圖片的大小是大于要顯示的尺寸大小的..如果是瀑布流中的圖片不方便確定最終大小,也可以不傳入尺寸,重繪一張圖片也會(huì)降低內(nèi)存的占用.


image.png

這里圖片的大小是固定的,在圖片下載來(lái)之后調(diào)用上述方法再賦值ImageView..再次運(yùn)行發(fā)現(xiàn)圖片沒(méi)有了黃色高亮...
但是快速滑動(dòng)之后FPS還是會(huì)降低到40左右..看來(lái)這項(xiàng)優(yōu)化對(duì)整體性能的影響還是很有限的.不過(guò)用Instruments監(jiān)測(cè)GPU的使用率發(fā)現(xiàn)比在優(yōu)化之前減少了不小.
此處FPS低還可能受Autolayout等影響.下一章再討論tableView的優(yōu)化.

開(kāi)啟Color Blended Layers 出現(xiàn)紅色高亮的情況 與高性能的切圓角

雖然使用核心繪圖進(jìn)行重繪也是一種消耗,<iOS核心動(dòng)畫(huà)高級(jí)技巧>中建議除非不得已不然不要進(jìn)行重繪,因?yàn)樗啾?Core Animation"使用CALayer效率低且差,不過(guò)它應(yīng)該是指各種動(dòng)畫(huà)效果.但是目前對(duì)于圖片的優(yōu)化來(lái)說(shuō),重繪確實(shí)可以做到對(duì)圖片的性能提高.
紅色高亮一般是因?yàn)閳D片是PNG并且有半透明的效果,像上面提到的,透明都了會(huì)增加性能的負(fù)擔(dān).
UIGraphicsBeginImageContextWithOptions(size, true, 0)
在重繪的時(shí)候第二個(gè)參數(shù)設(shè)置為true即可去掉透明效果.

不透明的圖片.png

但是原本透明的部分現(xiàn)在變成了難看的黑色,,與背景白色很違和,這不是我們想要的目的...總不能為了優(yōu)化,功能效果都變了..
因此我們需要加入一個(gè)參數(shù),把不違和的背景色填充到圖片中.

func circularImage(size: CGSize, backColor: UIColor) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        let rect = CGRect(origin: CGPoint(), size: size)
        //填充背景色
        backColor.setFill()
        UIRectFill(rect)
        
        draw(in: rect)
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }

這樣,雖然圖片已經(jīng)被優(yōu)化為非透明的圖片,但是與透明圖片的效果無(wú)差.

高效的切圓角.加邊框

貝塞爾曲線(xiàn)自帶生成圓形的路徑path..而且還有切割當(dāng)前上下文的函數(shù)

 func circularImage(size: CGSize, backColor: UIColor) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        let rect = CGRect(origin: CGPoint(), size: size)
        //填充背景色
        backColor.setFill()
        UIRectFill(rect)
        //切圓角
        let path = UIBezierPath(ovalIn: rect)
        path.addClip()
        draw(in: rect)
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }

新加這兩行代碼便可完成切圓.
再加一個(gè)邊框的話(huà)同樣是利用這個(gè)path

 func circularImage(size: CGSize, backColor: UIColor) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        let rect = CGRect(origin: CGPoint(), size: size)
        //填充背景色
        backColor.setFill()
        UIRectFill(rect)
        //切圓角
        let path = UIBezierPath(ovalIn: rect)
        path.addClip()
        draw(in: rect)
        //加圓框
        path.lineWidth = 5
        UIColor.orange.setStroke()
        path.stroke()
        
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }
切圓,加圓框后的效果.png

至此,這兩種常見(jiàn)的需要優(yōu)化的圖片情況基本解決了.因?yàn)槭诌呏挥衖Phone6,,測(cè)試了幾個(gè)demo還未發(fā)現(xiàn)因?yàn)閳D片拉伸或透明導(dǎo)致的性能問(wèn)題...一個(gè)有130張需要拉伸的高清圖片demo,可能demo條件不足,滑動(dòng)起來(lái)FPS仍然60..有機(jī)會(huì)用一個(gè)4S測(cè)試一下..

CPU VS GPU

CPU(中央處理器), GPU(圖形處理器)..在iOS設(shè)備中,都有可以運(yùn)行不同軟件的可編程芯片,總的來(lái)說(shuō),CPU所做的工作在軟件層面,GPU在硬件層面.
使用CPU可以做任何事情,,但是對(duì)于圖像處理,GPU可以使用圖像對(duì)高度并行浮點(diǎn)運(yùn)算做了優(yōu)化,所以iOS設(shè)備把 屏幕圖像的渲染交給了GPU... CPU負(fù)責(zé)計(jì)算要顯示的內(nèi)容,,提交給GPU,去渲染.

動(dòng)畫(huà)和屏幕上的組合圖層實(shí)際上被一個(gè)單獨(dú)的進(jìn)程管理,而不是你的應(yīng)用程序,這個(gè)進(jìn)程就是所謂的 渲染服務(wù)

當(dāng)圖層進(jìn)行打包發(fā)送到渲染服務(wù)之后就不是我們能掌控的事情了..但是在渲染之前,我們可以進(jìn)行一切操作來(lái)決定哪些交給CPU,哪些給GPU,以及它們要做多少事情..

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容