Android Native层 OpenGL ES开发

完整代码查看# AndroidShaderDemo下的NativeActivity

代码参考# Android-OpenGLRenderer
不过原代码是使用libpng对图片解码,并在native层建立个RenderLoop,这里改用jnigraphics库,直接画图,代码更加容易理解。

之前写的《Android基于Shader的图像处理》是在java层使用OpenGL ES画图,因为Android控件GLSurfaceView底层封装了对OpenGL ES的使用环境。这里使用NDK在Native层使用OpenGL ES开发,搭建Native的OpenGL ES开发环境。
搭建主要过程如下:
1、在Android.mk文件中加入EGL,OpenGL ES等库和头文件。
2、初始化EGL,创建Open GL上下文环境。
3、连接EGL和屏幕。
前面的很好理解,最后一步的屏幕是什么?其实就是抽象出来Surface,在Android中就是SurfaceView,这样就理解了,因为是在Native层开发,所以通过jni将java层的SurfaceView传到C++层,下面会讲解。
一、Android.mk文件中加入EGL,OpenGL ES等库和头文件
完整的Android.mk如下,按格式写,没什么好说

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS += -D__STDC_CONSTANT_MACROS

LOCAL_SRC_FILES = \
./NativeController.cpp

LOCAL_STATIC_LIBRARIES := librender

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_LDLIBS += -lz
LOCAL_LDLIBS += -landroid
LOCAL_LDLIBS += -ljnigraphics
# Link with OpenGL ES
LOCAL_LDLIBS += -lGLESv2
LOCAL_LDLIBS += -lEGL

LOCAL_MODULE := libdrawdemo
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))

二、初始化EGL和创建Open GL上下文环境
Demo里将对EGL的使用封装成为类EGLCore,在init函数里完成初始化,初始化的过程也是按流程走,分下面几步:
1、eglGetDisplay取得显示设备
2、eglInitialize初始化显示设备
3、eglChooseConfig配置颜色,像素格式,缓存等。
4、eglCreateContext使用前面的显示设备和配置信息来创建OpenGL上下文环境。
完整代码查看egl_core.cpp的init函数。

三、连接EGL和屏幕
这里从java层开始看,在java层使用SurfaceView显示OpenGL 画出来的东西。在NativeActivity里创建SurfaceView,然后将它添加在根布局上。同时设置回调。这些是使用SurfaceView的普通操作,没什么特别。
java层和native层的桥梁是NativeController

public class NativeController {
    public native void init();

    public native void setSurface(Surface surface);

    public native void resetSize(int width, int height);

    public native void showBitmap(Bitmap bitmap);

    public native void stop();
}

看NativeController的方法名就知道,NativeController的主要作用就是调用native层的C++函数。比如init()方法通过NativeController.cpp最后调用到了之前说的EGLCore初始化函数init()。
看下回调方法surfaceCreated完整代码:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    nativeController = new NativeController();
    nativeController.init();//初始化EGL
    nativeController.setSurface(holder.getSurface());
}

首先生成NativeController对象,通过它的一步步调用到EGLCore初始化函数init()完成EGL的初始化。
然后通过nativeController.setSurface(holder.getSurface());把SurfaceView传到native层。来看看在native层做了什么:

JNIEXPORT void JNICALL Java_com_andev_androidshaderdemo_nativedemo_NativeController_setSurface
  (JNIEnv * env, jobject obj, jobject surface){
   if (surface != 0 && NULL != controller) {
        window = ANativeWindow_fromSurface(env, surface);
        controller->setWindow(window);
    } else if (window != 0) {
        ANativeWindow_release(window);
        window = 0;
    }
}

其实就是通过ANativeWindow_fromSurface函数创建出一个window,这个window就表示设备屏幕。然后通过controller->setWindow(window);把这个window传给EGLCore来创建个EGLSurface,也就是能显示的Surface,代码如下:

EGLSurface EGLCore::createWindowSurface(ANativeWindow* _window) {
    EGLSurface surface = NULL;
    EGLint format;
    if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
        LOGE("eglGetConfigAttrib() returned error %d", eglGetError());
        release();
        return surface;
    }
    ANativeWindow_setBuffersGeometry(_window, 0, 0, format);
    if (!(surface = eglCreateWindowSurface(display, config, _window, 0))) {
        LOGE("eglCreateWindowSurface() returned error %d", eglGetError());
    }
    return surface;
}

这样就完成了EGL和显示屏幕的连接。

三、画图
上面把Native层 OpenGL ES开发环境搭建完成了,下面就可以画图了,这里是显示drawable下面的图片。原理就是加载drawable下面的图片生成Bitmap对象,然后将Bitmap对象传到natvie层生成纹理对象,这里封装了PicPreviewTexture,查看实现函数,里面都是OpenGL 使用纹理的基本操作,比如生成纹理,绑定纹理等等。这里不在重复说明。
画图过程在surfaceChanged方法里:

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    nativeController.resetSize(width, height);
    Resources resources = getResources();
    Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.face);
    nativeController.showBitmap(bitmap);
}

先用回调的长宽设置ViewPort,然后showBitmap画图,调用到native层的showBitmap函数,完整函数如下:

JNIEXPORT void JNICALL Java_com_andev_androidshaderdemo_nativedemo_NativeController_showBitmap
  (JNIEnv * env, jobject obj, jobject bitmap){

  int ret = 0;
  AndroidBitmapInfo bitmapInfo;
  void *pixels = NULL;

  int imgWidth = 0;
  int imgHeight = 0;

  if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0) {
        return;
  }

  LOGI("showBitmap width %d, height %d, format %d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.format);

  imgWidth = bitmapInfo.width;
  imgHeight = bitmapInfo.height;

  if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
        LOGI("Java_com_example_hellojni_HelloJni_showBitmap invalid rgb format");
        return;
  }

  if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
        LOGI("AndroidBitmap_lockPixels() failed ! error=%d", ret);
  }

  controller->loadTexture((byte*)pixels, imgWidth, imgHeight);
  controller->draw();

  AndroidBitmap_unlockPixels(env, bitmap);
}

前面一大段其实就是通过将Bitmap图片解码到内存,然后生成OpenGL纹理,真正画图就两步:

controller->loadTexture((byte*)pixels, imgWidth, imgHeight);
controller->draw();

查看代码就知道,里面都是OpenGL指令调用。

你可能感兴趣的:(Android Native层 OpenGL ES开发)