android平台下基于ANativeWindow实现渲染bitmap图像

本文详细介绍了在Android平台上使用OpenGLES3.0进行图形绘制的方法,从基础的纯色背景绘制到复杂的立方体渲染,再到纹理贴图和相机预览处理,提供了丰富的实践案例和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
    if (NULL == window) {
        ThrowException(env, "java/lang/RuntimeException", "unable to get native window");
        return;
    }
    //默认的是RGB_565
    int32_t result = ANativeWindow_setBuffersGeometry(window, 640, 640, WINDOW_FORMAT_RGBA_8888);
    if (result < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to set buffers geometry");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }
    ANativeWindow_acquire(window);

    ANativeWindow_Buffer buffer;
    if (ANativeWindow_lock(window, &buffer, NULL) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to lock native window");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }

    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;
    }

    if (ANativeWindow_unlockAndPost(window) < 0) {
        ThrowException(env, "java/lang/RuntimeException",
                       "unable to unlock and post to native 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;
    if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to get bitmap info");
        return;
    }

    char *data = NULL;
    //获取bitmap对应的native指针
    if (AndroidBitmap_lockPixels(env, bitmap, (void **) &data) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to lock pixels");
        return;
    }
    if (AndroidBitmap_unlockPixels(env, bitmap) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to unlock pixels");
        return;
    }

    //获取目标surface
    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
    if (NULL == window) {
        ThrowException(env, "java/lang/RuntimeException", "unable to get native window");
        return;
    }
    //这里设置为RGBA的方式,总共是4字节32位
    int32_t result = ANativeWindow_setBuffersGeometry(window, info.width, info.height,
                                                      WINDOW_FORMAT_RGBA_8888);
    if (result < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to set buffers geometry");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }
    ANativeWindow_acquire(window);

    ANativeWindow_Buffer buffer;
    //锁定窗口的绘图表面
    if (ANativeWindow_lock(window, &buffer, NULL) < 0) {
        ThrowException(env, "java/lang/RuntimeException", "unable to lock native window");
        //释放窗口
        ANativeWindow_release(window);
        window = NULL;
        return;
    }

    //转换为像素点来处理
    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;
    }
    //解锁窗口的绘图表面
    if (ANativeWindow_unlockAndPost(window) < 0) {
        ThrowException(env, "java/lang/RuntimeException",
                       "unable to unlock and post to native 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);

项目地址:
https://github.com/byhook/opengles4android

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值