iOS圖片解碼以及YYImage源碼探索

1.圖片加載原理

? ? 1.磁盤上的圖片文件大小和加載到imageView上的圖片大小的關(guān)系

準(zhǔn)備好的圖片

如圖所示 當(dāng)沒有加載圖片的時(shí)候此時(shí)內(nèi)存消耗為17.8M

當(dāng)我們點(diǎn)擊Push按鈕加載圖片到imageView中時(shí)浩考,此時(shí)內(nèi)存的占用缺達(dá)到了35M木张,比未加載圖片時(shí)多出了17.2M贰镣,但是我們的圖片如圖1所示只有126KB粟判,這又是這回事呢

總結(jié):磁盤上的圖片文件大小和加載到imageView上的圖片大小沒有關(guān)系豌习。

問題:加載到imageView上的圖片大小和什么有關(guān)系呢尾膊?

回到:由兩方面決定:1:圖片的尺寸大小 2:Color Profile(P3廣色域斩松,display3瞻凤,aRGB憨攒,sRGB)

圖片加載到imageView上實(shí)際的大小 1080*1920*4(aRGB) = 8294400b具體是不是這樣的呢

通過我們的打印得到的結(jié)果和我們的猜想完全對(duì)應(yīng),由于這里img和imgView的原因圖片被加載了2次所有內(nèi)存會(huì)多出17.2M阀参,同時(shí)我們也驗(yàn)證了磁盤上的圖片文件大小和加載到imageView上的圖片大小沒有關(guān)系肝集。

同時(shí)我們也可以通過Allocations來驗(yàn)證


通過圖片是否加載前后兩次的快照的內(nèi)存增長占用情況也可以得出相同的結(jié)果

? ? 2.imageNamed:這個(gè)方法到底做了什么呢?

在圖片加載時(shí)在內(nèi)存中創(chuàng)建一個(gè)Data Buffer(磁盤圖片加載進(jìn)內(nèi)存中的一個(gè)緩沖區(qū))儲(chǔ)存圖片壓縮之后的元數(shù)據(jù)(png蛛壳,jpg等)通過解碼Decode之后在內(nèi)存中創(chuàng)建Image Buffer儲(chǔ)存圖片的像素信息杏瞻,拿到像素信息后就交給GPU,GPU經(jīng)過計(jì)算放到當(dāng)前的Frame Buffer中衙荐,最后通過硬件把當(dāng)前的Frame Buffer中的信息渲染到屏幕中

1.Data Buffer:內(nèi)存中存儲(chǔ)圖片的原始信息(jpg捞挥,png等)

2.Image Buffer:內(nèi)存中儲(chǔ)存圖片的像素信息(大小和當(dāng)前圖片大小正比 寬*高*Color Profile系數(shù))

3.Frame Buffer:顯存中(Video RAM)

解碼過程:Data Buffer ->生成Image Buffer -> 上傳給GPU -> Frame Buffer -> V-sync 每秒60/120次更新屏幕

2.圖片解碼方式

在iOS中除了通過imageNamed:這種隱式解碼還有其他的方式可以進(jìn)行解碼

1.隱式解碼

????1.imageNamed:

2.主動(dòng)解碼

? ? 1.Core Graphics

????2.ImageIO

? ? 3.CGContext


Core Graphics解碼

通過Core Graphics就把在主線程解碼的操作放到了子線程中,下面我們利用Timer Profiler驗(yàn)證下

在主線程中我們沒有發(fā)現(xiàn)有圖片解碼的操作忧吟,這跟我們預(yù)想的一樣

ImageIO解碼
CGContext解碼

相較于其他使用CGBitmapContextCreate函數(shù)解碼最高

