不了解webp格式的同学可以先看一下腾讯的这篇博文: webp探寻之路
xUtils3中的webp解析库来自(最新的库编解码速度优化了不少) :
https://github.com/webmproject/libwebp
jni接口来自:
https://github.com/alexey-pelykh/webp-android-backport
虽然Android4.0宣称BitmapFactory支持webp, 但是很多设备支持的是有问题的, 比如有的不支持透明度, 有的甚至解析的图我都不认识了OMG.
其中webp-android-backport这个接口代码Android5.0开始不兼容, 源码我做了如下修改:
// android_backport_webp.cpp // 修改: jclassRef = jniEnv->FindClass(...); // 为: jclass temp = jniEnv->FindClass(...); jclassRef = (jclass)jniEnv->NewGlobalRef(temp); jniEnv->DeleteLocalRef(temp);
同时添加了一个直接解码文件的jni方法:
// android_backport_webp_WebPFactory.cpp /* * Class: android_backport_webp_WebPFactory * Method: nativeDecodeFile * Signature: (Ljava/lang/String;Landroid/graphics/BitmapFactory/Options;)Landroid/graphics/Bitmap; */ JNIEXPORT jobject JNICALL Java_android_backport_webp_WebPFactory_nativeDecodeFile (JNIEnv *jniEnv, jclass, jstring path, jobject options) { // Check if input is valid if(!path) { jniEnv->ThrowNew(jrefs::java::lang::NullPointerException->jclassRef, "path can not be null"); return 0; } // Log what version of WebP is used //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Using WebP Decoder %08x", WebPGetDecoderVersion()); char *inputBuffer; size_t inputBufferLen; const char* filePath = jniEnv->GetStringUTFChars(path, 0); FILE *file = NULL; file = fopen(filePath, "rb"); jniEnv->ReleaseStringUTFChars(path, filePath); if(file) { fseek(file, 0, SEEK_END); long file_len = ftell(file); fseek(file, 0, SEEK_SET); inputBuffer = (char *) malloc(file_len * sizeof(char)); if (inputBuffer == NULL) { fclose(file); jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "malloc error"); return 0; } inputBufferLen = fread(inputBuffer, sizeof(char), file_len, file); if (inputBufferLen != file_len) { free(inputBuffer); fclose(file); jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Read file error"); return 0; } fclose(file); } else { jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Can not open file"); return 0; } // Validate image int bitmapWidth = 0; int bitmapHeight = 0; if(!WebPGetInfo((uint8_t*)inputBuffer, inputBufferLen, &bitmapWidth, &bitmapHeight)) { jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Invalid WebP format"); return 0; } // Check if size is all what we were requested to do if(options && jniEnv->GetBooleanField(options, jrefs::android::graphics::BitmapFactory->Options.inJustDecodeBounds) == JNI_TRUE) { // Set values jniEnv->SetIntField(options, jrefs::android::graphics::BitmapFactory->Options.outWidth, bitmapWidth); jniEnv->SetIntField(options, jrefs::android::graphics::BitmapFactory->Options.outHeight, bitmapHeight); // Release buffer free(inputBuffer); return 0; } //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Decoding %dx%d bitmap", bitmapWidth, bitmapHeight); // Create bitmap jobject value__ARGB_8888 = jniEnv->GetStaticObjectField(jrefs::android::graphics::Bitmap->Config.jclassRef, jrefs::android::graphics::Bitmap->Config.ARGB_8888); jobject outputBitmap = jniEnv->CallStaticObjectMethod(jrefs::android::graphics::Bitmap->jclassRef, jrefs::android::graphics::Bitmap->createBitmap, (jint)bitmapWidth, (jint)bitmapHeight, value__ARGB_8888); if(!outputBitmap) { free(inputBuffer); jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Failed to allocate Bitmap"); return 0; } outputBitmap = jniEnv->NewLocalRef(outputBitmap); // Get information about bitmap passed AndroidBitmapInfo bitmapInfo; if(AndroidBitmap_getInfo(jniEnv, outputBitmap, &bitmapInfo) != ANDROID_BITMAP_RESUT_SUCCESS) { free(inputBuffer); jniEnv->DeleteLocalRef(outputBitmap); jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Failed to get Bitmap information"); return 0; } // Lock pixels void* bitmapPixels = 0; if(AndroidBitmap_lockPixels(jniEnv, outputBitmap, &bitmapPixels) != ANDROID_BITMAP_RESUT_SUCCESS) { free(inputBuffer); jniEnv->DeleteLocalRef(outputBitmap); jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Failed to lock Bitmap pixels"); return 0; } // Decode to ARGB if(!WebPDecodeRGBAInto((uint8_t*)inputBuffer, inputBufferLen, (uint8_t*)bitmapPixels, bitmapInfo.height * bitmapInfo.stride, bitmapInfo.stride)) { AndroidBitmap_unlockPixels(jniEnv, outputBitmap); free(inputBuffer); jniEnv->DeleteLocalRef(outputBitmap); jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Failed to unlock Bitmap pixels"); return 0; } // Unlock pixels if(AndroidBitmap_unlockPixels(jniEnv, outputBitmap) != ANDROID_BITMAP_RESUT_SUCCESS) { free(inputBuffer); jniEnv->DeleteLocalRef(outputBitmap); jniEnv->ThrowNew(jrefs::java::lang::RuntimeException->jclassRef, "Failed to unlock Bitmap pixels"); return 0; } // Release buffer free(inputBuffer); return outputBitmap; }
在xUtils3中可以这样直接绑定webp图片:
x.image().bind(imageView, url, imageOptions);
怎么将本地其他图片转换成webp:
// 读取任意格式图片 Bitmap bitmap = ImageDecoder.decodeBitmap(file, imageOptions, null); // 以webp格式写入文件流 ImageDecoder.compress(bitmap, Bitmap.CompressFormat.WEBP, 100, fileOutputStream);
更多介绍, 继续关注我的博客, 或者先参考源码: https://github.com/wyouflf/xUtils3