原生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.cpp
和native_window.h
文件:
来看看native_window.cpp
中drawColor
的实现:
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
也比较简单,但是我们需要通过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