国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android JNI處理圖片實現黑白濾鏡的方法

2019-10-22 18:17:29
字體:
來源:轉載
供稿:網友

前言

在android/138977.html">Android的開發中,我們有時會遇到對性能要求比較高的模塊。所幸Android通過NDK為我們提供了c++開發的方式。我們可以通過c++完成核心的耗時的計算,然后通過JNI的方式將處理完成的數據傳給Java層。

今天,我們就從一個很小的角度(Bitmap)的處理,來實踐NDK開發的方式。開發一個小小的圖片濾鏡。

準備

新版本的Android Studio在新建工程時,就可以選擇Include C++ support

Android,JNI處理圖片,黑白濾鏡,濾鏡

當我們勾上這個選擇后,Android Studio就會幫我們自動完成,c++開發目錄的創建。

Android,JNI處理圖片,黑白濾鏡,濾鏡

我們先看一下CMakeLists.txt:

# For more information about using CMake with Android Studio, read the# documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.4.1)# Creates and names a library, sets it as either STATIC# or SHARED, and provides the relative paths to its source code.# You can define multiple libraries, and CMake builds them for you.# Gradle automatically packages shared libraries with your APK.add_library( # Sets the name of the library.       native-lib       # Sets the library as a shared library.       SHARED       # Provides a relative path to your source file(s).       src/main/cpp/native-lib.cpp )# Searches for a specified prebuilt library and stores the path as a# variable. Because CMake includes system libraries in the search path by# default, you only need to specify the name of the public NDK library# you want to add. CMake verifies that the library exists before# completing its build.find_library( # Sets the name of the path variable.       log-lib       # Specifies the name of the NDK library that       # you want CMake to locate.       log )# Specifies libraries CMake should link to your target library. You# can link multiple libraries, such as libraries you define in this# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.            native-lib jnigraphics            # Links the target library to the log library            # included in the NDK.            ${log-lib} )

我們可以看到,這個文件中,包含了我們需要使用的cpp庫和cpp文件。由于這一次的例子,我們需要開發Bitmap相關的功能,所以我加入了jnigraphics。

Java

先看代碼:

public class MainActivity extends AppCompatActivity {  private ImageView mImg1, mImg2;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    mImg1 = (ImageView) findViewById(R.id.img_test1_id);    mImg2 = (ImageView) findViewById(R.id.img_test2_id);  }  /**   * 確定native處理圖片的接口   * @param bitmap 需要被處理的圖片   */  public native void nativeProcessBitmap(Bitmap bitmap);  /**   * 引入native庫   */  static {    System.loadLibrary("native-lib");  }  /**   * 點擊開始加載圖片   * @param view   */  public void onLoadClick(View view) {    Bitmap originalBitmap = loadBitmap();    mImg1.setImageBitmap(originalBitmap);    Bitmap resultBitmap = processBitmap(originalBitmap);    mImg2.setImageBitmap(resultBitmap);  }  /**   * 從assets中加載圖片   * @return   */  private Bitmap loadBitmap() {    Bitmap bmp = null;    AssetManager am = getResources().getAssets();    try {      InputStream is = am.open("test_img.jpg");      BitmapFactory.Options options = new BitmapFactory.Options();      bmp = BitmapFactory.decodeStream(is ,null , options);      is.close();    } catch (IOException e) {      e.printStackTrace();    }    return bmp;  }  /**   * 處理圖片,此方法中會調用nativeProcessBitmap   * @param bitmap   * @return   */  private Bitmap processBitmap(Bitmap bitmap) {    Bitmap bmp = bitmap.copy(Bitmap.Config.ARGB_8888, true);    nativeProcessBitmap( bmp);    return bmp;  }}

代碼比較簡單。不作過多的解釋。

與圖片相關的事情只有兩件:

  1. 引入native-lib庫
  2. 確定了native接口:public native void nativeProcessBitmap(Bitmap bitmap);

其他的代碼都是為了demo效果寫的一些業務代碼,不作過多贅述。

C++

由于c++的代碼比較長,我們分段來看。

#include <jni.h>#include <string>#include <android/bitmap.h>#include <android/log.h>#ifndef eprintf#define eprintf(...) __android_log_print(ANDROID_LOG_ERROR,"@",__VA_ARGS__)#endif

這一段主要引入了我們需要的庫并宏定義了eprintf,方便我們打日志并進行調試。

#define MAKE_RGB565(r,g,b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))#define MAKE_ARGB(a,r,g,b) ((a&0xff)<<24) | ((r&0xff)<<16) | ((g&0xff)<<8) | (b&0xff)#define RGB565_R(p) ((((p) & 0xF800) >> 11) << 3)#define RGB565_G(p) ((((p) & 0x7E0 ) >> 5) << 2)#define RGB565_B(p) ( ((p) & 0x1F )    << 3)#define RGB8888_A(p) (p & (0xff<<24) >> 24 )#define RGB8888_R(p) (p & (0xff<<16) >> 16 )#define RGB8888_G(p) (p & (0xff<<8) >> 8 )#define RGB8888_B(p) (p & (0xff) )

這一段定義了RGB565和ARGB8888的讀寫方法。對于RGB565和ARGB8888格式不熟悉的同學,可以參考:

