aemg动画教程(分享7日MG动画全套教程)

1.概述

在深入了解Apng动画播放之前,我们需要对Apng的结构有所了解,具体参见Apng动画介绍,对Apng的整体结构有所了解后,下面我们来讲讲Apng动画的播放,主要包括Apng解析和Apng渲染两个过程。

2. Apng动画播放流程

Apng动画播放流程包括Apng解析和Apng渲染两个过程,Apng解析主要有两种方法,下面我们将会介绍,而Apng渲染主要包括三个步骤:消除(dispose)、合成(blend)、绘制(draw),由此得到Apng动画播放流程图如下:

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

Apng动画播放流程

3. Apng的解析

Apng的解析主要是将Apng文件转化成Apng序列帧Frame-n,从上面的流程图可知,Apng文件的解析列出了两种方案,下面来分别说说:

1)Apng文件首先经过一个解压(ApngExact)的过程,生成png序列帧保存在本地,然后经过加载(LoadPng)处理生成序列帧Frame-n。假设Apng动画文件总共有90帧,那么经过ApngExact处理后,会生成90张png序列帧保存在本地,每帧通过LoadPng处理生成Bitmap并供后面的Apng渲染使用。

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的构造函数如下:

publicApngReader(StringapngFile)throwsIOException,FormatNotSupportException{
RandomAccessFilef=newRandomAccessFile(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){
thrownewFormatNotSupportException("Notapng/apngfile");
}
mChunk=newApngMmapParserChunk(mBuffer);
reset();
}

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

/**
*getnextframecontrolinfo&bitmap
*
*@returnnextframecontrolinfo,ornullifnonextFCTLchunk||nonextIDAT/FDAT
*@throwsIOException
*/
publicApngFramenextFrame()throwsIOException{
//resetreadpointersfrompreviousframe'slock
mPngStream.clearDataChunks();
mPngStream.resetPos();
mChunk.unlockRead();

//locatenextFCTLchunk
booleanihdrCopied=false;
while(mChunk.typeCode!=CODE_fcTL){
switch(mChunk.typeCode){
caseCODE_IEND:
returnnull;
caseCODE_IHDR:
mPngStream.setIHDR(mChunk.duplicateData());
break;
caseCODE_acTL:
handleACTL(mChunk);
ihdrCopied=true;
break;
default:
handleOtherChunk(mChunk);
}
mChunk.parseNext();
}

//locatedatFCTLchunk
ApngFrameframe=newApngFrame();
mChunk.assignTo(frame);

//locatenextIDATorfdAtchunk
mChunk.parseNext();//firstmovenextfromcurrentFCTL
while(mChunk.typeCode!=CODE_IDAT&&mChunk.typeCode!=CODE_fdAT){
switch(mChunk.typeCode){
caseCODE_IEND:
returnnull;
caseCODE_IHDR:
mPngStream.setIHDR(mChunk.duplicateData());
ihdrCopied=true;
break;
caseCODE_acTL:
handleACTL(mChunk);
break;
default:
handleOtherChunk(mChunk);
}
mChunk.parseNext();
}

//locatedatfirstIDATorfdATchunk
//collectallconsecutivedatchunks
booleanneedUpdateIHDR=true;
intdataOffset=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(newFdat2IdatChunk(mChunk));
}else{
mPngStream.addDataChunk(newApngMmapParserChunk(mChunk));
}
mChunk.parseNext();
}

//lockpositionforthisframe'simageasOutputStream
mChunk.lockRead(dataOffset);
frame.imageStream=mPngStream;
returnframe;
}

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

/**
*渲染当前帧画面
*
*@paramframeapng中当前帧
*@return渲染合成后的当前帧图像
*/
publicBitmaprender(ApngFrameframe,BitmapframeBmp){
//执行消除操作
dispose(frame);
//合成当前帧
blend(frame,frameBmp);
returnmRenderFrame;
}

dispose(ApngFrame frame)方法如下:

