ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

APNG4Android 源码解析:APNG 的 ACTL/FCTL/FDAT Chunk 逐块解码原理

APNG4Android 源码解析:APNG 的 ACTL/FCTL/FDAT Chunk 逐块解码原理 APNG4Android 源码解析APNG 的 ACTL/FCTL/FDAT Chunk 逐块解码原理【免费下载链接】APNG4AndroidAndroid animation support for APNG Animated WebP Gif Animated AVIF, High performance项目地址: https://gitcode.com/gh_mirrors/ap/APNG4Android如果你在 Android 上播放过带动画的 PNGAnimated PNG简称 APNG那么大概率接触过APNG4Android这个高性能开源库。它同时支持 APNG、Animated WebP、GIF 和 Animated AVIF被大量应用用于加载动图。本文是面向初学者的APNG4Android 源码解析重点拆解 APNG 格式中最核心的三个 Chunk数据块acTL、fcTL 和 fdAT带你逐块看懂这个库是如何把一张静态 PNG 变出一连串动画帧的。提示文章涉及的所有源码都位于apng/模块下你可以边读文章边对照源码理解会更深刻。先搞懂 APNG 和普通 PNG 的区别普通 PNG 是一张死图而 APNG 是在 PNG 规范上扩展出来的动画格式。它保留了 PNG 原有的 IHDR、IDAT、IEND 等基础块额外增加了几个动画专属块acTLAnimation Control动画控制块声明总帧数和循环次数是 APNG 的身份证fcTLFrame Control帧控制块描述每一帧的位置、大小、延迟和合成方式fdATFrame Data帧数据块存放每一帧真正的图像压缩数据。在APNG4Android 源码解析中这三个块分别对应ACTLChunk、FCTLChunk、FDATChunk三个类它们都继承自基类 Chunk.java。APNG 的 Chunk 通用结构一切解析的起点PNG 文件本质上是一连串 Chunk 的集合每个 Chunk 都是固定格式的四段式结构字段字节数含义Length4数据区长度Type Code4块类型标识如 acTL、fcTLData可变该块的实际数据CRC4循环冗余校验码基类 Chunk.java 里的length、fourcc、crc、offset四个字段就是按这个结构设计的。而parse()方法负责读完数据后如果子类没消费完整个数据区就自动skip跳过剩余字节保证解析器能安全地移动到下一个 Chunk。APNG4Android 如何识别一个文件是不是 APNG在真正逐块解码之前库先要回答一个问题这个文件是 APNG 还是普通 PNG判断逻辑在 APNGParser.java 的isAPNG()中先校验文件头 8 字节的 PNG 签名\x89PNG\r\n\x1a\n然后从文件头开始逐个 Chunk 顺序读取只要遇到acTL块就立刻断定这是 APNG返回true。因为 APNG 规范强制要求acTL必须出现在IDAT之前所以这个检测既快又可靠。这一步也解释了为什么ACTLChunk被称为 APNG 的身份证。ACTL Chunk动画控制块的数据结构acTL是 APNG 里出现的第一个动画专属块数据区只有 8 个字节见 ACTLChunk.javanum_frames4 字节动画总帧数num_plays4 字节循环播放次数0 表示无限循环。在 APNGDecoder.java 的read()中读到acTL后会把num_plays存进mLoopCount作为整个动画的循环控制依据。FCTL Chunk帧控制块的逐字段拆解fcTL是理解 APNG 动画最关键的一块数据区固定 26 字节FCTLChunk.java 的innerParse()按顺序读取了这些字段字段字节数含义sequence_number4序列号用于校验块顺序width / height44本帧图像宽高x_offset / y_offset44本帧相对画布左上角的偏移delay_num / delay_den22帧延迟分数如 1/10 秒dispose_op1本帧结束后如何处置画布区域blend_op1本帧如何与已有画布合成其中有两个概念新手最容易困惑值得展开讲讲。帧延迟 delay_num / delay_den 的计算帧的显示时长由delay_num / delay_den决定。若delay_den为 0则按 100 处理。在 APNGFrame.java 的构造函数里可以看到最终换算成毫秒的公式frameDuration fctlChunk.delay_num * 1000 / (fctlChunk.delay_den 0 ? 100 : fctlChunk.delay_den);有意思的是如果算出来的时长小于 10ms库会强制设为 100ms——这是为了防止某些广告图用 0 延迟疯狂闪烁代码注释里明确说了这是参考 Safari 和 Firefox 的行为。dispose_op 与 blend_op动画合成的两大开关dispose_op本帧播放完、下一帧开始前如何处理画布。有三种取值NONE保留原样、BACKGROUND清空本帧区域为透明、PREVIOUS恢复本帧之前的画面blend_op本帧画上去时如何合成。SOURCE表示整块覆盖OVER表示按 alpha 叠加。这两个字段在 APNGDecoder.java 的renderFrame()里被完整实现使用clipRect裁剪出帧区域按dispose_op清空或从快照恢复像素再按blend_op决定是否先清空再绘制。这段逻辑是动画能透明叠加、帧帧衔接的关键。FDAT Chunk帧数据块与 IDAT 的巧妙关系fdAT是存放动画帧压缩数据的地方数据区第一项是 4 字节的sequence_number见 FDATChunk.java后面才是真正的图像数据。这里有一个非常精妙的点fdAT 的数据内容本质上就是 PNG 的 IDAT 数据只是多包了一层序列号。所以当 APNGDecoder.java 遍历 Chunk 时无论是fdAT还是第一帧使用的IDAT都会被追加到当前帧对象的imageChunks列表里交给同一套解码流程处理。APNGFrame如何把 fdAT 重组回一张可解码的 PNG由于 Android 自带的BitmapFactory只认标准 PNGAPNG4Android 源码解析里最核心的偷懒但高效的技巧在 APNGFrame.java 的encode()方法中用 PNG 签名 构造好的 IHDR 块开头把前缀块如颜色配置原样拷贝进去遇到IDAT直接拷贝遇到fdAT则把它的sequence_number剥掉改写成标准IDAT块并重新计算 CRC最后补上IEND结束块。这样每一帧都被拼成一张独立、合法的 PNG 字节流直接交给BitmapFactory.decodeByteArray()解码成 Bitmap再按 fcTL 里记录的坐标和合成方式绘制到画布上。整个设计既绕开了自己写 zlib 解压的复杂度又复用了系统级的硬件加速解码能力。三个 Chunk 的完整协作流程小结把整个动画播放串起来就是下面这条流水线APNGParser.parse()从头到尾读取所有 Chunk遇到acTL记录总帧数和循环次数遇到fcTL就 new 一个APNGFrame把帧控制信息存进去接下来的fdAT或首帧IDAT都挂到当前帧的imageChunks上播放时APNGFrame.encode()把每帧重组为独立 PNG 并解码renderFrame()依据dispose_op/blend_op完成帧间合成与清屏形成连贯动画。相关源码文件速查想深入研读的话重点看这几个文件路径均在项目apng模块内Chunk 基类与四段式结构Chunk.java动画控制块ACTLChunk.java帧控制块FCTLChunk.java帧数据块FDATChunk.java逐块解析入口APNGParser.java帧重组与解码APNGFrame.java帧渲染与合成APNGDecoder.java写在最后通过这篇APNG4Android 源码解析你应该已经明白APNG 动画并没有想象中神秘它只是普通 PNG acTL 控制 多个 fcTL/fdAT 帧块的组合。APNG4Android 的高明之处在于让系统解码器去干活自己只负责拼装和合成因此在 Android 上能做到低内存、高性能的动图播放。如果你正在做 Android 图片加载相关的开发这套重组帧数据 复用系统解码的思路非常值得借鉴。【免费下载链接】APNG4AndroidAndroid animation support for APNG Animated WebP Gif Animated AVIF, High performance项目地址: https://gitcode.com/gh_mirrors/ap/APNG4Android创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表