学习目标:
NDKGIF-MASTR(github项目注释)
ps:这个项目是别人的,不是博主的,博主只是做个阅读,做了些注释,要是有问题话,欢迎留言。
此次学习github的项目链接
此次学习项目有注释后链接
学习内容:
类名 | 作用 |
---|---|
giflib | gif解析库 |
GifJni.cpp | Java层调用此类 |
GifPlayer.cpp | gif解析核心类 |
Pthread.cpp | 控制当前线程休眠与唤醒 |
SyncTime.cpp | 时间同步计算类 |
log.h | 日志类 |
GifJni.cpp
介绍其中的一些方法吧,其他的也就是调用方法而已,下面的方法是属于动态注册
//jni方法映射表
JNINativeMethod method[] = {
{"native_create", "()J", (void *) create_jni},
{"native_load", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z", (void *) load_jni},
{"native_start", "(JZLandroid/graphics/Bitmap;Ljava/lang/Runnable;)V", (void *) start_jni},
{"native_pause", "(J)V", (void *) pause_jni},
{"native_resume", "(J)V", (void *) resume_jni},
{"native_get_width", "(J)I", (void *) get_width_jni},
{"native_get_height", "(J)I", (void *) get_height_jni},
{"native_stop", "(J)V", (void *) stop_jni},
{"native_release", "(J)V", (void *) release_jni},
};
//注册
jint registerNativeMethod(JNIEnv *env) {
jclass cl = env->FindClass("com/dming/testgif/GifPlayer");
if ((env->RegisterNatives(cl, method, sizeof(method) / sizeof(method[0]))) < 0) {
return -1;
}
return 0;
}
//注销
jint unRegisterNativeMethod(JNIEnv *env) {
jclass cl = env->FindClass("com/dming/testgif/GifPlayer");
env->UnregisterNatives(cl);
return 0;
}
//下面的方法有jni自动调用(动态注册)
//自动注册方法
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
registerNativeMethod(env);
return JNI_VERSION_1_6;
} else if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) == JNI_OK) {
registerNativeMethod(env);
return JNI_VERSION_1_4;
}
return JNI_ERR;
}
//自动注销方法
JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
unRegisterNativeMethod(env);
} else if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) == JNI_OK) {
unRegisterNativeMethod(env);
}
}
GifPlayer.cpp
这个就是核心播放方法,你会发现有两个循环方法
这个第一个循环方法播放是第一次边解析边放gif,接着就第二次循环方法不断循环播放第一次解析的gif数据,进行不断播放。
void GifPlayer::playGif(JNIEnv *env, bool once, jobject bitmap, jobject runnable) {
int i, j, Row, Col, Width, Height, ExtCode;
GifRecordType RecordType;
GifByteType *Extension;
GifRowType *ScreenBuffer;
int InterlacedOffset[] = {0, 4, 2, 1}; /* The way Interlaced image should. */
int InterlacedJumps[] = {8, 8, 4, 2}; /* be read - offsets and jumps... */
int ImageNum = 0;
int Error;
GifByteType *GifExtension;
ColorMapObject *ColorMap;
size_t size;
SavedImage *sp = NULL;
int32_t delayTime = 0;
unsigned int dt = 0;
int32_t *user_image_data;
//图片信息
AndroidBitmapInfo bitmapInfo;
int bitmapWidth;
//设置播放状态为正在播放
setPlayState(PLAYING);
AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
bitmapWidth = bitmapInfo.stride / 4;
//反射run函数
jclass runClass = env->GetObjectClass(runnable);
jmethodID runMethod = env->GetMethodID(runClass, "run", "()V");
if ((ScreenBuffer = (GifRowType *)
malloc(gifFile->SHeight * sizeof(GifRowType))) == NULL) {
LOGI("Failed to allocate memory required, aborted.");
goto end;
}
size = gifFile->SWidth * sizeof(GifPixelType);
if ((ScreenBuffer[0] = (GifRowType) malloc(size)) == NULL) {
LOGE("Failed to allocate memory required, aborted.");
goto end;
}
GifPixelType *buffer;
buffer = (GifPixelType *) (ScreenBuffer[0]);
for (i = 0; i < gifFile->SWidth; i++)
buffer[i] = (GifPixelType) gifFile->SBackGroundColor;
for (i = 1; i < gifFile->SHeight; i++) {
if ((ScreenBuffer[i] = (GifRowType) malloc(size)) == NULL) {
LOGI("Failed to allocate memory required, aborted.");
goto end;
}
memcpy(ScreenBuffer[i], ScreenBuffer[0], size);
}
//首次解析gif
syncTime.set_clock();
do {
if (DGifGetRecordType(gifFile, &RecordType) == GIF_ERROR) {
PrintGifError(gifFile->Error);
goto end;
}
switch (RecordType) {
case IMAGE_DESC_RECORD_TYPE:
if (DGifGetImageDesc(gifFile) == GIF_ERROR) {
PrintGifError(gifFile->Error);
goto end;
}
sp = &gifFile->SavedImages[gifFile->ImageCount - 1];
sp->RasterBits = (unsigned char *) malloc(
sizeof(GifPixelType) * gif_width * gif_height +
sizeof(int32_t) * 2);
user_image_data = (int32_t *) sp->RasterBits;
user_image_data[0] = delayTime;
user_image_data[1] = transparentColorIndex;
user_image_data[2] = disposalMode;
Row = gifFile->Image.Top;
Col = gifFile->Image.Left;
Width = gifFile->Image.Width;
Height = gifFile->Image.Height;
if (gifFile->Image.Left + gifFile->Image.Width > gifFile->SWidth ||
gifFile->Image.Top + gifFile->Image.Height > gifFile->SHeight) {
LOGI("Image %d is not confined to screen dimension, aborted", ImageNum);
goto end;
}
if (gifFile->Image.Interlace) {
for (i = 0; i < 4; i++)
for (j = Row + InterlacedOffset[i]; j < Row + Height;
j += InterlacedJumps[i]) {
if (DGifGetLine(gifFile, &ScreenBuffer[j][Col],
Width) == GIF_ERROR) {
PrintGifError(gifFile->Error);
goto end;
}
}
} else {
for (i = 0; i < Height; i++) {
if (DGifGetLine(gifFile, &ScreenBuffer[Row++][Col],
Width) == GIF_ERROR) {
PrintGifError(gifFile->Error);
goto end;
}
}
}
//颜色数组
ColorMap = (gifFile->Image.ColorMap
? gifFile->Image.ColorMap
: gifFile->SColorMap);
//根据实际时间计算下一帧的时间
dt = syncTime.synchronize_time(delayTime * 10);
threadSleep.msleep(dt);
delayTime = 0;
//线程锁
pthread_mutex_lock(&play_mutex);
if (is_pause) {
is_pause = false;
//阻塞当前线程
pthread_cond_wait(&play_cond, &play_mutex);
}
//线程解锁
pthread_mutex_unlock(&play_mutex);
//开始绘制
drawBitmap(env, gifFile->ImageCount - 1,
bitmap, sp, ColorMap, ScreenBuffer,
bitmapWidth,
gifFile->Image.Left, gifFile->Image.Top,
gifFile->Image.Left + Width, gifFile->Image.Top + Height);
//回调java方法,更新bitmap(第一次读取)
env->CallVoidMethod(runnable, runMethod);
//更新现在的时间,记录这一帧的跟新时间
syncTime.set_clock();
break;
case EXTENSION_RECORD_TYPE:
if (DGifGetExtension(gifFile, &ExtCode, &Extension) == GIF_ERROR) {
PrintGifError(gifFile->Error);
goto end;
}
if (ExtCode == GRAPHICS_EXT_FUNC_CODE) {
if (Extension[0] != 4) {
PrintGifError(GIF_ERROR);
goto end;
}
GifExtension = Extension + 1;
//得到每一帧画面的持续时间
delayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]);
if(delayTime < 1){
delayTime = 6;
}
if (GifExtension[0] & 0x01) {
transparentColorIndex = (int) GifExtension[3];
} else {
transparentColorIndex = NO_TRANSPARENT_COLOR;
}
disposalMode = (GifExtension[0] >> 2) & 0x07;
}
while (Extension != NULL) {
if (DGifGetExtensionNext(gifFile, &Extension) == GIF_ERROR) {
PrintGifError(gifFile->Error);
goto end;
}
}
break;
case TERMINATE_RECORD_TYPE:
break;
default:
break;
}
} while (RecordType != TERMINATE_RECORD_TYPE && !is_play_quit);
// 释放不再使用变量
for (i = 0; i < gifFile->SHeight; i++) {
free(ScreenBuffer[i]);
}
free(ScreenBuffer);
ScreenBuffer = NULL;
if (gifFile->UserData) {
AAsset *asset = (AAsset *) gifFile->UserData;
AAsset_close(asset);
gifFile->UserData = NULL;
}
//解析之后的循环播放
syncTime.set_clock();
while (!is_play_quit && !once) {
for (int t = 0; t < gifFile->ImageCount; t++) {
if (is_play_quit) {
break;
}
SavedImage frame = gifFile->SavedImages[t];
GifImageDesc frameInfo = frame.ImageDesc;
ColorMap = (frameInfo.ColorMap
? frameInfo.ColorMap
: gifFile->SColorMap);
//
int32_t d_time = 0;
user_image_data = (int32_t *) frame.RasterBits;
d_time = user_image_data[0];
transparentColorIndex = user_image_data[1];
disposalMode = user_image_data[2];
dt = syncTime.synchronize_time(d_time * 10);
threadSleep.msleep(dt);
//
pthread_mutex_lock(&play_mutex);
if (is_pause) {
is_pause = false;
pthread_cond_wait(&play_cond, &play_mutex);
}
pthread_mutex_unlock(&play_mutex);
//
void *pixels;
AndroidBitmap_lockPixels(env, bitmap, &pixels);
uint32_t *sPixels = (uint32_t *) pixels;
//
int pointPixelIdx = sizeof(int32_t) * DATA_OFFSET;
int dH = bitmapWidth * frameInfo.Top;
for (int h = frameInfo.Top; h < frameInfo.Top + frameInfo.Height; h++) {
for (int w = frameInfo.Left; w < frameInfo.Left + frameInfo.Width; w++) {
setColorARGB(&sPixels[dH + w],
t,
ColorMap,
frame.RasterBits[pointPixelIdx++]);
}
dH += bitmapWidth;
}
//
AndroidBitmap_unlockPixels(env, bitmap);
env->CallVoidMethod(runnable, runMethod);
//
syncTime.set_clock();
}
}
// free
end:
if (ScreenBuffer) {
free(ScreenBuffer);
}
if (gifFile->UserData) {
AAsset *asset = (AAsset *) gifFile->UserData;
AAsset_close(asset);
gifFile->UserData = NULL;
}
if (DGifCloseFile(gifFile, &Error) == GIF_ERROR) {
PrintGifError(Error);
}
gifFile = NULL;
setPlayState(IDLE);
}
其他的类就可以下载下来看,注释比较详细吧。