使用xUtils3支持webp图片 加速图片载入

原创
2015/11/20 12:16
阅读数 2.8K

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

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部