在Android的Bitmap.Config中有四個枚舉類型:ALPHA_8、ARGB_4444、ARGB_8888和RGB_565

下面是這四種類型的詳細解釋:

ALPHA_8:每個像素都需要1(8位)個字節的內存,只存儲位圖的透明度,沒有顏色信息

ARGB_4444:A(Alpha)占4位的精度,R(Red)占4位的精度,G(Green)占4位的精度,B(Blue)占4位的精度,加起來一共是16位的精度,折合是2個字節,也就是一個像素占兩個字節的內存,同時存儲位圖的透明度和顏色信息。不過由于該精度的位圖質量較差,官方不推薦使用

ARGB_8888:這個類型的跟ARGB_4444的原理是一樣的,只是A,R,G,B各占8個位的精度,所以一個像素占4個字節的內存。由于該類型的位圖質量較好,官方特別推薦使用。但是,如果一個480*800的位圖設置了此類型,那個它占用的內存空間是:480*800*4/(1024*1024)=1.5M

RGB_565:同理,R占5位精度,G占6位精度,B占5位精度,一共是16位精度,折合兩個字節。這里注意的時,這個類型存儲的只是顏色信息,沒有透明度信息

值得注意的是雖然RGB565的三色只有5位信息,但其實它們的值是8位,提供的5位信息是高5位的信息。

extern "C"{  JNIEXPORT void JNICALL  Java_com_live_longsiyang_jnibitmapdemo_MainActivity_nativeProcessBitmap(JNIEnv *env,                                      jobject instance,                                      jobject bitmap) {    if (bitmap == NULL) {      eprintf("bitmap is null/n");      return;    }    AndroidBitmapInfo bitmapInfo;    memset(&bitmapInfo , 0 , sizeof(bitmapInfo));    // Need add "jnigraphics" into target_link_libraries in CMakeLists.txt    AndroidBitmap_getInfo(env , bitmap , &bitmapInfo);    // Lock the bitmap to get the buffer    void * pixels = NULL;    int res = AndroidBitmap_lockPixels(env, bitmap, &pixels);    // From top to bottom    int x = 0, y = 0;    for (y = 0; y < bitmapInfo.height; ++y) {      // From left to right      for (x = 0; x < bitmapInfo.width; ++x) {        int a = 0, r = 0, g = 0, b = 0;        void *pixel = NULL;        // Get each pixel by format        if(bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888)        {          pixel = ((uint32_t *)pixels) + y * bitmapInfo.width + x;          int r,g,b;          uint32_t v = *((uint32_t *)pixel);          r = RGB8888_R(v);          g = RGB8888_G(v);          b = RGB8888_B(v);          int sum = r+g+b;          *((uint32_t *)pixel) = MAKE_ARGB(0x1f , sum/3, sum/3, sum/3);        }        else if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565) {          pixel = ((uint16_t *)pixels) + y * bitmapInfo.width + x;          int r,g,b;          uint16_t v = *((uint16_t *)pixel);          r = RGB565_R(v);          g = RGB565_G(v);          b = RGB565_B(v);          int sum = r+g+b;          *((uint16_t *)pixel) = MAKE_RGB565(sum/3, sum/3, sum/3); }      }    }    AndroidBitmap_unlockPixels(env, bitmap);  }}

這一段代碼雖然長,但邏輯其實非常簡單。

    AndroidBitmapInfo bitmapInfo;    memset(&bitmapInfo , 0 , sizeof(bitmapInfo));    // Need add "jnigraphics" into target_link_libraries in CMakeLists.txt    AndroidBitmap_getInfo(env , bitmap , &bitmapInfo);

我們通過bitmap獲得AndroidBitmapInfo對象。AndroidBitmapInfo為我們提供了Bitmap的所有信息。

然后我們,再調用AndroidBitmap_lockPixels:

void * pixels = NULL;int res = AndroidBitmap_lockPixels(env, bitmap, &pixels);

獲得bitmap的像素矩陣,并將它存放在&pixels中。

pixels的每一位就包含了一個像素點的顏色信息。因此在RGB565模式下,它就是16位的,在ARGB8888模式下,它就是24位的。最后,我對RGB三色的值取了平均,從而得到一個新的圖片。在這個圖片中,RGB三色的值是相等的。因此,它是一個黑白圖片。

我們在修改圖片的像素值時,圖片其實是被鎖定的,修改完成后,我們需要解鎖:

AndroidBitmap_unlockPixels(env, bitmap);

至此,我們的圖片修改就完成了。最后看一下效果。

Android,JNI處理圖片,黑白濾鏡,濾鏡

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 阳朔县| 文水县| 泸定县| 奎屯市| 叙永县| 浙江省| 云浮市| 昔阳县| 扬中市| 彭水| 正阳县| 汉阴县| 宣武区| 无锡市| 民勤县| 陈巴尔虎旗| 玉林市| 朝阳县| 保定市| 太白县| 天等县| 南澳县| 射阳县| 沙雅县| 武川县| 库尔勒市| 德钦县| 喀什市| 福贡县| 安达市| 札达县| 沿河| 山阴县| 余姚市| 进贤县| 钟山县| 万年县| 恩施市| 特克斯县| 临潭县| 邛崃市|