写点什么

Android-Apng 动画的播放,大牛手把手动态教学,你都不愿意看吗

用户头像
Android架构
关注
发布于: 刚刚

2)Apng 是一个独立的文件,我们自己编写读取 Apng 文件的代码类:ApngReader,当渲染第 i 帧时,通过 ApngReader 直接获取第 i 帧的 Bitmap。


比较:


1)方案一是将 Apng 文件全部解压成 png 序列图片保存在本地,方案二是把 Apng 文件当做一个整体去处理,需要第几帧直接读取第几帧,并将该帧以 Bitmap 的形似保存到内存。


2)方案一解压得到的 png 图片在后面的渲染中需要转化成 Bitamp,而方案二直接就获取了第几帧的 Bitmap,相比于方案一,方案二减少了一个从 SD 卡读取 png 文件的操作。

4. Apng 的渲染

方案一的具体实现大家可以参考 github 上面的一个项目 apng-view,下面我们来讲讲方案二的具体实现,即 ApngReader 的具体实现。


  1. 解析 Apng 的每一帧我们是将整个文件放到一个 buffer 里面,并且通过 RandomAccessFile、MappedByteBuffer 来读取 Apng 的每一帧,ApngReader 的构造函数如下:


<pre style="box-sizing: border-box; outline: none; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; font-size: 1em; line-height: inherit; font-family: couriernew, courier, monospace; vertical-align: baseline;">


public?ApngReader(String?apngFile)?throws?IOException,?FormatNotSupportException?{RandomAccessFile?f?=?new?RandomAccessFile(apngFile,?"r");mBuffer?=?f.getChannel().map(FileChannel.MapMode.READ_ONLY,?0,?f.length());f.close();if?(mBuffer.getInt()?!=?PNG_SIG&&?mBuffer.getInt(4)?!=?PNG_SIG_VER&&?mBuffer.getInt(8)?!=?CODE_IHDR)?{throw?new?FormatNotSupportException("Not?a?png/apng?file");}mChunk?=?new?ApngMmapP


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


arserChunk(mBuffer);reset();}


</pre>


下面来看看读取每一帧的方法:


<pre style="box-sizing: border-box; outline: none; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; font-size: 1em; line-height: inherit; font-family: couriernew, courier, monospace; vertical-align: baseline;">


/***?get?next?frame?control?info?&?bitmap**?@return?next?frame?control?info,?or?null?if?no?next?FCTL?chunk?||?no?next?IDAT/FDAT*?@throws?IOException*/public?ApngFrame?nextFrame()?throws?IOException?{//?reset?read?pointers?from?previous?frame's?lockmPngStream.clearDataChunks();mPngStream.resetPos();mChunk.unlockRead();


//?locate?next?FCTL?chunkboolean?ihdrCopied?=?false;while?(mChunk.typeCode?!=?CODE_fcTL)?{switch?(mChunk.typeCode)?{case?CODE_IEND:return?null;case?CODE_IHDR:mPngStream.setIHDR(mChunk.duplicateData());break;case?CODE_acTL:handleACTL(mChunk);ihdrCopied?=?true;break;default:handleOtherChunk(mChunk);}mChunk.parseNext();}


//?located?at?FCTL?chunkApngFrame?frame?=?new?ApngFrame();mChunk.assignTo(frame);


//?locate?next?IDAT?or?fdAt?chunkmChunk.parseNext();//?first?move?next?from?current?FCTLwhile?(mChunk.typeCode?!=?CODE_IDAT?&&?mChunk.typeCode?!=?CODE_fdAT)?{switch?(mChunk.typeCode)?{case?CODE_IEND:return?null;case?CODE_IHDR:mPngStream.setIHDR(mChunk.duplicateData());ihdrCopied?=?true;break;case?CODE_acTL:handleACTL(mChunk);break;default:handleOtherChunk(mChunk);}mChunk.parseNext();}


//?located?at?first?IDAT?or?fdAT?chunk//?collect?all?consecutive?dat?chunksboolean?needUpdateIHDR?=?true;int?dataOffset?=?mChunk.getOffset();while?(mChunk.typeCode?==?CODE_fdAT?||?mChunk.typeCode?==?CODE_IDAT)?{if?(needUpdateIHDR?&&?(!ihdrCopied?||?mChunk.typeCode?==?CODE_fdAT))?{mPngStream.updateIHDR(frame.getWidth(),?frame.getHeight());needUpdateIHDR?=?false;}


if?(mChunk.typeCode?==?CODE_fdAT)?{mPngStream.addDataChunk(new?Fdat2IdatChunk(mChunk));}?else?{mPngStream.addDataChunk(new?ApngMmapParserChunk(mChunk));}mChunk.parseNext();}


