1. 程式人生 > >android平臺下基於ANativeWindow實現渲染bitmap影象

android平臺下基於ANativeWindow實現渲染bitmap影象

OpenGL ES 3.0學習實踐

概述

原生Window API支援我們在ndk下開發原生的繪製功能,後續的一些視訊渲染,包括相機採集預覽等都可以通過這些API來實現,筆者今天通過幾個簡單的API來實踐一下native層的繪製功能

配置環境

筆者Android Studio配置的是android-ndk-r16b版本,作業系統是ubuntu 16.05

繪製背景色

還是從最簡單的入手,開始嘗試繪製背景顏色,我們此次使用的方案是SurfaceView+ANativeWindow的方式,基於之前的專案工程

先定義Java層的本地方法

/**
 * @anchor: andy
 * @date: 2018-11-13
 * @description:
 */
public class NativeWindowSample { static { System.loadLibrary("native-window"); } /** * 繪製指定顏色背景 * * @param surface * @param color */ public native void drawColor(Object surface, int color); /** * 繪製指定顏色背景 * * @param surface * @param bitmap */
public native void drawBitmap(Object surface, Object bitmap); }

配置CMakeLists.txt檔案內容如下:


cmake_minimum_required(VERSION 3.4.1)

##官方標準配置
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -Wall")

add_library(native-window
           SHARED
           src/
main/cpp/native_window.cpp) target_link_libraries(native-window ${OPENGL_LIB} android jnigraphics log)

build.gradle中的配置,這裡不贅述,比較簡單

在我們的子工程目錄src/main/cpp下新建我們的native_window.cppnative_window.h檔案:

來看看native_window.cppdrawColor的實現:

void drawColor(JNIEnv *env, jobject obj, jobject surface, jint colorARGB) {
    //分離ARGB
    int alpha = (colorARGB >> 24) & 0xFF;
    int red = (colorARGB >> 16) & 0xFF;
    int green = (colorARGB >> 8) & 0xFF;
    int blue = colorARGB & 0xFF;

    int colorABGR = (alpha << 24) | (blue << 16) | (green << 8) | red;

    //獲取目標surface
    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
    //預設的是RGB_565
    ANativeWindow_setBuffersGeometry(window, 640, 640, WINDOW_FORMAT_RGBA_8888);
    ANativeWindow_acquire(window);

    ANativeWindow_Buffer buffer;
    ANativeWindow_lock(window, &buffer, NULL);

    uint32_t *line = (uint32_t *) buffer.bits;
    for (int y = 0; y < buffer.height; y++) {
        for (int x = 0; x < buffer.width; x++) {
            line[x] = colorABGR;
        }
        line = line + buffer.stride;
    }

    ANativeWindow_unlockAndPost(window);
    //釋放視窗
    ANativeWindow_release(window);
}

這裡要注意的就是,我們從Java層傳入的是32位的ARGB的顏色,直接寫入我們的windowBuffer,顏色顯示可能不正確,需要按照ANativeWindow_Buffer指定的顏色順序作一次轉換

繪製一個灰色背景:

mNativeWindowSample.drawColor(mSurfaceView.getHolder().getSurface(), Color.GRAY);

繪製bitmap

直接繪製bitmap也比較簡單,但是我們需要通過AndroidBitmap_lockPixels方法獲取bitmap對應的本地的資料的指標,通過這個指標來讀取對應的畫素資料,註釋也比較清楚

void drawBitmap(JNIEnv *env, jobject obj, jobject surface, jobject bitmap) {
    //獲取bitmap的資訊,比如寬和高
    AndroidBitmapInfo info;
    AndroidBitmap_getInfo(env, bitmap, &info);

    char *data = NULL;
    //獲取bitmap對應的native指標
    AndroidBitmap_lockPixels(env, bitmap, (void **) &data);
    AndroidBitmap_unlockPixels(env, bitmap);

    //獲取目標surface
    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
    //這裡設定為RGBA的方式,總共是4位元組32位
    ANativeWindow_setBuffersGeometry(window, info.width, info.height, WINDOW_FORMAT_RGBA_8888);
    ANativeWindow_acquire(window);

    ANativeWindow_Buffer buffer;
    //鎖定視窗的繪圖表面
    ANativeWindow_lock(window, &buffer, NULL);

    //轉換為畫素點來處理
    int32_t *bitmapPixes = (int32_t *) data;
    uint32_t *line = (uint32_t *) buffer.bits;
    for (int y = 0; y < buffer.height; y++) {
        for (int x = 0; x < buffer.width; x++) {
            line[x] = bitmapPixes[buffer.height * y + x];
        }
        line = line + buffer.stride;
    }
    //解鎖視窗的繪圖表面
    ANativeWindow_unlockAndPost(window);
    //釋放
    ANativeWindow_release(window);

}

繪製一個bitmap物件

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.main, options);
mNativeWindowSample.drawBitmap(mSurfaceView.getHolder().getSurface(), bitmap);