为什么80%的码农都做不了架构师?>>>
1. 问题描述
在Android Studio 1.2.2下编译期间,出现了下面警告信息:
...\res\drawable-hdpi\add_green.png: libpng warning: iCCP: Not recognizing known sRGB profile that has been edited
baidu和google,有一些网友是非png格式的图片(例如jpg格式等)而错误地采用了png为后缀,也会出现上述告警信息,可参见[7],本文不考虑这些情况。
其他网友的回答基本上都是:原因是新版本的libpng对关于iCCP采用了更严苛的约束。但是是从哪个libpng版本开始严格检查,主要是检查哪些内容导致的告警信息呢?基本上没有看到答案。
本文先学习下PNG文件格式,然后了解下libpng, 再来分析和解决这个警告信息。
2. PNG文件格式
[2]是WWW PNG的规范,[3]是通过例子来介绍PNG文件格式中文编写,下面材料主要来自于这两份文档。
每个PNG文件是由一个PNG标识(signature),后面跟一些数据块(chunk)组成,每个chunk由一个chunk类型来标识其功能。
2.1 PNG标识(signature)
每个PNG文件的前8个字节总是包含以下值:
十进制 137 80 78 71 13 10 26 10
十六进制 89 50 4E 47 0D 0A 1A 0A
第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理。
2.2 数据块(chunk)
在png规范[2]中总计定义了18种chunk,其中4类chunk是关键数据块(critical chunk),每个PNG文件都必须包含它们,其余14类为辅助数据块(ancillary chunks),这是可选的数据块。
2.2.1 4类关键chunk
IHDR: image header, 在PNG文件中位置为第一块chunk.
PLTE: 调色板(palette table), 位于IDAT块之前.
IDAT: 图像数据块, 可以有多个连续的IDAT块.
IEND: image trailer, 在PNG文件中位置为最后一块chunk.
2.2.2 14类辅助chunk
14类辅助chunk可以归类为以下几种:
a. Transparency information(透明信息)
tRNS(Transparency-透明)
b. Colour space information(颜色空间信息)
cHRM(Primary chromaticities and white point:基色与白色点)
gAMA(Image gamma:图像gamma)
iCCP(Embedded ICC profile:内嵌ICC profile)
sBIT(Significant bits:样本有效位)
sRGB(Standard RGB colour space:标准RGB颜色空间)
c. Textual information(文本信息)
iTXt(International textual data: 国际化文本数据)
tEXt(Textual data:文本数据)
zTXt(Compressed textual data: 压缩文本数据)
d. Miscellaneous information(其他信息)
bKGD(Background colour:背景颜色)
hIST(Image histogram:图像直方图)
pHYs(Physical pixel dimensions:物理像素尺寸)
sPLT(Suggested palette:建议调色)
e. Time information(时间信息)
tIME(Image last-modification time: 图像最后修改时间)
2.2.3 chunk格式
每一块chunk由3个或4个字段组成。
Length (长度) 4字节 指定Chunk Data字段的长度,可以为0, 不超过(2^31-1)字节
Chunk Type(数据块类型) 4字节 数据块类型由ASCII字母(A-Z和a-z)组成,
每个字节的bit 5表示chunk属性, 可参见[2]中5.4 Chunk naming conventions
Chunk Data (数据块数据) 可变长度 存储按照Chunk Type指定的数据
CRC (循环冗余检测) 4字节 存储用来检测是否有错误的循环冗余码
[2]中5.6 Chunk ordering描述了每一类chunk在PNG文件中的顺序。
IEND chunk中没有data字段,因此Length字段为0, IEND chunk为以下12个字节(十六进制):
00 00 00 00 49 45 4E 44 AE 42 60 82
前4个字节为00 00 00 00,Type总是IEND(49 45 4E 44),因此,CRC码也总是AE 42 60 82,每个PNG文件最后12字节都是相同的。
3. libpng
[4]是libpng官方首页,从介绍中可知道它是用ANSI C (C89)编写,需要zlib 1.0.4/1.2.5及更高版本。当前最新版本是1.6.17(2015-07-16),从1.6源码 http://sourceforge.net/p/libpng/code/ci/libpng16/tree/CHANGES 可看出1.6.18正式版本正即将发布。
在该官网首页有以下漏洞警告信息:
libpng versions 1.6.9 through 1.6.15 (and some subset of versions up through 1.5.20) have an integer-overflow vulnerability in png_combine_row() when decoding very wide interlaced images, which can allow an attacker to overwrite an arbitrary amount of memory with arbitrary (attacker-controlled) data. This vulnerability has been assigned ID CVE-2014-9495 and is fixed in versions 1.6.16 and 1.5.21, released on 21 December 2014.
因此推荐尽可能使用最新版本的linpng。
4. 问题分析与解决
前面简要分析了PNG文件格式中的chunk, 以及libpng后,下面就开始分析和解决前面遇到的警告问题:
...\res\drawable-hdpi\add_green.png: libpng warning: iCCP: Not recognizing known sRGB profile that has been edited
4.1 iCCP chunk分析
还是需要先分析下iCCP chunk, 其chunk type为十六进制的69 43 43 50(iCCP)。参考[2]中11.3.3.3 iCCP Embedded ICC profile, 可以看出,iCCP chunk包含的data字段为:
Profile name 1-79 bytes (character string)
Null separator 1 byte (null character)
Compression method 1 byte
Compressed profile n bytes
其中,profile name是大小写敏感的,只能包含可打印拉丁字符与空格(即范围为十进制字符 32-126 与161-255 ), 不允许在前面与后面存在空格,不允许中间有多个连续空格。压缩方法只能取值为0,0表示对zlib数据流采用deflate压缩,接下来是压缩的profile.
每个PNG文件中最多只能包含一个内嵌profile, 可通过在iCCP chunk中显式指定或在sRGB chunk中隐含指定。
4.2 出问题PNG图片iCCP chunk分析
下面是出问题add_green.png文件中包含的iCCP chunk截图:
iCCP数据块各字段的含义:
十六进制值 | 描 述 |
00 00 0A 4F | iCCP数据块的长度,00 00 0A 4F =十进制2639 |
69 43 43 50 | 数据块类型标志,69 43 43 50的ASCII值等于iCCP |
50 68 6F 74 6F 73 68 6F 70 20 49 43 43 20 70 72 6F 66 69 6C 65 00 |
Profile名称,长度1~79字节, 以0作为终止符的字符串, ASCII值等于Photoshop ICC profile |
00 | 压缩方法,0表示使用deflate压缩 |
78 DA 9D 53~03 98 F3 FC | 压缩的profile,解码时使用 |
63 33 2D DB | CRC |
因此,这里的iCCP chunk的data字段从0x3D开始,由于长度为0A 4F, 因此data字段范围为0x00 3D~ 0A 8C。
4.3 libpng代码分析
从 http://sourceforge.net/p/libpng/code/ci/libpng16/tree/ 下载代码,可看到告警信息是在int png_compare_ICC_profile_with_sRGB( )函数中出现的:
通过检查http://sourceforge.net/p/libpng/code/ci/d630301d996b152de09028bb6803c4c136a0e85f/log/?path=%2Fpng.c, 可看到png.c中这个函数是在2012.03.29由 John Bowler新增的,修改注释信息为:
[libpng16] Recognize known sRGB ICC profiles while reading; prefer writing the iCCP profile over writing the sRGB chunk, controlled by the PNG_sRGB_PROFILE_CHECKS option.
代码中利用PNG_ICC_CHECKSUM宏来定义数组png_sRGB_checks[]中的一项,每一项的结构字段包括adler crc, length, MD5[4], have_md5, is_broken, intent, 见下图:
PNG_ICC_CHECKSUM宏里后三个参数date, length, file-name只是用于标记。
// 以下4个ICC sRGB profiles是来自于 www.color.org, 每个都有MD5校验码
下面3个profiles没有明确的MD5校验码,如果匹配空的MD5则用其他字段来尝试匹配并给出警告。下面这些profiles中前两个有一个'cprt' tag, 表示它们是由HP(Hewlett Packard)创建的。
根据png_compare_ICC_profile_with_sRGB( )函数中的逻辑,遍历png_sRGB_checks[]数组,当.md5[4]与profile中的md5值相同,length, intent, adler也相同,但重新计算的crc不等时,就将提示“Not recognizing known sRGB profile that has been edited”警告信息。
在scripts/pnglibconf.dfa文件中说明了本次修改的意图以及缺省检查级别为2的理由:
setting sRGB_PROFILE_CHECKS default 2
详细可参见[5]。
4.4 警告信息分析
结合libpng中png.c文件 png_compare_ICC_profile_with_sRGB( )函数可以看出,当profile的许多字段都相同时,如果crc不等则提示“Not recognizing known sRGB profile that has been edited”警告信息。
前面对出问题add_green.png文件分析可得出iCCP chunk中压缩的profile的最后四个字节是03 98 F3 FC, 对应的就是png_sRGB_checks[]数组中最后一个profile的adler字段:
PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d,
PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/,
"1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative")
因此,这张图的iCCP profile file name就是"HP-Microsoft sRGB v2 media-relative"。
从https://github.com/madler/zlib/blob/master/zlib.h 中struct z_stream可以看出:
uLong adler; /* adler32 value of the uncompressed data */
字段adler就是未压缩数据的adler值。那么这个adler字段是做什么用的呢?根据[6], Adler-32校验和在zlib中作为CRC32几乎是可靠的,但是计算起来更快,将iCCP profile data压缩后就追加在data后面。adler其实就是创始人Mark Adler的名字。
4.5 回答第一章节中提出的问题
综上,通过对PNG文件格式中iCCP chunk中profile的分析,libpng中png.c文件的分析,www.color.org中ICC sRGB profiles的说明,以及对zlib.h中adler32的分析,可以得出以下结论:
当PNG图片中iCCP chunk中压缩的profile在处理时,当md5, length, intent, adler32等字段相同,但重新计算的crc不等时则提示“Not recognizing known sRGB profile that has been edited”警告信息。
上述警告信息在2012.03.29由 John Bowler新增的png_compare_ICC_profile_with_sRGB( )函数在检查时给出,即libpng 1.6.0正式版本中引入这个检查函数。
4.6 问题解决
明确了linpng严格检查的版本以及检查的内容后,那么如何来解决该问题呢。
4.6.1 解决方案1: 删除png图片内嵌的iCCP profile sRGB
[13, 16, 17]中有一些答案建议通过Image Magick/mogrify/GIMP/exiftool等工具来"convert"或"mogrify"图片,删除png图片中内嵌的iCCP profile sRGB:
Image Magick使用举例:
删除单个png文件内的profile: % convert -strip
批量删除所有png文件内的profile sRGB:
set fn=E:\Program Files\ImageMagick-6.9.0-Q16\convert.exe
for /f "tokens=*" %%i in ('dir/s/b *.png') do "%fn%" "%%i" -strip "%%i"
mogrify使用举例:
删除单个png文件内的profile sRGB: mogrify +profile sRGB
批量删除所有png文件内的profile sRGB:
find
GIMP使用举例:
删除内嵌profile, 可先进入Image > Mode > Assign Color Profile并设置为RGB workspace(sRGB built-in), 然后File > Overwrite add_green.png覆盖原来的png文件。
修改内嵌profile, 可进入Image > Mode > Convert to Color Profile, 可选择一种profile。
在[17]也还提到:libpng 1.6+更严格的检查会对original HP/MS sRGB profile报警。老的profile使用D50 whitepoint, 而D65才是标准。这种profile由Adobe Photoshop使用, 虽然缺省在png图片中并不嵌入该profile。最简单的方法是从图片中删除内嵌的profile,但这会导致颜色有稍许偏差(当有颜色校正系统时)。但如果不希望颜色有偏差(例如用于打印输出), 可以嵌入另一种不同的颜色profile。
[13]中也有网友指出:这样删除png图片中的iCCP profile sRGB, 将丢失如何来render图片等信息,png中的色彩可能被改变。
4.6.2 解决方案2: 将aRGB转换为sRGB
[14]中有网友提到:这个图片是sRGB的改成ARGB(Adobe RGB)的就可以啦,在Android Studio中的右上角会显示24位而ARGB的图片显示是32位,但我本地报这种警告的png图片除了有32位以外还有24位的,因此这个方案不太可行。
综合上面的意见,[17]中给出的结论比较令人信服,利用GIMP工具删除内嵌的profile后问题解决。
5. 参考资料
[1] Libpng 1.6.17 - March 26, 2015, http://www.libpng.org/pub/png/src/libpng-1.6.17-README.txt
[2] Portable Network Graphics (PNG) Specification (Second Edition), http://www.w3.org/TR/2003/PR-PNG-20030520/
[3] PNG文件结构分析, http://wenku.baidu.com/view/b87e978583d049649b66586a.html?re=view
[4] libpng官方, http://libmng.com/pub/png/libpng.html
[5] [libpng16] Recognize known sRGB ICC profiles while reading, http://sourceforge.net/p/libpng/code/ci/921648a997e733eb63e18e835a9b98a5507da197/
[6] zlib库剖析(1):实现概览, http://blog.csdn.net/zhoudaxia/article/details/8034606
[7] 图片资源添加出现问题: No resource found that matches the given name 安卓 maven编译, http://1985wanggang.blog.163.com/blog/static/77638332015011114647601/
[8] zlib Technical Details, http://www.zlib.net/zlib_tech.html
[9] 漫谈显示器色彩管理(一), http://zhuanlan.zhihu.com/hardware/19648994
[10] 漫谈显示器色彩管理(二), http://zhuanlan.zhihu.com/hardware/19649559
[11] 漫谈显示器色彩管理(三), http://zhuanlan.zhihu.com/hardware/19649897
[12] 漫谈显示器色彩管理(四), http://zhuanlan.zhihu.com/hardware/19651812
[13] Issue 77704: Built tools 21.0.1: multiple libpng warnings, https://code.google.com/p/android/issues/detail?id=77704
[14] AndroidStudio中\com.android.support错误如何解决, http://ask.csdn.net/questions/161424
[15] sRGB与aRGB的颜色设置转换, http://blog.sina.com.cn/s/blog_6cf45fc10102v81s.html
[[16] libpng warning: iCCP: Not recognizing known sRGB profile that has been edited, https://groups.google.com/forum/#!msg/adt-dev/rjTQ_STR3OE/-UcNQRISTKsJ
[17] libpng errors, https://wiki.archlinux.org/index.php/Libpng_errors
[18] GIMP, http://www.gimp.org/