一允坚、GIF 的文件格式
GIF 實(shí)際是一種壓縮文件,采用 LZW 壓縮算法進(jìn)行編碼。GIF 格式的文件結(jié)構(gòu)整體上分為文件頭,GIF 數(shù)據(jù)流和文件結(jié)尾铐维。所有的顏色由一個(gè)全局顏色列表管理柬泽,當(dāng) GIF 要顯示顏色的時(shí)候,會(huì)通過索引查找顏色列表中的值.
[圖片上傳失敗...(image-e5d5e5-1580903437592)]
[圖片上傳失敗...(image-fbb60f-1580903437592)]
二嫁蛇、集成 GIF LIB
將 android 自帶的 gif_lib c 文件集成到工程中
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. src_files)
add_library( # Sets the name of the library.
native-lib
SHARED
src/main/cpp/gif_main.cpp
${src_files}
)
find_library(
jnigraphics-lib
jnigraphics)
target_link_libraries(native-lib
log
${jnigraphics-lib}
)
native 代碼
//
// Created by Apple on 2020-02-04.
//
#include <jni.h>
#include <string>
#include "gif_lib.h"
#include <android/log.h>
#include <android/bitmap.h>
#define LOG_TAG "GifDecoder"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define argb(a, r, g, b) ( ((a) & 0xff) << 24 ) | ( ((b) & 0xff) << 16 ) | ( ((g) & 0xff) << 8 ) | ((r) & 0xff)
typedef struct GifBean {
// 當(dāng)前播放的幀
int current_frame;
// 總的幀數(shù)
int total_frame;
// 每一幀的間隔時(shí)間 指針數(shù)組
int *dealys;
};
/**
* 繪制一幀圖像
* @param gifFileType
* @param gifBean
* @param info
* @param pixels
*/
void drawFrame(GifFileType *gifFileType, GifBean *gifBean, AndroidBitmapInfo info, void *pixels) {
......
// 當(dāng)前幀的圖像信息
GifImageDesc imageInfo = savedImage.ImageDesc;
// 拿到圖像數(shù)組的首地址
int *px = (int *) pixels;
// 圖像顏色表
ColorMapObject *colorMap = imageInfo.ColorMap;
if (colorMap == nullptr) {
colorMap = gifFileType->SColorMap;
}
// y 方向偏移量
px = (int *) ((char *) px + info.stride * imageInfo.Top);
// 像素點(diǎn)的位置
int pointPixel;
GifByteType gifByteType;//壓縮數(shù)據(jù)
// 每一行的首地址
int *line;
for (int y = imageInfo.Top; y < imageInfo.Top + imageInfo.Height; ++y) {
line = px;
for (int x = imageInfo.Left; x < imageInfo.Left + imageInfo.Width; ++x) {
pointPixel = (y - imageInfo.Top) * imageInfo.Width + (x - imageInfo.Left);
// 通過 LWZ 壓縮算法拿到當(dāng)前數(shù)組的值
gifByteType = savedImage.RasterBits[pointPixel];
GifColorType gifColorType = colorMap->Colors[gifByteType];
// 將 color type 轉(zhuǎn)換成 argb 的值
line[x] = argb(255, gifColorType.Red, gifColorType.Green, gifColorType.Blue);
}
// 更新到下一行
px = (int *) ((char *) px + info.stride);
}
}
extern "C"
JNIEXPORT jlong JNICALL
Java_com_baidu_crazyorange_gifdecoder_gif_GifDecoder_loadGIF(JNIEnv *env, jclass type,
jstring path_) {
const char *path = env->GetStringUTFChars(path_, 0);
int errorCode = 0;
// 根據(jù)路徑獲取 Gif 文件的結(jié)構(gòu)
GifFileType *gifFileType = DGifOpenFileName(path, &errorCode);
// Gif 結(jié)構(gòu)初始化锨并,會(huì)填充上面讀取的內(nèi)容到 GifFileType 對象中
DGifSlurp(gifFileType);
// 給結(jié)構(gòu)體 分配內(nèi)存空間
GifBean *gifBean = (GifBean *) malloc(sizeof(GifBean));
memset(gifBean, 0, sizeof(GifBean));
/**
* 給 GifBean 賦值
*/
.....
// 圖形拓展塊
ExtensionBlock *extensionBlock;
for (int i = 0; i < gifFileType->ImageCount; ++i) {
// 取出 GIF 中的每一幀
SavedImage frame = gifFileType->SavedImages[i];
// 拿到每一幀的拓展塊
for (int j = 0; j < frame.ExtensionBlockCount; ++j) {
if (frame.ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) {
extensionBlock = &frame.ExtensionBlocks[j];
break;
}
if (extensionBlock) {
// 拿到圖形控制拓展塊和當(dāng)前幀的延時(shí)時(shí)間
// 因?yàn)樗?Bytes 是小端字節(jié)序 延時(shí)單位是 10 ms
// Bytes[0] 是保留字段
// Bytes[1] 是低 8 位
// Bytes[2] 表示高 8 位
int frame_delay = (extensionBlock->Bytes[2] << 8 | extensionBlock->Bytes[1]) * 10;
gifBean->dealys[i] = frame_delay;
}
}
}
gifBean->total_frame = gifFileType->ImageCount;
// 把這個(gè)數(shù)據(jù)交給 gifFileType 保存
gifFileType->UserData = gifBean;
env->ReleaseStringUTFChars(path_, path);
return (jlong) gifFileType;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_baidu_crazyorange_gifdecoder_gif_GifDecoder_getGifWidth(JNIEnv *env, jclass type,
jlong gifPointer) {
GifFileType *gifFileType = (GifFileType *) (gifPointer);
return gifFileType->SWidth;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_baidu_crazyorange_gifdecoder_gif_GifDecoder_getGifHeight(JNIEnv *env, jclass type,
jlong gifPointer) {
GifFileType *gifFileType = (GifFileType *) (gifPointer);
return gifFileType->SHeight;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_baidu_crazyorange_gifdecoder_gif_GifDecoder_displayGif(JNIEnv *env, jclass type,
jobject bitmap, jlong gifPointer) {
GifFileType *gifFileType = (GifFileType *) (gifPointer);
GifBean *gifBean = (GifBean *) gifFileType->UserData;
// 使用 AndroidBitmap 渲染 Bitmap
AndroidBitmapInfo info;
AndroidBitmap_getInfo(env, bitmap, &info);
// 鎖定 Bitmap pixels 是創(chuàng)建一個(gè)像素?cái)?shù)組,用來裝 Gif 中的像素元素
void *pixels;
AndroidBitmap_lockPixels(env, bitmap, &pixels);
drawFrame(gifFileType, gifBean, info, pixels);
gifBean->current_frame += 1; // 繪制當(dāng)前幀后加 1
// 如果當(dāng)前幀已經(jīng)是最后一幀睬棚,代表它已經(jīng)播完了琳疏,繼續(xù)播放
......
AndroidBitmap_unlockPixels(env, bitmap);
return gifBean->dealys[gifBean->current_frame];
}