欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

在AS上用C语言(JNI方式)播放gif动图

程序员文章站 2023-12-31 14:37:46
Android中gif播放一般是比较耗内存的操作,Android中的ImageView不能直接播放gif,使用Java方式实现的gif播放的是非常耗内存的,就算是使用Glide这种优秀的三方库,也是一样的,所以项目中有gif播放需求,尤其是列表中有gif播放的,建议使用JNI的实现方式。创建工程先将基本的功能,例如申请权限,按钮点击等 用Java实现,activity_main.xml 如下:

Android中gif播放一般是比较耗内存的操作,Android中的ImageView不能直接播放gif,使用Java方式实现的gif播放的是非常耗内存的,就算是使用Glide这种优秀的三方库,也是一样的,所以项目中有gif播放需求,尤其是列表中有gif播放的,建议使用JNI的实现方式。

创建工程

先将基本的功能,例如申请权限,按钮点击等 用Java实现,activity_main.xml 如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="C方式加载Gif"
        android:gravity="center"
        android:onClick="ndkLoadGif"
        />
</LinearLayout>

接下来将android源码下的gif加载文件拷贝过来,底层的渲染加载都由这些库完成,我们主要实现获取gif的路径、宽高和渲染等逻辑

除了箭头两个是AS生成的,其他都是来自系统源码,Android源码中的版本号\external\giflib路径下。

 

在AS上用C语言(JNI方式)播放gif动图

 

另外还有一点要注意的是要将引用到的文件加到中CMakeLists.txt,不然有些方法拿不到会报错

在AS上用C语言(JNI方式)播放gif动图

接着在 GifHandler 类中放获取加载gif、获取宽高、渲染图片的方法 代码如下:

package com.xifei.gifdemo;

import android.graphics.Bitmap;

public class GifHandler {
    long gifHander;//地址   指针类型

    static {
        System.loadLibrary("native-lib");
    }

    public int getWidth() {
        return getWidth(gifHander);
    }

    public int getHeight() {
        return getHeight(gifHander);
    }

    public int updateFrame(Bitmap bitmap) {
        return updateFrame(gifHander, bitmap);
    }

    private GifHandler(long gifHander) {
        this.gifHander = gifHander;
    }

    public static GifHandler load(String path) {
        long gifHander = loadGif(path);
        GifHandler gifHandler = new GifHandler(gifHander);
        return gifHandler;
    }

    // 开始加载gif文件  Java+包名+类名+方法名  中间分隔用下划线
    public static native long loadGif(String path);

    // 宽
    public static native int getWidth(long gifHander);

    // 高
    public static native int getHeight(long gifPoint);

    // 渲染图片
    public static native int updateFrame(long gifPoint, Bitmap bitmap);

}
static {
    System.loadLibrary("native-lib");
}

将GifHandler 的方法和native-lib.cpp文件关联起来,

public static native long loadGif(String path);   对应  Java_com_xifei_gifdemo_GifHandler_loadGif(JNIEnv *env, jclass clazz, jstring path_) 方法

// 宽
public static native int getWidth(long gifHander);  对应    Java_com_xifei_gifdemo_GifHandler_getWidth(JNIEnv *env, jclass clazz, jlong gif_hander)

// 高
public static native int getHeight(long gifPoint);  对应   Java_com_xifei_gifdemo_GifHandler_getHeight(JNIEnv *env, jclass clazz, jlong gif_hander)

// 渲染图片
public static native int updateFrame(long gifPoint, Bitmap bitmap);   对应  Java_com_xifei_gifdemo_GifHandler_updateFrame(JNIEnv *env, jclass clazz, jlong gif_point,jobject bitmap)

主要的实现逻辑也是要在native-lib.cpp的方法中完成的,代码如下:

#include <jni.h>
#include <string>
#include <android/bitmap.h>
#include <malloc.h>
#include <string.h>

extern "C" {
#include "gif_lib.h"
}
//16个字节
struct GifBean {
    int current_frame;
    int total_frame;
    int *delays;
};

//解析图像
#define  argb(a, r, g, b) ( ((a) & 0xff) << 24 ) | ( ((b) & 0xff) << 16 ) | ( ((g) & 0xff) << 8 ) | ((r) & 0xff)
extern "C"
JNIEXPORT jlong JNICALL
Java_com_xifei_gifdemo_GifHandler_loadGif(JNIEnv *env, jclass clazz, jstring path_) {
    const char *path = env->GetStringUTFChars(path_, 0);
    int Error;//打开失败还是成功
    GifFileType *gifFileType = DGifOpenFileName(path, &Error);

    //初始化缓冲区 数组SaveImages
    DGifSlurp(gifFileType);
    GifBean *gifBean = static_cast<GifBean *>(malloc(sizeof(GifBean)));
    //重置,防止有脏数据
    memset(gifBean, 0, sizeof(GifBean));
    //赋值
    gifFileType->UserData = gifBean;
    gifBean->current_frame = 0;
    //总帧数
    gifBean->total_frame = gifFileType->ImageCount;

    //释放空间
    env->ReleaseStringUTFChars(path_, path);
    return reinterpret_cast<jlong>(gifFileType);

}