CGBitmapContextCreate(<#void * _Nullable data#>, <#size_t width#>, <#size_t height#>, <#size_t bitsPerComponent#>, <#size_t bytesPerRow#>, <#CGColorSpaceRef? _Nullable space#>, <#uint32_t bitmapInfo#>)參數(shù)所代表的意義

data:所需要的內(nèi)存空間砌函,如果不需要操作這片內(nèi)存空間可以直接傳nil

heigh/width : 高和寬

bitsPerComponent:像素點(diǎn)RGB(8bit)

bytesPerRow:每一行使用的字節(jié)數(shù)( 4 * width)最后是64的整數(shù)倍---字節(jié)對(duì)齊

space:顏色空間

bitmapInfo:RGBA順序, 大小端的模式

3.YYImage實(shí)現(xiàn)源碼解析

YYImage.h

1.YYImage繼承自UIImage,并遵守了YYAnimatedImage協(xié)議

2.重寫了UIimage加載圖片的方法

????+ (nullable YYImage *)imageNamed:(NSString *)name; // no cache!

????+ (nullable YYImage *)imageWithContentsOfFile:(NSString *)path;

????+ (nullable YYImage *)imageWithData:(NSData *)data;

????+ (nullable YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale;

3.添加了一些屬性

? ? 1.animatedImageType:圖片類型

????2.animatedImageData:原圖片Data

? ? 3.animatedImageMemorySize:圖片占用的內(nèi)存空間(多幀圖片使用)

? ? 4.preloadAllAnimatedImageFrames:將所有幀圖像預(yù)加載到內(nèi)存讹俊。

YYImage入口函數(shù)

通過圖片的名字在Bundle中獲取到圖片的路徑并把圖片轉(zhuǎn)成NSData傳入到下個(gè)方法中

首先初始化了一個(gè)信號(hào)量垦沉,接著把接下來的所生成的對(duì)象加入到了自動(dòng)釋放池中

YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:scale];

獲取ImageSource

YYImageFrame *frame = [decoder frameAtIndex:0 decodeForDisplay:YES];

對(duì)圖片進(jìn)行手動(dòng)解碼,并生成了Image Buffer(第一幀)

如果是多幀圖計(jì)算了內(nèi)存占用量否則直接返回

1.對(duì)傳入的data判空處理

2.Image解碼器的初始化

3.手動(dòng)解碼?- (BOOL)updateData:(NSData *)data final:(BOOL)final

4.判斷解碼是否成功


這里的代碼就很簡單

1.加了一把遞歸鎖 ----- 防止 漸進(jìn)式解碼重復(fù)進(jìn)入同一線程

? ? 漸進(jìn)式解碼:

? ??CGImageSourceCreateIncremental(<#CFDictionaryRef? _Nullable options#>)

? ??CGImageSourceUpdateData(<#CGImageSourceRef? _Nonnull isrc#>, <#CFDataRef? _Nonnull data#>, <#bool final#>)

2.調(diào)用一個(gè)私有函數(shù)仍劈,返回是否獲取imageSource成功

1.YYImageType type = YYImageDetectType((__bridge CFDataRef)data);獲取圖片的格式(png厕倍,jpg等)

2.- (void)_updateSource:獲取ImageSource

根據(jù)圖片格式的不同,分別采用不同的解碼方式

1.判斷源是否為nil贩疙,如果為空創(chuàng)建輸入源 這里創(chuàng)建輸入源用了兩種方式绑青,在前文中已經(jīng)介紹過了

2.判斷圖片是否為GIF圖片

由于代碼太長沒截完整

根據(jù)前文中獲取到的圖片有多少幀進(jìn)行一個(gè)循環(huán),獲取每一幀的圖片信息

接下來我們來到關(guān)鍵的地方解碼圖片:YYImageFrame *frame = [decoder frameAtIndex:0 decodeForDisplay:YES];

這里跟前文中?- (BOOL)updateData:(NSData *)data final:(BOOL)final類似加了一把遞歸鎖屋群,這里就不做贅述闸婴,繼續(xù)往下走

1.首先判斷了圖片是否需要混合

2.調(diào)用了一個(gè)私有函數(shù)獲取到ImageRef ?CGImageSourceCreateImageAtIndex(_source, index, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(YES)});

3.調(diào)用YYCGImageCreateDecodedCopy(imageRef, YES);函數(shù)

這這里我們就看到了一些比較熟悉的函數(shù)調(diào)用,原來它也是通過調(diào)用原生底層的函數(shù)對(duì)圖片進(jìn)行解碼

這個(gè)方法里也是這樣


總結(jié):

YYImage

1.創(chuàng)建了YYImageDecoder(解碼操作)

? ? 1.獲取圖片的元數(shù)據(jù) (_updateSource)

? ? 2.獲取YYImageDecoderFrame (圖片信息)

2.frameAtIndex: decodeForDisplay:(解碼操作)線程安全芍躏,此時(shí)已經(jīng)獲取到了解碼后數(shù)據(jù)

4.YYAnimatedImageView解析

1.YYAnimatedImageView繼承自UIImageView 用于展示動(dòng)圖

2.autoPlayAnimatedImage:默認(rèn)YES 自動(dòng)播放動(dòng)圖

3.currentAnimatedImageIndex:當(dāng)前展示的圖片是動(dòng)圖的第幾幀

4.currentIsPlayingAnimation:當(dāng)前是否在播放中

5.runloopMode:當(dāng)前runloop的模式

6.maxBufferSize:最大容積

首先我們找到這個(gè)類的入口函數(shù)

1.設(shè)置了runloopMode:NSRunLoopCommonModes邪乍,為了播放動(dòng)圖時(shí)不受其他事件的影響

2.把a(bǔ)utoPlayAnimatedImage設(shè)置為YES

重寫了Image的set方法作為下一步的入口

1.停止動(dòng)畫

2.調(diào)用resetAnimated重置動(dòng)畫

3.imageChanged:圖片改變

1.詢問當(dāng)前的協(xié)議[newVisibleImage conformsToProtocol:@protocol(YYAnimatedImage)])

2.獲取當(dāng)前動(dòng)圖的下一幀

3.已經(jīng)圖片的幀

4.[self setNeedsDisplay];在下一個(gè)runloop到來時(shí)對(duì)圖層樹進(jìn)行更改

5.[self didMoved]

判斷當(dāng)前圖片是否需要自動(dòng)播放

1.創(chuàng)建一個(gè)定時(shí)器用于播放動(dòng)圖

注:為什么使用CADisplayLink而不使用NSTimer,CADisplayLink根據(jù)屏幕的刷新頻率來執(zhí)行事件对竣,而NSTimer在預(yù)先設(shè)置的時(shí)間點(diǎn)上執(zhí)行事件庇楞,CADisplayLink比NSTimer跟精準(zhǔn)

2.在后臺(tái)現(xiàn)場(chǎng)異步釋放對(duì)象的策略

1.當(dāng)緩存區(qū)命中時(shí)進(jìn)行播放圖片

2.當(dāng)未命中緩沖區(qū)時(shí),證明需要進(jìn)行解碼


總結(jié):YYAnimatedImageView

1.動(dòng)圖的播放 -> 定時(shí)器的實(shí)現(xiàn)

2.根據(jù)index讀取下一幀nextIndex

3.去緩沖區(qū)里面取數(shù)據(jù)(_timer到了當(dāng)前幀的時(shí)間)

4.setNeedsDisplay 更新視圖

5.異步子線程進(jìn)行解碼操作

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末否纬,一起剝皮案震驚了整個(gè)濱河市吕晌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌临燃,老刑警劉巖睛驳,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異膜廊,居然都是意外死亡乏沸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門爪瓜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹬跃,“玉大人,你說我怎么就攤上這事铆铆〉海” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵薄货,是天一觀的道長翁都。 經(jīng)常有香客問我,道長菲驴,這世上最難降的妖魔是什么荐吵? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任骑冗,我火速辦了婚禮赊瞬,結(jié)果婚禮上先煎,老公的妹妹穿的比我還像新娘。我一直安慰自己巧涧,他們只是感情好薯蝎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谤绳,像睡著了一般占锯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缩筛,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天消略,我揣著相機(jī)與錄音,去河邊找鬼瞎抛。 笑死艺演,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的桐臊。 我是一名探鬼主播胎撤,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼断凶!你這毒婦竟也來了伤提?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤认烁,失蹤者是張志新(化名)和其女友劉穎肿男,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體却嗡,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡次伶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稽穆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冠王。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖舌镶,靈堂內(nèi)的尸體忽然破棺而出柱彻,到底是詐尸還是另有隱情,我是刑警寧澤餐胀,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布哟楷,位于F島的核電站,受9級(jí)特大地震影響否灾,放射性物質(zhì)發(fā)生泄漏卖擅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惩阶。 院中可真熱鬧挎狸,春花似錦、人聲如沸断楷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冬筒。三九已至恐锣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舞痰,已是汗流浹背土榴。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留响牛,地道東北人鞭衩。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像娃善,于是被迫代替她去往敵國和親论衍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容