使用 Gif Lib 加载 Gif 图

一、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];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 版本记录 前言 只要是做图片的或者与图片相关的,那么图片的格式就是一个不可以绕过的问题,我们见过很多的图片格式,但...
    刀客传奇阅读 5,091评论 0 7
  • 前言 本文参考gif 格式图片详细解析。加入了一些自己的理解和解析方面的示例。 GIF格式解析 图像互换格式(GI...
    oceanLong阅读 16,982评论 4 26
  • [[图片上传失败...(image-cae8e3-1541926581609)]](file://C:\Users...
    沉默的大多数1876阅读 9,015评论 0 12
  • 身边的朋友都考了科二,自己特别害怕报名,也害怕考试,但是又不能不考 头一次对考试这件事这么紧张,以前在学校每周都有...
    陈培岩阅读 165评论 1 0
  • 窗外的风轻柔,有些许的阳光透进房间,条线似的跟琴弦一样。风,无色无味,阳光的琴弦也奏不出声音,他发掘所有的潜能,努...
    疯哥哥l阅读 4,226评论 115 80