/**
*帧图像析构消除-提交结果
*/
privatevoiddispose(ApngFrameframe){
//lastframedisposeop
switch(mLastDisposeOp){
caseAPNG_DISPOSE_OP_NONE:
//noop
break;

caseAPNG_DISPOSE_OP_BACKGROUND:
//clearrect
mRenderCanvas.clipRect(mDisposeRect);
mRenderCanvas.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
mRenderCanvas.clipRect(mFullRect,Region.Op.REPLACE);
break;

caseAPNG_DISPOSE_OP_PREVIOUS:
//swapworkandcachebitmap
Bitmapbmp=mRenderFrame;
mRenderFrame=mDisposedFrame;
mDisposedFrame=bmp;
mRenderCanvas.setBitmap(mRenderFrame);
mDisposeCanvas.setBitmap(mDisposedFrame);
break;
}

//currentframedisposeop
mLastDisposeOp=frame.getDisposeOp();
switch(mLastDisposeOp){
caseAPNG_DISPOSE_OP_NONE:
//noop
break;

caseAPNG_DISPOSE_OP_BACKGROUND:
//cacherectfornextcleardispose
intx=frame.getxOff();
inty=frame.getyOff();
mDisposeRect.set(x,y,x+frame.getWidth(),y+frame.getHeight());
break;

caseAPNG_DISPOSE_OP_PREVIOUS:
//cachebmpfornextrestoredispose
mDisposeCanvas.clipRect(mFullRect,Region.Op.REPLACE);
mDisposeCanvas.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
mDisposeCanvas.drawBitmap(mRenderFrame,0,0,null);
break;
}
}

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

/**
*帧图像合成
*/
privatevoidblend(ApngFrameframe,BitmapframeBmp){
intxOff=frame.getxOff();
intyOff=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);
}

4) Apng的绘制Apng的每一帧经过消除、合成操作之后,就可以在View上面draw,具体代码如下:

/**
*drawtheappointedframe
*/
privatevoiddrawFrame(AnimParamsanimItem,ApngFrameframe,BitmapframeBmp){
if(surfaceEnabled&&!isInterrupted()){
//starttodrawtheframe
try{
Matrixmatrix=newMatrix();
matrix.setScale(mScale,mScale);
Bitmapbmp=mFrameRender.render(frame,frameBmp);

//saveBitmap(bmp,index);
index++;

Canvascanvas=getHolder().lockCanvas();
//anti-aliasing
canvas.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
float[]tranLeftAndTop=ApngUtils.getTranLeftAndTop(canvas,bmp,animItem.align,mScale,animItem.percent);
canvas.setDrawFilter(newPaintFlagsDrawFilter(0,Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
matrix.postTranslate(tranLeftAndTop[0],tranLeftAndTop[1]);
canvas.drawBitmap(bmp,matrix,null);
getHolder().unlockCanvasAndPost(canvas);//unlockthecanvas
}catch(Exceptione){
Log.e(TAG,"drawerrormsg:"+Log.getStackTraceString(e));
}
}
}
  1. 实例我们是在SurfaceView上面来绘制Apng的每一帧,例子如下:

Activity代码:

publicclassMainActivityextendsActivity{
privateApngSurfaceViewmApngSurfaceView;
privatestaticfinalStringCOLOR_BALL_IMAGE_PATH="assets://color_ball.png";
//privatestaticfinalStringCAR_IMAGE_PATH="assets://car.png";


@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mApngSurfaceView=(ApngSurfaceView)findViewById(R.id.apng_surface_view);
ButtonstartPlay=(Button)findViewById(R.id.start_play);
startPlay.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
playAnim();
}
});
}


privatevoidplayAnim(){
Filefile=FileUtils.processApngFile(COLOR_BALL_IMAGE_PATH,this);
if(file==null)return;
AnimParamsanimItem=newAnimParams();
animItem.align=2;
animItem.imagePath=file.getAbsolutePath();
animItem.isHasBackground=true;
animItem.percent=0.5f;
mApngSurfaceView.addApngForPlay(animItem);
}
}

Layout代码:

<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context=".MainActivity">

<com.apng.ApngSurfaceView
android:id="@+id/apng_surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
<Button
android:id="@+id/start_play"
android:text="@string/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

</RelativeLayout>

我们把一张Apng图片放到Asset目录下,通过ApngSurfaceView来播放。

5. 说明

例子演示:

Android-Apng动画的播放,大牛手把手动态教学,你都不愿意看吗?Android-Apng动画的播放,大牛手把手动态教学,你都不愿意看吗?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至624739273@qq.com举报,一经查实,本站将立刻删除。
Like (0)
柳的头像

相关推荐

发表回复

Please Login to Comment
微信
微信
SHARE
TOP
要想花得少,就用购宝。话费电费9折起,官方公众号:购宝