通過jni調(diào)用C函數(shù)實(shí)現(xiàn)Android毛玻璃效果

之前項(xiàng)目中,有個(gè)需求是對(duì)Activtiy背景進(jìn)行虛化,實(shí)現(xiàn)毛玻璃效果。實(shí)現(xiàn)的大體思路:

  1. 對(duì)手機(jī)屏幕進(jìn)行截屏處理,獲得截屏圖片的bitmap;
  2. 對(duì)bitmap進(jìn)行相應(yīng)的config處理和縮放像素處理;
  3. 通過高斯模糊算法對(duì)處理后的bitmap進(jìn)行虛化;
  4. 將虛化后的bitmap作為背景圖展示。

雖然實(shí)現(xiàn)了,但由于效果不佳或是影響性能,最終選擇放棄。最近閑的沒事,準(zhǔn)備將這件事給完成。
因?yàn)橹皩?duì)bitmap的算法處理是通過java代碼實(shí)現(xiàn)的,如果想提高性能或是減少處理時(shí)間,達(dá)到秒開的效果,必須設(shè)置虛化的程度低,但這樣效果并不好看,但只是一味追求效果,由于java代碼運(yùn)行比較慢,在處理時(shí)間上必定會(huì)有延遲,總之就是有性能沒效果,有效果沒性能的矛盾,體驗(yàn)十分不佳,網(wǎng)上也給出過方案是通過c的方式實(shí)現(xiàn),今天就來完成,隨便回顧一遍jni的流程,長時(shí)間不碰全忘...

配置NDK,生成.so動(dòng)態(tài)庫

  1. build.gradle文件下,添加ndk配置:
android {
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 25
        ...
        ndk {
            moduleName "blur_lib"   // 動(dòng)態(tài)庫名稱
            abiFilters "armeabi", "armeabi-v7a", "x86" // 相應(yīng)的架構(gòu)平臺(tái)
            ldLibs "log" //log輸出
            ldLibs "jnigraphics" //graphic相關(guān)jni
        }
    }
   ...
}

項(xiàng)目的gradle.properties添加支持NDKandroid.useDeprecatedNdk=true
local.properties添加ndk-bundle路徑
ndk.dir=C\:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\ndk-bundle

  1. 創(chuàng)建NativeHelper類如下:
public class NativeHelper {
    static {
        System.loadLibrary("blur_lib");
    }
    // 參數(shù)r為對(duì)bitmap虛化的程度范圍
    static native void blurBitmap(Object bitmap, int r);
}

在Terminal中cd到j(luò)ava目錄下生成.h頭文件,方便得到c中的類名,輸入命令行:
javah -jni com.pecoo.blurjnidemo.NativeHelper

  1. main下創(chuàng)建一個(gè)jni folder,里面創(chuàng)建.c/c++和.h頭文件,高斯算法代碼粘進(jìn)來,并在.h頭文件進(jìn)行相應(yīng)的方法申明。
  2. 再創(chuàng)建一個(gè)c/c++文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_pecoo_blurjnidemo_NativeHelper */
#ifndef _Included_com_pecoo_blurjnidemo_NativeHelper
#define _Included_com_pecoo_blurjnidemo_NativeHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_pecoo_blurjnidemo_NativeHelper
 * Method:    blurBitmap
 * Signature: (Ljava/lang/Object;I)V
 */
 #include <android/log.h>
 #include <android/bitmap.h> 
 #include "stackblur.h"   // 在第一步中創(chuàng)建的.h頭文件,下面可以調(diào)用里面的方法

// log宏定義
 #define TAG "Native_Blur_Jni"
 #define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)

JNIEXPORT void JNICALL Java_com_pecoo_blurjnidemo_NativeHelper_blurBitmap
  (JNIEnv *env, jclass obj, jobject bitmapIn, jint r)
  {
      AndroidBitmapInfo infoIn;
      void *pixels;
     // 獲取bitmap的信息
      if (AndroidBitmap_getInfo(env, bitmapIn, &infoIn) != ANDROID_BITMAP_RESULT_SUCCESS) {
          LOG_D("AndroidBitmap_getInfo failed!");
          return;
      }
      // 檢測bitmap是不是這兩種格式,因?yàn)樗惴ㄖ兄挥袑?duì)這兩種圖片會(huì)做處理
      if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 &&
          infoIn.format != ANDROID_BITMAP_FORMAT_RGB_565) {
          LOG_D("Only support ANDROID_BITMAP_FORMAT_RGBA_8888 and ANDROID_BITMAP_FORMAT_RGB_565");
          return;
      }
      // 鎖定圖片
      if (AndroidBitmap_lockPixels(env, bitmapIn, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
          LOG_D("AndroidBitmap_lockPixels failed!");
          return;
      }
      // 得到寬高
      int h = infoIn.height;
      int w = infoIn.width;
      if (infoIn.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
          // 調(diào)用stackblur.c中的blur_ARGB_8888()或blur_RGB_565()
          pixels = blur_ARGB_8888((int *) pixels, w, h, r);
      } else if (infoIn.format == ANDROID_BITMAP_FORMAT_RGB_565) {
          pixels = blur_RGB_565((short *) pixels, w, h, r);
      }
      // 對(duì)應(yīng)上面的AndroidBitmap_lockPixels()
      AndroidBitmap_unlockPixels(env, bitmapIn);
  }
#ifdef __cplusplus
}
#endif
#endif

5.生成.so動(dòng)態(tài)庫:Build->Rebuild Project完成后,會(huì)在build文件下生成相應(yīng)平臺(tái)的.so。復(fù)制到main下的jniLibs中
若失敗試試
D:\workspace\ndk\NDKDemo\myapplication\src\main>javah -d jni -classpath C:\Users\Administrator\AppData\Local\Android\Sdk\platforms\android-20\android.jar;..\..\build\intermediates\classes\debug com.pecoo.myapplication.NativeUtils

虛化圖片,實(shí)現(xiàn)效果:

對(duì)于截屏,獲取到截屏的bitmap步驟這兒就忽略了,直接拿張資源圖片進(jìn)行處理,顯示在界面上

// 獲得bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.blur_img);
// 獲得圖片的寬高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// 設(shè)置想要的大小
Display display = getWindowManager().getDefaultDisplay();
int newWidth = display.getWidth();
int newHeight = display.getHeight();
// 計(jì)算縮放比例
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Log.d(TAG, "scaleWidth:" + scaleWidth);
Log.d(TAG, "scaleHeight:" + scaleHeight);
// 取得想要縮放的matrix參數(shù)
Matrix matrix = new Matrix();
// matrix.postScale(scaleWidth, scaleHeight);
// 實(shí)現(xiàn)模糊效果之前,這里可對(duì)bitmap進(jìn)行更大縮放,減少像素點(diǎn)還可提高性能
float scaleFactor = 10;
float scale = 1f / scaleFactor;
matrix.postScale(scale, scale);
// 得到新的圖片
Bitmap newbm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix,true);
bitmap.recycle();

子線程中調(diào)用處理bitmap的本地函數(shù)

    private void blur(final Bitmap bitmap, final int radius) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                super.run();
                // blurNatively()主要是檢查圖片是否為ARGB8888和RGB565,如果是則調(diào)用 NativeHelper中的本地方法blurBitmap()
                final Bitmap ret = blurNatively(bitmap, radius,true);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mImageView.setBackground(new BitmapDrawable(getResources(), ret));
                    }
                });
            }
        };
        thread.start();
    }

最后把 github地址發(fā)一下,僅供參考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容