先看一篇关于gif格式文件的原理介绍:
http://blog.csdn.net/wzy198852/article/details/17266507
播放步骤:
1.加载gif文件
2.获取到gif文件的信息,包括宽、高、帧数、每帧播放时长、当前播放到的帧的位置
3.对每帧图片进行渲染
android源码中其实提供了播放gif的类库,但是并没有提供上层调用的接口
路径:/Users/huozhenpeng/Documents/android-6.0.0_r1/external
将这些文件copy到我们的工程中
代码:
这个文件夹下的东西都是系统的,处理native-lib.cpp是我们自己写的
native-lib.cpp
#include <jni.h>
#include <string>
#include "gif_lib.h"
#include <android/bitmap.h>
#define argb(a,r,g,b) ( ((a) & 0xff) << 24 ) | ( ((b) & 0xff) << 16 ) | ( ((g) & 0xff) << 8 ) | ((r) & 0xff)
typedef struct GifBean
{
//正在播放的当前帧
int currentFrame;
//记录所有帧的时长
int *dealys;
//记录总的帧数
int totalFrame;
} GifBean;
void drawFrame(GifFileType *gifFileType,GifBean *gifBean,AndroidBitmapInfo info,void *pixels)
{
//获取当前帧
SavedImage savedImage=gifFileType->SavedImages[gifBean->currentFrame];
GifImageDesc imageDesc=savedImage.ImageDesc;
//某个像素位置
int pointPixel;
int *px= (int *) pixels;
int *line;
GifColorType gifColorType;
ColorMapObject *colorMapObject=imageDesc.ColorMap;
if(colorMapObject==NULL)
{
colorMapObject=gifFileType->SColorMap;
}
GifByteType gifByteType;
px = (int *)((char *)px + info.stride * imageDesc.Top);
for(int y=imageDesc.Top;y<imageDesc.Top+imageDesc.Height;y++)
{
line=px;
for(int x=imageDesc.Left;x<imageDesc.Left+imageDesc.Width;x++)
{
pointPixel=(y-imageDesc.Top)*imageDesc.Width+(x-imageDesc.Left);
gifByteType=savedImage.RasterBits[pointPixel];
//需要给每个像素赋颜色
if(colorMapObject!=NULL)
{
gifColorType=colorMapObject->Colors[gifByteType];
line[x]=argb(255,gifColorType.Red,gifColorType.Green,gifColorType.Blue);
}
}
px = (int *)((char *)px + info.stride);
}
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_huozhenpeng_amdemo_MainActivity_updateFrame(JNIEnv *env, jobject instance,
jobject bitmap,jlong gifPoint) {
GifFileType * gifFileType= (GifFileType *) gifPoint;
GifBean *gifBean= (GifBean *) gifFileType->UserData;
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env,bitmap,&info);
void *pixels;
//锁定画布
AndroidBitmap_lockPixels(env,bitmap,&pixels);
//绘制图形
drawFrame(gifFileType,gifBean,info,pixels);
//控制当前播放量
gifBean->currentFrame+=1;
if(gifBean->currentFrame>=gifBean->totalFrame-1)
{
gifBean->currentFrame=0;
}
//解锁画布
AndroidBitmap_unlockPixels(env,bitmap);
return gifBean->dealys[gifBean->currentFrame];
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_huozhenpeng_amdemo_MainActivity_getWidth(JNIEnv *env, jobject instance,
jlong gifPoint) {
GifFileType *gifFileType= (GifFileType *) gifPoint;
return gifFileType->SWidth;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_huozhenpeng_amdemo_MainActivity_getHeight(JNIEnv *env, jobject instance,
jlong gifPoint) {
GifFileType *gifFileType= (GifFileType *) gifPoint;
return gifFileType->SHeight;
}
extern "C"
JNIEXPORT jlong JNICALL
Java_com_example_huozhenpeng_amdemo_MainActivity_loadGif(JNIEnv *env, jobject instance,
jstring path_) {
const char *path = env->GetStringUTFChars(path_, 0);
int error;
//Open a new GIF file for read, given by its name.
GifFileType *gifFileType = DGifOpenFileName(path, &error);
//This routine reads an entire GIF into core, hanging all its state info off the GifFileType pointer
DGifSlurp(gifFileType);
GifBean *gifBean= (GifBean *) malloc(sizeof(GifBean));
memset(gifBean,0, sizeof(GifBean));
gifBean->dealys= (int *) malloc(sizeof(int) * gifFileType->ImageCount);
memset(gifBean->dealys,0, sizeof(int)*gifFileType->ImageCount);
//在图形控制扩展块中的第5个字节和第6个字节存放的是每帧的延迟时间,单位是
// 1/100秒,而唯一能标识这是一个图形扩展块的是第2个字节,固定值0xF9
ExtensionBlock *extensionBlock;
//给gibBean的delays赋值,它将存放所有的帧的延迟时间
for(int i=0;i<gifFileType->ImageCount;i++)
{
//取出每一帧
SavedImage frame=gifFileType->SavedImages[i];
// Count of extensions before imag
//遍历扩展块,取出图形扩展块的表示的时间
for(int j=0;j<frame.ExtensionBlockCount;j++)
{
//#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */
if(GRAPHICS_EXT_FUNC_CODE==frame.ExtensionBlocks[j].Function)
{
extensionBlock= &frame.ExtensionBlocks[j];
break;
}
}
if(extensionBlock)
{
int delay=10*((extensionBlock->Bytes[2]<<8)|extensionBlock->Bytes[1]);
gifBean->dealys[i]=delay;
}
}
gifBean->totalFrame=gifFileType->ImageCount;
gifFileType->UserData=gifBean;
env->ReleaseStringUTFChars(path_, path);
return (jlong) gifFileType;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_huozhenpeng_amdemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
MainActivity.java
package com.example.huozhenpeng.amdemo;
import android.Manifest;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import java.io.File;
public class MainActivity extends AppCompatActivity {
private ImageView iv_gif;
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_gif= (ImageView) findViewById(R.id.iv_gif);
}
private int REQUEST_CAMERA_CODE=1;
private void requestCameraAccess() {
ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CAMERA_CODE);
}
public void rquestPermission(View view)
{
requestCameraAccess();
}
private Bitmap bitmap;
private int delay;
private long gifPoint;
public void play(View view)
{
File file = new File(Environment.getExternalStorageDirectory(), "dashaguai.gif");
gifPoint=loadGif(file.getAbsolutePath());
int width=getWidth(gifPoint);
int height=getHeight(gifPoint);
bitmap=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
delay=updateFrame(bitmap,gifPoint);
handler.sendEmptyMessageDelayed(0xa1,delay);
}
private Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
iv_gif.setImageBitmap(bitmap);
handler.sendEmptyMessageDelayed(0xa1,delay);
delay=updateFrame(bitmap,gifPoint);
}
};
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native long loadGif(String path);
public native int getWidth(long gifPoint);
public native int getHeight(long gifPoint);
public native int updateFrame(Bitmap bitmap,long gifPoint);
}
CMakeList.txt
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp
src/main/cpp/dgif_lib.c
src/main/cpp/egif_lib.c
src/main/cpp/gif_err.c
src/main/cpp/gif_hash.c
src/main/cpp/gifalloc.c
src/main/cpp/quantize.c
)
find_library( # Sets the name of the path variable.
jnigraphics-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
jnigraphics )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
${jnigraphics-lib}
)
注意这块
我们播放这两个gif文件
运行结果:
遇到的问题:
在播放demo.gif的时候可以正常播放,播放dashaguai.gif的时候无法
播放,debug的时候发现在播放dashaguai.gif的时候
ColorMapObject *colorMapObject=imageDesc.ColorMap;
这个colorMapObject是为null的,有的gif确实是获取不到,这个时候我们用全局的,修改代码
ColorMapObject *colorMapObject=imageDesc.ColorMap;
if(colorMapObject==NULL)
{
colorMapObject=gifFileType->SColorMap;
}