目前gif動畫的加載庫有很多,比如:gifView,Glide等都可以進行giff動畫的加載,但是用這些gif加載較大的gif時就有可能出現ooM。所以今天我們用系統的giflib庫進行gif的加載去避免ooM的出現,在性能上也要比這些gif的加載庫要高。
首先這個giflib這個庫是我們可以到系統原碼里面找搜索giflib就可以找到這個gif庫了
找到這個gif庫打開就是c的代碼。
我們拿到一個gif的動畫圖片怎么打開呢?這時我們就要用到系統中的gif_lib.c中的API了,在gif_lib中的有一個方法DGifOpenFileName()就是打開gif動畫讀取gif的信息的。
//GifFileType是一個保存gif信息的結構體
GifFileType *gifFileType = DGifOpenFileName(gifpath,&err);//調用系統的gif_lib.c的api打開gif,返回的是GifFileType的結構體
dGifSlurp(gifFileType);//進行gif的初始化,拿到gif的詳細信息
備注:GifFlieType是一個保存gif天動畫的結構體,里面保存著gif的寬高和總幀數等信息
利用DGifOpenFileName()打開gif后我們要對gif信息進行初始化,也是利用gif_lib.c的方法進行初始化,該方法是dGifSlurp();需要傳入GifFlieType進行初始化,調用完這個方法后我們的gif動畫的信息就讀取到GifFlieType的這個結構體中保存了。
下面要創建一個保存gif相關信息的結構體保存從GifFlieType讀出的信息
typedef struct GifBean{
//總時間
int total_time;
//當前幀
int current_frame;
//每一幀時間
int frame_duration;
//總幀數
int total_frame;
}GifBean;
//初始化一下GifBean保存gif的信息,為GifBean開劈一個內存空間
GifBean *gifBean = (GifBean *) malloc(sizeof(GifBean));
gifBean->frame_duration = 0;
gifBean->current_frame = 0;
gifBean->total_frame = 0;
gifBean->total_time = 0;
gifFileType->UserData = gifBean;//這里是設置一下tag,相當于給view設置一個tag后面獲取寬高時會用到
下面我就要對這個GIfBean的結構體進行賦值保存信息
int i, j, frame_delay;
SavedImage *frame;//這個是系統中保存gif的擴展塊的信息,不太了解gif的文件結構的可以看一下:http://blog.csdn.net/wzy198852/article/details/17266507
ExtensionBlock *ext;//這個保存gif延時代碼塊的結構體
//遍歷拿到每一幀時間,總時間,延遲時間
for (i+0;i<gifFileType->ImageCount;i++){
frame = &gifFileType->SavedImages[i];//拿到每個一個圖片相關信息
for (j = 0;j<frame->ExtensionBlockCount; j++) {
//找到含有延遲時間的代碼塊
if(frame->ExtensionBlocks[j].Function==GRAPHICS_EXT_FUNC_CODE){
ext = &(frame->ExtensionBlocks[j]);
}
}
//拿到延遲時間
if(ext){
//延遲時間1-->10ms
frame_delay = 10*(ext->Bytes[2]<<8 | ext->Bytes[1]);//拿到延遲時間
//拿到總時間
gifBean->total_time +=frame_delay;
}
}
//每一幀時間,拿總時間除總幀數
gifBean->frame_duration = gifBean->total_time/gifFileType->ImageCount;
//總幀數
gifBean->total_frame = gifFileType->ImageCount;
這里就已經完成gif動畫的加載。
gif已經加載完了那個怎么繪制到頁面上顯示呢?
下面我們來實現gif的繪制
GifFileType *gifFileType = (GifFileType *) gifHelper;
GifBean *gifBean = gifFileType->UserData;
AndroidBitmapInfo info;//這個Android中保存Bitmap信息的結構體
void *pixels; //代表圖片的像素數組
//給AndroidBitamapInfo賦值
AndroidBitmap_getInfo(env,bitmap,&info);
//鎖定bitmap,圖片是二維數組
AndroidBitmap_lockPixels(env,bitmap,&pixels);
//繪制每一幀
drawFrame(gifFileType,&info,(int *)pixels,gifBean->current_frame,false);
//繪制完當前幀,讓當前針+1
gifBean->current_frame+=1;
//判斷是不是最后一幀
if(gifBean->current_frame==gifBean->total_frame){
gifBean->current_frame = 0;
}
//最后解鎖圖片
AndroidBitmap_unlockPixels(env,bitmap);
這樣就可以把gif繪制到頁面上了
下面是具體代碼實現
#include "xiaowei_gifload_com_gifdemo_GifHelper.h"
#include "gif_lib.h"
#include <stdio.h>
#include <stdlib.h>
#include <android/bitmap.h>
//如果對gif信息不了解可以看一下http://blog.csdn.net/wzy198852/article/details/17266507
typedef struct GifBean{
//總時間
int total_time;
//當前幀
int current_frame;
//每一幀時間
int frame_duration;
//總幀數
int total_frame;
}GifBean;
JNIEXPORT jlong JNICALL Java_xiaowei_gifload_com_gifdemo_GifHelper_GifLoad
(JNIEnv * env, jclass cls, jstring giffile){
char * gifpath = (char *) env->GetStringChars(giffile, false);//gif文件路徑
int err;
//GifFileType是一個保存gif信息的結構體
GifFileType *gifFileType = DGifOpenFileName(gifpath,&err);//調用系統的gif_lib.c的api打開gif,返回的是GifFileType的結構體
dGifSlurp(gifFileType);//進行gif的初始化,拿到gif的詳細信息
//初始化一下GifBean保存gif的信息,為GifBean開劈一個內存空間
GifBean *gifBean = (GifBean *) malloc(sizeof(GifBean));
gifBean->frame_duration = 0;
gifBean->current_frame = 0;
gifBean->total_frame = 0;
gifBean->total_time = 0;
gifFileType->UserData = gifBean;//這里是設置一上tag,相當于給view設置一個tag
//給gifbean成員變量賦值,得到當前播放時間的總時長;
int i, j, frame_delay;
SavedImage *frame;//這個是系統中保存gif圖片個數的結構體
ExtensionBlock *ext;//這個保存gif延時代碼塊的結構體
//遍歷拿到每一幀時間,總時間,延遲時間
for (i+0;i<gifFileType->ImageCount;i++){
frame = &gifFileType->SavedImages[i];//拿到每個一個圖片相關信息
for (j = 0;j<frame->ExtensionBlockCount; j++) {
//找到含有延遲時間的代碼塊
if(frame->ExtensionBlocks[j].Function==GRAPHICS_EXT_FUNC_CODE){
ext = &(frame->ExtensionBlocks[j]);
}
}
//拿到延遲時間
if(ext){
//延遲時間1-->10ms
frame_delay = 10*(ext->Bytes[2]<<8 | ext->Bytes[1]);//拿到延遲時間
//拿到總時間
gifBean->total_time +=frame_delay;
}
}
//每一幀時間,拿總時間除總幀數
gifBean->frame_duration = gifBean->total_time/gifFileType->ImageCount;
//總幀數
gifBean->total_frame = gifFileType->ImageCount;
return (long long) gifFileType;
}
//參數gifHepler是GifLoad返回的long類型
JNIEXPORT jint JNICALL Java_xiaowei_gifload_com_gifdemo_GifHelper_getWidth
(JNIEnv * env, jclass cls, jlong gifHelper){
GifFileType *gifFileType = (GifFileType *) gifHelper;
return gifFileType->SWidth;
}
JNIEXPORT jint JNICALL Java_xiaowei_gifload_com_gifdemo_GifHelper_getHeight
(JNIEnv * env, jclass cls, jlong gifHelper) {
GifFileType *gifFileType = (GifFileType *) gifHelper;
return gifFileType->SHeight;
}
JNIEXPORT jint JNICALL Java_xiaowei_gifload_com_gifdemo_GifHelper_NextTime
(JNIEnv * env, jclass cls, jlong gifHelper){
GifFileType *gifFileType = (GifFileType *) gifHelper;
GifBean *gifBean = (GifBean *) gifFileType->UserData;
return gifBean->frame_duration;
}
JNIEXPORT jint JNICALL Java_xiaowei_gifload_com_gifdemo_GifHelper_updateFrame
(JNIEnv * env, jclass cls, jlong gifHelper,jobject bitmap){
GifFileType *gifFileType = (GifFileType *) gifHelper;
GifBean *gifBean = gifFileType->UserData;
AndroidBitmapInfo info;//這個Android中保存Bitmap信息的結構體
void *pixels; //代表圖片的像素數組
//給AndroidBitamapInfo賦值
AndroidBitmap_getInfo(env,bitmap,&info);
//鎖定bitmap,圖片是二維數組
AndroidBitmap_lockPixels(env,bitmap,&pixels);
//繪制每一幀
drawFrame(gifFileType,&info,(int *)pixels,gifBean->current_frame,false);
//繪制完當前幀,讓當前針+1
gifBean->current_frame+=1;
//判斷是不是最后一幀
if(gifBean->current_frame==gifBean->total_frame){
gifBean->current_frame = 0;
}
//最后解鎖圖片
AndroidBitmap_unlockPixels(env,bitmap);
return gifBean->frame_duration;
}
如果需要系統原碼的可以聯系我,同時如果表示上面有錯的地方也可以向我提出來,大家共同學習進步。