android使用gif首选当然是android-gif-drawable了,不过根据需求,我要用c语言来显示gif,我前边有文章用Nsgif来实现,最近研究libgif,就写了个demo,首先找了个有libgif的开源项目gifdrawable-android,接下来把libgif代码复制到自己的demo里
修改CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1) add_library( native-lib SHARED src/main/cpp/native-lib.cpp src/main/cpp/gif/dgif_lib.c src/main/cpp/gif/gifalloc.c src/main/cpp/gif/gif_err.c ) find_library( log-lib log ) target_link_libraries( native-lib jnigraphics ${log-lib} )
新建GifHandler.java
public class GifHandler { static { System.loadLibrary("native-lib"); } private long gifAddr; public GifHandler(String path) { this.gifAddr = loadPath(path); } private native long loadPath(String path); private native int getWidth(long ndkGif); private native int getHeight(long ndkGif); private native int getLength(long ndkGif); private native int updateFrame(Bitmap bitmap,int index, long ndkGif); private native void recycleGif(long ndkGif); public int getWidth() { return getWidth(gifAddr); } public int getHeight() { return getHeight(gifAddr); } public int getLength(){ return getLength(gifAddr); } public int updateFrame(Bitmap bitmap,int index) { return updateFrame(bitmap,index, gifAddr); } }
native-lib.cpp
extern "C" JNIEXPORT GifFileType* JNICALL Java_com_example_libgif_GifHandler_loadPath(JNIEnv *env, jobject instance, jstring path_) { const char *path = env->GetStringUTFChars(path_, 0); // TODO int err; GifFileType* gif = DGifOpenFileName(path,&err); err = DGifSlurp(gif); env->ReleaseStringUTFChars(path_, path); return gif; }extern "C" JNIEXPORT jint JNICALL Java_com_example_libgif_GifHandler_getWidth__J(JNIEnv *env, jobject instance, jlong ndkGif) { // TODO return ((GifFileType *) ndkGif)->SWidth; }extern "C" JNIEXPORT jint JNICALL Java_com_example_libgif_GifHandler_getHeight__J(JNIEnv *env, jobject instance, jlong ndkGif) { // TODO return ((GifFileType *) ndkGif)->SHeight; }extern "C" JNIEXPORT jint JNICALL Java_com_example_libgif_GifHandler_getLength__J(JNIEnv *env, jobject instance, jlong ndkGif) { // TODO GifFileType *gif_handle = (GifFileType *) ndkGif; return gif_handle->ImageCount; } #define argb(a,r,g,b) ( ((a) & 0xff) << 24 ) | ( ((b) & 0xff) << 16 ) | ( ((g) & 0xff) << 8 ) | ((r) & 0xff) #define dispose(ext) (((ext)->Bytes[0] & 0x1c) >> 2) #define trans_index(ext) ((ext)->Bytes[3]) #define transparency(ext) ((ext)->Bytes[0] & 1) #define delay(ext) (10*((ext)->Bytes[2] << 8 | (ext)->Bytes[1])) int drawFrame(GifFileType* gif, AndroidBitmapInfo* info, int* pixels, int frame_no, bool force_dispose_1) { GifColorType *bg; GifColorType *color; SavedImage * frame; ExtensionBlock * ext = 0; GifImageDesc * frameInfo; ColorMapObject * colorMap; int *line; int width, height,x,y,j,loc,n,inc,p; int* px; width = gif->SWidth; height = gif->SHeight; frame = &(gif->SavedImages[frame_no]); frameInfo = &(frame->ImageDesc); if (frameInfo->ColorMap) { colorMap = frameInfo->ColorMap; } else { colorMap = gif->SColorMap; } bg = &colorMap->Colors[gif->SBackGroundColor]; for (j=0; j<frame->ExtensionBlockCount; j++) { if (frame->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) { ext = &(frame->ExtensionBlocks[j]); break; } } // For dispose = 1, we assume its been drawn px = pixels; if (ext && dispose(ext) == 1 && force_dispose_1 && frame_no > 0) { drawFrame(gif, info, pixels, frame_no-1, true); } else if (ext && dispose(ext) == 2 && bg) { for (y=0; y<height; y++) { line = (int*) px; for (x=0; x<width; x++) { line[x] = argb(255, bg->Red, bg->Green, bg->Blue); } px = (int *) ((char*)px + info->stride); } } else if (ext && dispose(ext) == 3 && frame_no > 1) { drawFrame(gif, info, pixels, frame_no-2, true); } px = pixels; if (frameInfo->Interlace) { n = 0; inc = 8; p = 0; px = (int *) ((char*)px + info->stride * frameInfo->Top); for (y=frameInfo->Top; y<frameInfo->Top+frameInfo->Height; y++) { for (x=frameInfo->Left; x<frameInfo->Left+frameInfo->Width; x++) { loc = (y - frameInfo->Top)*frameInfo->Width + (x - frameInfo->Left); if (ext && frame->RasterBits[loc] == trans_index(ext) && transparency(ext)) { continue; } color = (ext && frame->RasterBits[loc] == trans_index(ext)) ? bg : &colorMap->Colors[frame->RasterBits[loc]]; if (color) line[x] = argb(255, color->Red, color->Green, color->Blue); } px = (int *) ((char*)px + info->stride * inc); n += inc; if (n >= frameInfo->Height) { n = 0; switch(p) { case 0: px = (int *) ((char *)pixels + info->stride * (4 + frameInfo->Top)); inc = 8; p++; break; case 1: px = (int *) ((char *)pixels + info->stride * (2 + frameInfo->Top)); inc = 4; p++; break; case 2: px = (int *) ((char *)pixels + info->stride * (1 + frameInfo->Top)); inc = 2; p++; } } } } else { px = (int *) ((char*)px + info->stride * frameInfo->Top); for (y=frameInfo->Top; y<frameInfo->Top+frameInfo->Height; y++) { line = (int*) px; for (x=frameInfo->Left; x<frameInfo->Left+frameInfo->Width; x++) { loc = (y - frameInfo->Top)*frameInfo->Width + (x - frameInfo->Left); if (ext && frame->RasterBits[loc] == trans_index(ext) && transparency(ext)) { continue; } color = (ext && frame->RasterBits[loc] == trans_index(ext)) ? bg : &colorMap->Colors[frame->RasterBits[loc]]; if (color) line[x] = argb(255, color->Red, color->Green, color->Blue); } px = (int *) ((char*)px + info->stride); } } return delay(ext); } extern "C" JNIEXPORT jint JNICALL Java_com_example_libgif_GifHandler_updateFrame__Landroid_graphics_Bitmap_2IJ(JNIEnv *env, jobject instance, jobject bitmap, jint index, jlong ndkGif) { // TODO GifFileType *gif_handle = (GifFileType *) ndkGif; AndroidBitmapInfo info; void* pixels; int ret; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { return -1; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { return -1; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { return -1; } int delay_time = drawFrame(gif_handle, &info, (int*)pixels,index, false); AndroidBitmap_unlockPixels(env, bitmap); return delay_time; }extern "C" JNIEXPORT void JNICALL Java_com_example_libgif_GifHandler_recycleGif(JNIEnv *env, jobject instance, jlong ndkGif) { // TODO GifFileType* gif_handle = (GifFileType*)ndkGif; free(gif_handle->UserData); DGifCloseFile(gif_handle); }
MainActivity.java
public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. private String[] denied; private String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < permissions.length; i++) { if (PermissionChecker.checkSelfPermission(this, permissions[i]) == PackageManager.PERMISSION_DENIED) { list.add(permissions[i]); } } if (list.size() != 0) { denied = new String[list.size()]; for (int i = 0; i < list.size(); i++) { denied[i] = list.get(i); ActivityCompat.requestPermissions(this, denied, 5); } } else { init(); } }else{ init(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == 5) { boolean isDenied = false; for (int i = 0; i < denied.length; i++) { String permission = denied[i]; for (int j = 0; j < permissions.length; j++) { if (permissions[j].equals(permission)) { if (grantResults[j] != PackageManager.PERMISSION_GRANTED) { isDenied = true; break; } } } } if (isDenied) { Toast.makeText(this, "请开启权限", Toast.LENGTH_SHORT).show(); } else { init(); } } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } private ImageView imageView; private GifHandler gifHandler; private Bitmap bitmap; private int index = 0; private final Object object = new Object(); private boolean isStart = false; private void init() { imageView = findViewById(R.id.gif_view); String s = Environment.getExternalStorageDirectory().getAbsolutePath(); final File file = new File(s+"/HMSDK/ad/2/", "人人车.gif"); if (file != null && file.exists()) { new Thread() { @Override public void run() { super.run(); gifHandler = new GifHandler(file.getAbsolutePath()); int width = gifHandler.getWidth(); int height = gifHandler.getHeight(); bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); while (index >= 0){ int nextFrame = gifHandler.updateFrame(bitmap,index); index++; if(index >= gifHandler.getLength()){ index = 0; } synchronized (object){ try { if(isStart){ object.wait(nextFrame); }else{ object.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); } } }.start(); } else { Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show(); } } public void startGif(View view){ if(!isStart){ isStart = true; synchronized (object){ object.notifyAll(); } } } public void stopGif(View view){ if(isStart){ isStart = false; } } }
效果
密码:7u1m