[iOS/OC]iOS10.3.3上APNG不動(dòng)

0x0背景

原本是放到自己博客的,不怎么用了,把文章同步過(guò)來(lái),原文地址[iOS/OC]iOS10.3.3上APNG不動(dòng)

問(wèn)題現(xiàn)象:有1張APNG動(dòng)圖,在其他系統(tǒng)上都OK,單單在iOS10.3.3上不動(dòng)。特別迷,花了一下午的時(shí)間排查這個(gè)問(wèn)題。最終咨詢了@DreamPiggy ,才把這個(gè)問(wèn)題搞清楚。

0x1原因

APNG的數(shù)據(jù)塊有IHDR、acTL、fcTL、IDAT、fdAT等類(lèi)型。每幀APNG的圖像由1個(gè)FCTL,>=1個(gè)(通常是1~2個(gè))IDAT圖像組成。其中FCTL是每幀的控制信息,IDAT是圖像信息。在iOS10.3.3系統(tǒng)中,當(dāng)每幀APNG的fdAT>=3時(shí),系統(tǒng)解碼會(huì)出錯(cuò),導(dǎo)致解碼失敗,并取第一幀信息作為解碼結(jié)果,即展示PNG靜圖。

簡(jiǎn)單的說(shuō),即:
iOS 10.3.3系統(tǒng)BUG,在APNG較大且較復(fù)雜時(shí),APNG解碼失敗,返回首幀PNG靜圖。

0x2APNG

分析過(guò)程中,第一步就是查詢APNG的標(biāo)準(zhǔn),發(fā)現(xiàn)這塊文檔十分匱乏,尤其是中文文檔。在此,按個(gè)人理解整理一份。

1.APNG結(jié)構(gòu)

一個(gè)流傳甚廣的圖如下。PNG的基本結(jié)構(gòu)是PNG簽名(PNG Signature)+圖像頭(IHDR)+數(shù)據(jù)塊(IDAT)+結(jié)束塊(IEND),4部分組成,而APNG則是在此基礎(chǔ)上擴(kuò)展,主要是增加了acTL控制塊保存整體動(dòng)圖控制信息,將N張圖片的IDAT取出來(lái)作為每一幀的信息,并在每一幀增加fcTL控制卡保存每幀圖像的控制信息。

[圖片上傳失敗...(image-90acc5-1537881630217)]

2.手工解碼APNG

只知道APNG的結(jié)構(gòu),當(dāng)出現(xiàn)APNG相關(guān)問(wèn)題的時(shí)候,你還是不知道是怎么回事。下面我以上面有問(wèn)題的APNG為例,手工解碼APNG。從結(jié)構(gòu)上分,APNG有PNG簽名、數(shù)據(jù)塊,兩種類(lèi)型。

1)PNG簽名

整個(gè)文件的前8個(gè)byte是PNG簽名頭,為8950 4e47 0d0a 1a0a,將這8個(gè)byte轉(zhuǎn)為ascii就是PNG

[圖片上傳失敗...(image-c96bb8-1537881630218)]

2)數(shù)據(jù)塊類(lèi)型

數(shù)據(jù)塊(chunk)常見(jiàn)類(lèi)型有:IHDR、acTL、fcTL、IDAT、fdAT、IEND

基本格式如下:

序號(hào) 描述 長(zhǎng)度(byte)
1 chunk內(nèi)容長(zhǎng)度 4
2 chunk類(lèi)型 4
3 chunk內(nèi)容 由1chunk內(nèi)容長(zhǎng)度決定
4 校驗(yàn)碼 4

其中chunk類(lèi)型將其由hex轉(zhuǎn)為ascii,即為對(duì)應(yīng)的值,如:
[圖片上傳失敗...(image-e3dd5b-1537881630218)]

①I(mǎi)HDR

長(zhǎng)度(byte) 內(nèi)容 意義
4 0000 000d chunk內(nèi)容長(zhǎng)度為13 byte
4 4948 4452 IDHR
13 0000 0465 0000 01ea 0806 0000 00 見(jiàn)下標(biāo)
4 ad34 f3f4 校驗(yàn)碼

IHDR的內(nèi)容意義如下:

描述 長(zhǎng)度(byte) 內(nèi)容
圖片寬度 4 byte 0000 0465
圖片高度 4 byte 0000 01ea
圖像深度 1 byte 8
顏色類(lèi)型 1 byte 6
壓縮方法 1 byte 0
過(guò)濾方式 1 byte 0
掃描方式 1 byte 0

