不了解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