mChunk.parseNext();
}
// located at first IDAT or fdAT chunk
// collect all consecutive dat chunks
boolean 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 OutputStream
mChunk.lockRead(dataOffset);
frame.imageStream = mPngStream;
return frame;
}
- Apng的消除操作Apng的消除操作是在ApngFrameRender的render方法做的,方法如下:
/** * 渲染当前帧画面 * * @param frame apng中当前帧 * @return 渲染合成后的当前帧图像 */ public Bitmap render(ApngFrame frame, Bitmap frameBmp) { // 执行消除操作 dispose(frame); // 合成当前帧 blend(frame, frameBmp); return mRenderFrame; }
dispose(ApngFrame frame)方法如下:
/** * 帧图像析构消除 - 提交结果 */ private void dispose(ApngFrame frame) { // last frame dispose op switch (mLastDisposeOp) { case APNG_DISPOSE_OP_NONE: // no op break; case APNG_DISPOSE_OP_BACKGROUND: // clear rect mRenderCanvas.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 bitmap Bitmap bmp = mRenderFrame; mRenderFrame = mDisposedFrame; mDisposedFrame = bmp; mRenderCanvas.setBitmap(mRenderFrame); mDisposeCanvas.setBitmap(mDisposedFrame); break; } // current frame dispose op mLastDisposeOp = frame.getDisposeOp(); switch (mLastDisposeOp) { case APNG_DISPOSE_OP_NONE: // no op break; case APNG_DISPOSE_OP_BACKGROUND: // cache rect for next clear dispose int 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 dispose mDisposeCanvas.clipRect(mFullRect, Region.Op.REPLACE); mDisposeCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); mDisposeCanvas.drawBitmap(mRenderFrame, 0, 0, null); break; } }
- Apng的合成操作Apng的合成操作是在每一帧经过dispose之后做的,具体方法是blend(ApngFrame frame, Bitmap frameBmp),代码如下:
/** * 帧图像合成 */ 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); mRenderCanvas.clipRect(mFullRect, Region.Op.REPLACE); }
- Apng的绘制Apng的每一帧经过消除、合成操作之后,就可以在View上面draw,具体代码如下:
/** * draw the appointed frame */ private void drawFrame(AnimParams animItem, ApngFrame frame, Bitmap frameBmp) { if (surfaceEnabled && !isInterrupted()) { //start to draw the frame try { Matrix matrix = new Matrix(); matrix.setScale(mScale, mScale); Bitmap bmp = mFrameRender.render(frame, frameBmp); //saveBitmap(bmp, index); index ++; Canvas canvas = getHolder().lockCanvas(); //anti-aliasing canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); float[] tranLeftAndTop = ApngUtils.getTranLeftAndTop(canvas, bmp, animItem.align, mScale, animItem.percent); canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG)); matrix.postTranslate(tranLeftAndTop[0], tranLeftAndTop[1]); canvas.drawBitmap(bmp, matrix, null); getHolder().unlockCanvasAndPost(canvas); // unlock the canvas } catch (Exception e) { Log.e(TAG, "draw error msg:" + Log.getStackTraceString(e)); } } }
- 实例我们是在SurfaceView上面来绘制Apng的每一帧,例子如下:
Activity代码:
public class MainActivity extends Activity{ private ApngSurfaceView mApngSurfaceView; private static final String COLOR_BALL_IMAGE_PATH = "assets://color_ball.png"; //private static final String CAR_IMAGE_PATH = "assets://car.png"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mApngSurfaceView = (ApngSurfaceView)findViewById(R.id.apng_surface_view); Button startPlay = (Button) findViewById(R.id.start_play); startPlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { playAnim(); } }); } private void playAnim(){ File file = FileUtils.processApngFile(COLOR_BALL_IMAGE_PATH, this); if(file == null) return; AnimParams animItem = new AnimParams(); animItem.align = 2; animItem.imagePath = file.getAbsolutePath(); animItem.isHasBackground = true; animItem.percent = 0.5f; mApngSurfaceView.addApngForPlay(animItem); } }
Layout代码:
<?xml version="1.0" encoding="utf-8"?>