美文网首页
使用 Gif Lib 加载 Gif 图

使用 Gif Lib 加载 Gif 图

作者: 石器时代小古董 | 来源:发表于2020-02-05 19:50 被阅读0次

一、GIF 的文件格式

What's a GIF 文章

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];
}

相关文章

网友评论

      本文标题:使用 Gif Lib 加载 Gif 图

      本文链接:https://www.haomeiwen.com/subject/dshexhtx.html