九个宽度或高度为1px的补丁图像——特殊情况或错误文件



在更改Launcher2.apk中发现的图像的过程中,我发现一些文件显然是九个补丁,但高度或宽度仅为1px:

$ file **/*.9.png G "( 1 x|x 1,)"
Launcher2/res/drawable-land-xhdpi/workspace_bg.9.png:                   PNG image, 400 x 1, 8-bit gray+alpha, non-interlaced
Launcher2/res/drawable-sw720dp-xhdpi/workspace_bg.9.png:                PNG image, 1 x 400, 8-bit gray+alpha, non-interlaced
Launcher2/res/drawable-xhdpi/workspace_bg.9.png:                        PNG image, 1 x 400, 8-bit gray+alpha, non-interlaced

这些文件是由创建者搞砸的,还是特殊情况,即固定宽度但可变高度,反之亦然?

九个补丁文件的格式有什么正式的定义吗?当然,我在谷歌上搜索了一下,花了我很长时间,没有找到任何对我有帮助的信息。最好能涵盖其他方面,因为我发现了很多其他方面多个包中的可疑图像在最左边/最上面/最右边/最下面的像素中不包含二进制信息;因此,我也想确切地知道哪些值按原样编码repeat/estretch/,哪些值被忽略。

谷歌Canvas and Drawables文档中的这一部分没有提供太多细节(实际上没有)。分布式draw9patch.jar也没有多大帮助。

以防万一:我从Launcher2.apk和其他包中拿走的设备是Moto G,库存4.3固件。

提前感谢

好吧,自从我对九个补丁图像进行了更多研究以来,我学到了什么:

主要错误可能是认为九个补丁的重复/拉伸/保持元数据总是编码在图像文件的边界像素中。这不是真的。

事实上,有两种九种补丁格式,显然被称为源格式和编译格式,前者具有众所周知的特殊边界,其中像素指定补丁(实际上,其中可以有九种以上)。这很可能被称为形式,因为像素在图像编辑器中会显示出来(然而,在某些情况下,它们可能不会显示出来;请参阅下文)。

根据apktool wiki;已编译—表单将这个元信息编码在一个特殊的(私有的)PNG块中;同一页声称这些块被称为npTc,我尝试用基本的Linux实用程序来验证这一点,可以证实这一点:

$ strings search_frame.9.png 
IHDR
TnpTc
yIDATX
[...]

另外,TweakPNG显示了这个区块。

所以,我实际问题的答案是:我认为那些1像素高/宽的图像是源形式的九个补丁,而它们实际上是经过编译的压缩格式。


由于似乎没有官方规范,而且这是问题的一部分,我只是在这里开始收集信息。


从一种形式转换为另一种形式的工具

  • aapt(安卓sdk附带)有两个子命令,

    • s[inglecrunch]将一个带边框的九个补丁图像编译为分块/压缩格式(分别对输入和输出文件使用命令行选项-i-o)
    • c[runch]获取一个资源目录(选项-S)和一个输出目录的名称(选项-C),遍历整个源目录,并根据需要创建目标子目录

    (请注意:如果您忘记事先创建目标目录树的根,aapt不会出错,而是忙于$whatever,占用了整个CPU核心/线程的注意力和能力,因此有效地表明它很忙,而它至少没有达到用户的期望。(为了完整起见:我正在运行SDK 19.0.1)

  • apktool是一个自定义的java应用程序,它能够解包整个*.apk文件,将*.xml文件转换为纯文本文件,并将*.9.png文件转换为其格式。

    要将所有内容(可能修改的内容)放回*.apk归档中,您需要从原始包中手动提取一些文件,大多数zip/unzip实用程序应该能够做到这一点,但无需转换。在重新包装的过程中,apktool显然使用了aapt,它捆绑了一个版本。

    这个捆绑的aapt可能是从一个旧的SDK(19之前)中提取的,因为它显示了相同的缺陷(上面提到的紧密繁忙循环,以及几个segfault,其中一个简单的if可以防止这种恶劣的行为(例如,当你不给c[runch]子命令-C选项时,它会segfault)),而它缺乏对s[ingleCrunch]子命令的支持。

  • xUltimate-d9pc(这似乎是一个-到经过处理的格式转换器,但我并没有真正测试它。)

  • WebLaF(同样未经测试;它似乎有来源,可以从中获得更多信息。)

九个补丁的格式

源窗体

大多数人已经知道,形式的九个补丁确实包括图像的正常像素数据,以及一个额外的边界。我将尝试用一个简单的ASCII表示来说明(并希望您的字体能够识别它的含义):

-------##--#####--##-------
-......XXXXXXXXXXXXX......-
-...XXX.............XXX...-
-..X...................X..-
-.X.....................X.-         Legend
-.X.....................X.-
#X.......................X-         (top and left borders)
#X.......................X-         #  denotes a "stretch"/"repeat" mark
-X.......XXXXXXXXX.......X-         -  denotes a "keep" mark
-X.......X.......X.......X#
#X.......X.......X.......X#         (bottom and right borders)
#X.......X.......X.......X#         #  marks the content area (aka fill area)
-X.......X.......X.......X#         -  marks ... umm... what is it called?
-X.......XXXXXXXXX.......X-
#X.......................X-         (image area)
#X.......................X-         .  pixel in "background" color
-.X.....................X.-         X  pixel in foreground color
-.X.....................X.-
-..X...................X..-
-...XXX.............XXX...-
-......XXXXXXXXXXXXX......-
----------#######----------

这是某种双盒,其外部有圆角。这种九块补丁的大小是27x22px,但实际图像大小只有25x20px;这也是它在应用程序中显示的最小大小,但它可以扩展到任意大小。

重复标记告诉框架,当图像被拉伸时,例如,当图像以26个像素的实际高度显示时,要重复哪些行,第7、8、11、12、15和16行,并且每行重复一次,以弥补屏幕上多余的行。这样可以确保两个框的边界始终保持1像素薄,并且外框的圆角始终具有相同的大小/半径。而标记区域将被充分拉伸(好吧,如果拉伸尺寸与原始尺寸的差异不是标记线的倍数,就会有不规则性,但在今天的160+dpi显示屏上,这可能无关紧要;然而,较少的重复/拉伸标记可能会更具性能,但话说回来…在今天的1.6+GHz四核手机上,这也无关紧要…)

重复/拉伸标记的值当我发布问题时,我不确定拉伸重复之间是否有任何语义差异,但我仍然没有。

根据Android UI的9补丁简单指南,标记必须是实心黑色,以重复有问题的行/列,否则结果将是错误的,除非在屏幕上错误显示;这对于以前的SDK版本可能是正确的(文章来自2011年5月)。

然而,两个aapt版本(SDK 19和apktool1.5.2附带的版本)都抱怨边界中的任意颜色,例如:

ERROR: 9-patch image test/search_frame_.9.png malformed.
Ticks in transparent frame must be black or red.
Found at pixel #21 along top edge.
ERROR: 9-patch image test/tab_unselected_pressed_focused_holo.9.png malformed.
Must have one-pixel frame that is either transparent or white.

其中第二个文件已经是一个编译/处理过的九个补丁,而第一个不是。

我从"黑色或红色"消息中了解到,边框可能不包含#000000000(纯黑色,用于重复/拉伸标记)、#000ff0000(纯红色,可能用于相同标记)和#xx000000(黑色,具有任意透明度(xx>0),用于"保留"标记,可能也包含#xxFF0000,但我没有测试)以外的像素值。

我仍然不确定的是,红色和黑色是否有不同的语义,或者红色是否只是使用黑色背景的图像编辑器的替代品,例如,在黑色背景下,很难在视觉上区分#000000000和#001000000)。

编译/处理过的表格

SDK包含两个类android.graphics.NinePatchandroid.graphics.NinePatch_Delegate,它们使用对本机库的调用,因此源代码不会给我们太多信息。我没有安装NDK,甚至不知道它是否为相关库提供了源代码。

我发现最好的是Android的答案:编译9个补丁文件,在可绘制文件夹之外使用?

最新更新