一、GIF 的文件格式
GIF 实际是一种压缩文件,采用 LZW 压缩算法进行编码。GIF 格式的文件结构整体上分为文件头,GIF 数据流和文件结尾。所有的颜色由一个全局颜色列表管理,当 GIF 要显示颜色的时候,会通过索引查找颜色列表中的值.
[图片上传失败...(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 {
// 当前播放的帧
int current_frame;
// 总的帧数
int total_frame;
// 每一帧的间隔时间 指针数组
int *dealys;
};
/**
* 绘制一帧图像
* @param gifFileType
* @param gifBean
* @param info
* @param pixels
*/
void drawFrame(GifFileType *gifFileType, GifBean *gifBean, AndroidBitmapInfo info, void *pixels) {
......
// 当前帧的图像信息
GifImageDesc imageInfo = savedImage.ImageDesc;
// 拿到图像数组的首地址
int *px = (int *) pixels;
// 图像颜色表
ColorMapObject *colorMap = imageInfo.ColorMap;
if (colorMap == nullptr) {
colorMap = gifFileType->SColorMap;
}
// y 方向偏移量
px = (int *) ((char *) px + info.stride * imageInfo.Top);
// 像素点的位置
int pointPixel;
GifByteType gifByteType;//压缩数据
// 每一行的首地址
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 压缩算法拿到当前数组的值
gifByteType = savedImage.RasterBits[pointPixel];
GifColorType gifColorType = colorMap->Colors[gifByteType];
// 将 color type 转换成 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;
// 根据路径获取 Gif 文件的结构
GifFileType *gifFileType = DGifOpenFileName(path, &errorCode);
// Gif 结构初始化,会填充上面读取的内容到 GifFileType 对象中
DGifSlurp(gifFileType);
// 给结构体 分配内存空间
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) {
// 拿到图形控制拓展块和当前帧的延时时间
// 因为它的 Bytes 是小端字节序 延时单位是 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;
// 把这个数据交给 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 是创建一个像素数组,用来装 Gif 中的像素元素
void *pixels;
AndroidBitmap_lockPixels(env, bitmap, &pixels);
drawFrame(gifFileType, gifBean, info, pixels);
gifBean->current_frame += 1; // 绘制当前帧后加 1
// 如果当前帧已经是最后一帧,代表它已经播完了,继续播放
......
AndroidBitmap_unlockPixels(env, bitmap);
return gifBean->dealys[gifBean->current_frame];
}
网友评论