②acTL

長(zhǎng)度 內(nèi)容 意義
4 byte 0000 0008 chunk內(nèi)容長(zhǎng)度為8 byte
4 byte 6163 544c acTL
8 byte 0000 000a 0000 0000 前4byte為幀數(shù),10幀;后4byte為循環(huán)次數(shù),無(wú)限循環(huán);
4 byte ad34 f3f4 校驗(yàn)碼

③fcTL

長(zhǎng)度 內(nèi)容 意義
4 byte 0000 001a chunk內(nèi)容長(zhǎng)度為26 byte
4 byte 6663 544c fcTL
26 byte 00 0000 0000 0004 6500 0001 ea00 0000 0000 0000 0000 0c00 6400 00
4 byte 50 8aec fb 校驗(yàn)碼

④IDAT

長(zhǎng)度 內(nèi)容 意義
4 byte 00 0080 00 chunk內(nèi)容長(zhǎng)度為32768 byte
4 byte 4944 4154 IDAT
32768 byte
4 byte 876e ca46 校驗(yàn)碼

3)數(shù)據(jù)塊類(lèi)型補(bǔ)充

在動(dòng)圖中,第1幀稱(chēng)為關(guān)鍵幀,其他幀信息在壓縮算法下需要有第1幀計(jì)算得來(lái)。在APNG中,關(guān)鍵幀就是IDAT,第2幀開(kāi)始為fdAT。

根據(jù)數(shù)據(jù)塊類(lèi)型可知,通常37byte開(kāi)始,為acTL數(shù)據(jù)塊,可以以此作為是否為APNG的標(biāo)識(shí)。但是這個(gè)不是強(qiáng)制的,你也可以自己定義數(shù)據(jù)塊類(lèi)型,在IHDR之后添加相應(yīng)信息。

例如,為APNG添加了簽名和時(shí)間戳后,在Safari下顯示是正常的,在Chrome下就無(wú)法正常加載。即:Chrome和Safari對(duì)于APNG的標(biāo)準(zhǔn)解讀不同,且明顯Chrome對(duì)APNG的標(biāo)準(zhǔn)支持不完善。

3.參考文章

PNG規(guī)范中文解讀:png的故事:獲取圖片信息和像素內(nèi)容

PNG標(biāo)準(zhǔn)英文文檔:PNG (Portable Network Graphics) Specification, Version 1.2
APNG標(biāo)準(zhǔn)英文文檔:APNG Specification
APNG介紹:APNG那些事

APNG分析網(wǎng)站:https://animatedpngs.com/
HEX轉(zhuǎn)ascii網(wǎng)站:https://www.rapidtables.com/convert/number/hex-to-ascii.html

0x3問(wèn)題分析

1.分析圖片數(shù)據(jù)

在有以上的對(duì)APNG的知識(shí)儲(chǔ)備后,就可以開(kāi)始進(jìn)行正式的問(wèn)題分析了。

對(duì)多組圖片手動(dòng)解碼,分析fdAT數(shù)據(jù)發(fā)現(xiàn),fdAT在同一幀中連續(xù)3次或以上,會(huì)導(dǎo)致在iOS10.3.3上解碼失敗。結(jié)論以猜測(cè)為主,部分證實(shí),暫無(wú)實(shí)錘。相關(guān)數(shù)據(jù)就不放了。有興趣的同學(xué)可以自己解析一下看看。

2.ImageIO的符號(hào)斷點(diǎn)

ImageIO有一個(gè)LogDebug函數(shù),添加符號(hào)斷點(diǎn)后,可以斷到這個(gè)符號(hào),側(cè)面印證了此時(shí)系統(tǒng)APNG解碼失敗。

0x4總結(jié)

有必要總結(jié)下。各大廠對(duì)圖片格式的解讀是不一致的,尤其是在動(dòng)圖上,在一些特定場(chǎng)景下就會(huì)踩坑。目前我已知的有:

1.本文的問(wèn)題,APNG的fdAT>=3時(shí),蘋(píng)果系統(tǒng)(iOS/mac os)解碼失敗,變?yōu)殪o圖;
2.APNG添加簽名后,Android解碼失敗,無(wú)法展示;
3.安卓和蘋(píng)果對(duì)GIF的循環(huán)次數(shù)理解不一致,通??梢栽谔O(píng)果解GIF時(shí)循環(huán)次數(shù)加1,以保持多端一致;

其他隱藏的坑不知道還有多少。

最后編輯于
?著作權(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)容