//?lock?position?for?this?frame's?image?as?OutputStreammChunk.lockRead(dataOffset);frame.imageStream?=?mPngStream;return?frame;}


</pre>


  1. Apng 的消除操作 Apng 的消除操作是在 ApngFrameRender 的 render 方法做的,方法如下:


<pre style="box-sizing: border-box; outline: none; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; font-size: 1em; line-height: inherit; font-family: couriernew, courier, monospace; vertical-align: baseline;">


/***?渲染当前帧画面**?@param?frame?apng 中当前帧*?@return?渲染合成后的当前帧图像*/public?Bitmap?render(ApngFrame?frame,?Bitmap?frameBmp)?{//?执行消除操作 dispose(frame);//?合成当前帧 blend(frame,?frameBmp);return?mRenderFrame;}


</pre>


dispose(ApngFrame frame)方法如下:


<pre style="box-sizing: border-box; outline: none; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; font-size: 1em; line-height: inherit; font-family: couriernew, courier, monospace; vertical-align: baseline;">


/***?帧图像析构消除?-?提交结果*/private?void?dispose(ApngFrame?frame)?{//?last?frame?dispose?opswitch?(mLastDisposeOp)?{case?APNG_DISPOSE_OP_NONE://?no?opbreak;


case?APNG_DISPOSE_OP_BACKGROUND://?clear?rectmRenderCanvas.clipRect(mDisposeRect);mRenderCanvas.drawColor(Color.TRANSPARENT,?PorterDuff.Mode.CLEAR);mRenderCanvas.clipRect(mFullRect,?Region.Op.REPLACE);break;


case?APNG_DISPOSE_OP_PREVIOUS://?swap?work?and?cache?bitmapBitmap?bmp?=?mRenderFrame;mRenderFrame?=?mDisposedFrame;mDisposedFrame?=?bmp;mRenderCanvas.setBitmap(mRenderFrame);mDisposeCanvas.setBitmap(mDisposedFrame);break;}


//?current?frame?dispose?opmLastDisposeOp?=?frame.getDisposeOp();switch?(mLastDisposeOp)?{case?APNG_DISPOSE_OP_NONE://?no?opbreak;


case?APNG_DISPOSE_OP_BACKGROUND://?cache?rect?for?next?clear?disposeint?x?=?frame.getxOff();int?y?=?frame.getyOff();mDisposeRect.set(x,?y,?x?+?frame.getWidth(),?y?+?frame.getHeight());break;


case?APNG_DISPOSE_OP_PREVIOUS://?cache?bmp?for?next?restore?disposemDisposeCanvas.clipRect(mFullRect,?Region.Op.REPLACE);mDisposeCanvas.drawColor(Color.TRANSPARENT,?PorterDuff.Mode.CLEAR);mDisposeCanvas.drawBitmap(mRenderFrame,?0,?0,?null);break;}}


</pre>


  1. Apng 的合成操作 Apng 的合成操作是在每一帧经过 dispose 之后做的,具体方法是 blend(ApngFrame frame, Bitmap frameBmp),代码如下:


<pre style="box-sizing: border-box; outline: none; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; font-size: 1em; line-height: inherit; font-family: couriernew, courier, monospace; vertical-align: baseline;">


/***?帧图像合成*/private?void?blend(ApngFrame?frame,?Bitmap?frameBmp)?{int?xOff?=?frame.getxOff();int?yOff?=?frame.getyOff();


mRenderCanvas.clipRect(xOff,?yOff,?xOff?+?frame.getWidth(),?yOff?+?frame.getHeight());if?(frame.getBlendOp()?==?APNG_BLEND_OP_SOURCE)?{mRenderCanvas.drawColor(Color.TRANSPARENT,?PorterDuff.Mode.CLEAR);}mRenderCanvas.drawBitmap(frameBmp,?xOff,?yOff,?null);

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android-Apng动画的播放,大牛手把手动态教学,你都不愿意看吗