extern "C"
JNIEXPORT jint JNICALL
Java_com_xifei_gifdemo_GifHandler_getWidth(JNIEnv *env, jclass clazz, jlong gif_hander) {
    GifFileType *gifFileType = reinterpret_cast<GifFileType *>(gif_hander);
    return gifFileType->SWidth;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_xifei_gifdemo_GifHandler_getHeight(JNIEnv *env, jclass clazz, jlong gif_hander) {
    GifFileType *gifFileType = reinterpret_cast<GifFileType *>(gif_hander);
    return gifFileType->SHeight;
}

void drawFrame1(GifFileType *gifFileType, AndroidBitmapInfo info, void *pixels) {
    GifBean *gifBean = static_cast<GifBean *>(gifFileType->UserData);
    //获取到当前帧
    SavedImage savedImage = gifFileType->SavedImages[gifBean->current_frame];
    //图像分成两部分  像素   一部分是 描述
    GifImageDesc frameInfo = savedImage.ImageDesc;
    ColorMapObject *colorMapObject = frameInfo.ColorMap;
    //记录每一行的首地址
    int *px = (int *) pixels;
    //临时 索引
    int *line;
    //索引
    int pointPixel;
    GifByteType gifByteType;
    //解压
    GifColorType gifColorType;
    for (int y = frameInfo.Top; y < frameInfo.Top + frameInfo.Height; ++y) {
        //每次遍历行将首地址 传给line
        line = px;
        for (int x = frameInfo.Left; x < frameInfo.Left + frameInfo.Width; ++x) {
            // 定位像素  索引
            pointPixel = (y - frameInfo.Top) * frameInfo.Width + (x - frameInfo.Left);
            // 压缩的像素
            gifByteType = savedImage.RasterBits[pointPixel];
            gifColorType = colorMapObject->Colors[gifByteType];
            //line 进行复制   0  255  屏幕有颜色 line
            line[x] = argb(255, gifColorType.Red, gifColorType.Green, gifColorType.Blue);
        }
        //遍历条件 转到下一行
        px = (int *) ((char *) px + info.stride);
    }
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_xifei_gifdemo_GifHandler_updateFrame(JNIEnv *env, jclass clazz, jlong gif_point,
                                              jobject bitmap) {
    //获取bitmap
    GifFileType *gifFileType = reinterpret_cast<GifFileType *>(gif_point);
    //第一种获取bitmap、宽高的方法
    int width = gifFileType->SWidth;
    int height = gifFileType->SHeight;
    //第二种获取bitmap、宽高的方法
    AndroidBitmapInfo info;
    AndroidBitmap_getInfo(env, bitmap, &info);
    width = info.width;
    height = info.height;

    //获取bitmap 相当于二维数组(万物皆为数组)
    void *pixels;
    //锁住当前bitmap
    AndroidBitmap_lockPixels(env, bitmap, &pixels);
    //绘制
    drawFrame1(gifFileType, info, pixels);
    AndroidBitmap_unlockPixels(env, bitmap);

    GifBean *gifBean = static_cast<GifBean *>(gifFileType->UserData);
    //帧数移动
    gifBean->current_frame++;
    if (gifBean->current_frame >= gifBean->total_frame - 1) {
        gifBean->current_frame = 0;
    }
    return 100;
}

MainActivity的代码如下:
package com.xifei.gifdemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.app.Activity;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import java.io.File;

public class MainActivity extends AppCompatActivity {
    Bitmap bitmap;
    GifHandler gifHandler;
    ImageView image;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        verifyStoragePermissions(this);
        image= (ImageView) findViewById(R.id.image);
    }

    Handler myHandler = new Handler() {
        public void handleMessage(Message msg) {
            int delay=gifHandler.updateFrame(bitmap);
            myHandler.sendEmptyMessageDelayed(1,delay);
            image.setImageBitmap(bitmap);
//            Object
        }
    };
    public void verifyStoragePermissions(Activity activity) {
        int REQUEST_EXTERNAL_STORAGE = 1;
        String[] PERMISSIONS_STORAGE = {
                "android.permission.READ_EXTERNAL_STORAGE",
                "android.permission.WRITE_EXTERNAL_STORAGE" };
        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void ndkLoadGif(View view) {
        File file=new File(Environment.getExternalStorageDirectory(),"demo.gif");
        Log.e("xifei >>","-------"+file.getAbsolutePath());
        gifHandler = GifHandler.load(file.getAbsolutePath());
        Log.e("xifei >>","gifHandler -------"+gifHandler);

        int width=gifHandler.getWidth();
        int height=gifHandler.getHeight();
        Log.i("xifei >>","宽   "+width+"   高  "+height);
        bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
        //C  通知C渲染完成
        int delay= gifHandler.updateFrame(bitmap);
        Log.e("xifei >>","delay  "+delay);
        image.setImageBitmap(bitmap);

        myHandler.sendEmptyMessageDelayed(1, delay);
        View view1;

    }
}

项目源码下载:

https://download.csdn.net/download/xifei66/13191665

 

 

本文地址:https://blog.csdn.net/xifei66/article/details/110163884

上一篇:

下一篇: