在Android中的jni里使用OpenGL ES 渲染一幅图片

1、准备工作:Android开发IDE——Eclipse,JDK,ADT,CDT,Android-ndk-r10d(这是我的ndk版本,其余版本也可以),这些如果不会配置可以参考我的博客及网上资料。

2、建立一个项目,取名为:ShowImage。

在Android中的jni里使用OpenGL ES 渲染一幅图片_第1张图片

在项目包中新建一个类:GL2JNIView.java,该类用于配置EGL,并添加如下代码:
package com.example.showimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.opengl.GLSurfaceView;
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;


/**
 * A simple GLSurfaceView sub-class that demonstrate how to perform
 * OpenGL ES 2.0 rendering into a GL Surface. 
 *一个简单的GLSurfaceView子类,该类论证怎样把一个OpenGL ES 2.0转化成一个GL Surface
 *Note the following important details:注意以下详细步骤:
 * - The class must use a custom context factory to enable 2.0 rendering.
 * 该类必须用一个自定义环境来启动2.0渲染
 *   See ContextFactory class definition below.
 *看一下以下定义的环境工厂类
 * - The class must use a custom EGLConfigChooser to be able to select
 *   an EGLConfig that supports 2.0. This is done by providing a config
 *   specification to eglChooseConfig() that has the attribute
 *   EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT flag
 *   set. See ConfigChooser class definition below.
 *该类必须用一个自定义的EGL配置选择器才能够选择一个支持2.0的EGL配置。这个是通过提供一个配置定义来做到的
 * - The class must select the surface's format, then choose an EGLConfig
 *   that matches it exactly (with regards to red/green/blue/alpha channels
 *   bit depths). Failure to do so would result in an EGL_BAD_MATCH error.
 */


class GL2JNIView extends GLSurfaceView{
    private static String TAG = "GL2JNIView";
    private static final boolean DEBUG = false;


    public GL2JNIView(Context context) {
        super(context);
        init(false, 0, 0);
    }


    public GL2JNIView(Context context, boolean translucent, int depth, int stencil) {
        super(context);
        init(translucent, depth, stencil);
    }


    private void init(boolean translucent, int depth, int stencil) {


        /* By default, GLSurfaceView() creates a RGB_565 opaque surface.
         * If we want a translucent one, we should change the surface's
         * format here, using PixelFormat.TRANSLUCENT for GL Surfaces
         * is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
         * 
         */
        if (translucent) {
            this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        }


        /* Setup the context factory for 2.0 rendering.
         * See ContextFactory class definition below
         */
        setEGLContextFactory(new ContextFactory());


        /* We need to choose an EGLConfig that matches the format of
         * our surface exactly. This is going to be done in our
         * custom config chooser. See ConfigChooser class definition
         * below.
         */  
        setEGLConfigChooser( translucent ?
                             new ConfigChooser(8, 8, 8, 8, depth, stencil) :
                             new ConfigChooser(5, 6, 5, 0, depth, stencil) );


        /* Set the renderer responsible for frame rendering */
        setRenderer(new Renderer());
    }


    private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
        private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
        
        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
            Log.w(TAG, "creating OpenGL ES 2.0 context");
            checkEglError("Before eglCreateContext", egl);
            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
            EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
            checkEglError("After eglCreateContext", egl);
            return context;
        }


        public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
            egl.eglDestroyContext(display, context);
        }
    }


    private static void checkEglError(String prompt, EGL10 egl) {
        int error;
        while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
            Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
        }
    }


    private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {


        public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
            mRedSize = r;
            mGreenSize = g;
            mBlueSize = b;
            mAlphaSize = a;
            mDepthSize = depth;
            mStencilSize = stencil;
        }


        /* This EGL config specification is used to specify 2.0 rendering.
         * We use a minimum size of 4 bits for red/green/blue, but will
         * perform actual matching in chooseConfig() below.
         */
        private static int EGL_OPENGL_ES2_BIT = 4;
        private static int[] s_configAttribs2 =
        {
            EGL10.EGL_RED_SIZE, 4,
            EGL10.EGL_GREEN_SIZE, 4,
            EGL10.EGL_BLUE_SIZE, 4,
            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL10.EGL_NONE
        };


        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {


            /* Get the number of minimally matching EGL configurations
             */
            int[] num_config = new int[1];
            egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);


            int numConfigs = num_config[0];


            if (numConfigs <= 0) {
                throw new IllegalArgumentException("No configs match configSpec");
            }


            /* Allocate then read the array of minimally matching EGL configs
             */
            EGLConfig[] configs = new EGLConfig[numConfigs];
            egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);


            if (DEBUG) {
                 printConfigs(egl, display, configs);
            }
            /* Now return the "best" one
             */
            return chooseConfig(egl, display, configs);
        }


        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
                EGLConfig[] configs) {
            for(EGLConfig config : configs) {
                int d = findConfigAttrib(egl, display, config,
                        EGL10.EGL_DEPTH_SIZE, 0);
                int s = findConfigAttrib(egl, display, config,
                        EGL10.EGL_STENCIL_SIZE, 0);


                // We need at least mDepthSize and mStencilSize bits
                if (d < mDepthSize || s < mStencilSize)
                    continue;


                // We want an *exact* match for red/green/blue/alpha
                int r = findConfigAttrib(egl, display, config,
                        EGL10.EGL_RED_SIZE,0);
                int g = findConfigAttrib(egl, display, config,
                        EGL10.EGL_GREEN_SIZE,0);
                int b = findConfigAttrib(egl, display, config,
                        EGL10.EGL_BLUE_SIZE,0);
                int a = findConfigAttrib(egl, display, config,
                        EGL10.EGL_ALPHA_SIZE,0);


                if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)
                    return config;
            }
            return null;
        }


        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
                EGLConfig config, int attribute, int defaultValue) {


            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
                return mValue[0];
            }
            return defaultValue;
        }


        private void printConfigs(EGL10 egl, EGLDisplay display,
            EGLConfig[] configs) {
            int numConfigs = configs.length;
            Log.w(TAG, String.format("%d configurations", numConfigs));
            for (int i = 0; i < numConfigs; i++) {
                Log.w(TAG, String.format("Configuration %d:\n", i));
                printConfig(egl, display, configs[i]);
            }
        }


        private void printConfig(EGL10 egl, EGLDisplay display,
                EGLConfig config) {
            int[] attributes = {
                    EGL10.EGL_BUFFER_SIZE,
                    EGL10.EGL_ALPHA_SIZE,
                    EGL10.EGL_BLUE_SIZE,
                    EGL10.EGL_GREEN_SIZE,
                    EGL10.EGL_RED_SIZE,
                    EGL10.EGL_DEPTH_SIZE,
                    EGL10.EGL_STENCIL_SIZE,
                    EGL10.EGL_CONFIG_CAVEAT,
                    EGL10.EGL_CONFIG_ID,
                    EGL10.EGL_LEVEL,
                    EGL10.EGL_MAX_PBUFFER_HEIGHT,
                    EGL10.EGL_MAX_PBUFFER_PIXELS,
                    EGL10.EGL_MAX_PBUFFER_WIDTH,
                    EGL10.EGL_NATIVE_RENDERABLE,
                    EGL10.EGL_NATIVE_VISUAL_ID,
                    EGL10.EGL_NATIVE_VISUAL_TYPE,
                    0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
                    EGL10.EGL_SAMPLES,
                    EGL10.EGL_SAMPLE_BUFFERS,
                    EGL10.EGL_SURFACE_TYPE,
                    EGL10.EGL_TRANSPARENT_TYPE,
                    EGL10.EGL_TRANSPARENT_RED_VALUE,
                    EGL10.EGL_TRANSPARENT_GREEN_VALUE,
                    EGL10.EGL_TRANSPARENT_BLUE_VALUE,
                    0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
                    0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
                    0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
                    0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
                    EGL10.EGL_LUMINANCE_SIZE,
                    EGL10.EGL_ALPHA_MASK_SIZE,
                    EGL10.EGL_COLOR_BUFFER_TYPE,
                    EGL10.EGL_RENDERABLE_TYPE,
                    0x3042 // EGL10.EGL_CONFORMANT
            };
            String[] names = {
                    "EGL_BUFFER_SIZE",
                    "EGL_ALPHA_SIZE",
                    "EGL_BLUE_SIZE",
                    "EGL_GREEN_SIZE",
                    "EGL_RED_SIZE",
                    "EGL_DEPTH_SIZE",
                    "EGL_STENCIL_SIZE",
                    "EGL_CONFIG_CAVEAT",
                    "EGL_CONFIG_ID",
                    "EGL_LEVEL",
                    "EGL_MAX_PBUFFER_HEIGHT",
                    "EGL_MAX_PBUFFER_PIXELS",
                    "EGL_MAX_PBUFFER_WIDTH",
                    "EGL_NATIVE_RENDERABLE",
                    "EGL_NATIVE_VISUAL_ID",
                    "EGL_NATIVE_VISUAL_TYPE",
                    "EGL_PRESERVED_RESOURCES",
                    "EGL_SAMPLES",
                    "EGL_SAMPLE_BUFFERS",
                    "EGL_SURFACE_TYPE",
                    "EGL_TRANSPARENT_TYPE",
                    "EGL_TRANSPARENT_RED_VALUE",
                    "EGL_TRANSPARENT_GREEN_VALUE",
                    "EGL_TRANSPARENT_BLUE_VALUE",
                    "EGL_BIND_TO_TEXTURE_RGB",
                    "EGL_BIND_TO_TEXTURE_RGBA",
                    "EGL_MIN_SWAP_INTERVAL",
                    "EGL_MAX_SWAP_INTERVAL",
                    "EGL_LUMINANCE_SIZE",
                    "EGL_ALPHA_MASK_SIZE",
                    "EGL_COLOR_BUFFER_TYPE",
                    "EGL_RENDERABLE_TYPE",
                    "EGL_CONFORMANT"
            };
            int[] value = new int[1];
            for (int i = 0; i < attributes.length; i++) {
                if (egl.eglGetConfigAttrib(display, config, attributes[i], value)) {
                    Log.w(TAG, String.format("  %s: %d\n", names[i], value[0]));
                } else {
                    // Log.w(TAG, String.format("  %s: failed\n", name));
                    while (egl.eglGetError() != EGL10.EGL_SUCCESS);
                }
            }
        }


        // Subclasses can adjust these values:
        protected int mRedSize;
        protected int mGreenSize;
        protected int mBlueSize;
        protected int mAlphaSize;
        protected int mDepthSize;
        protected int mStencilSize;
        private int[] mValue = new int[1];
    }


    private static class Renderer implements GLSurfaceView.Renderer {
   
    Context context=MyApplication.getContextObject();
    Bitmap bitmap=((BitmapDrawable)
    context.getResources().getDrawable(R.drawable.pic3)).getBitmap();
    int col=bitmap.getWidth();
    int row=bitmap.getHeight();
    int[] pix=new int[col*row];
   
        public void onDrawFrame(GL10 gl) {
            GL2JNILib.step();
        }


        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GL2JNILib.init(width, height,col,row, pix);
        }


        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            // Do nothing.
        bitmap.getPixels(pix,0,col,0,0,col,row);
        }
    }
}
新建类GL2JNILib.java用于管理本地库,添加代码如下:

package com.example.showimage;


public class GL2JNILib {
static{
System.loadLibrary("gl2jni");
}
public static native void init(int width,int height,int col,int row, int[] pix);
public static native void step();
}


新建类MyApplication.java用于设置全局的context,并添加代码如下:

package com.example.showimage;

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application{
private static Context context;
public void onCreate(){
//获取Context
   context=getApplicationContext();
}

//返回
public static Context getContextObject(){
return context;
}
}


在MainActivity.java 中添加如下代码:

package com.example.showimage;


import android.os.Bundle;
import android.app.Activity;


public class MainActivity extends Activity {


GL2JNIView mView;
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mView = new GL2JNIView(getApplication());
   setContentView(mView);
    }


    @Override
    protected void onPause() {
        super.onPause();
        mView.onPause();
    }


    @Override
    protected void onResume() {
        super.onResume();
        mView.onResume();
    }
}

右键单击项目添加C属性(不会添加的参见我之前的博客)

在项目中新建文件夹jni,在jni中新建文件image.cpp并添加如下代码:

#include
#include
#include
#include
#include
#include
#include
#include


#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)


using namespace std;
using namespace cv;


static void printGLString(const char* name, GLenum s){
    const char* v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}


static void checkGlError(const char* op) {
    for (GLint error = glGetError();
    error;
    error = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}


static const char gVertexShader[] =
    "uniform   mat4 u_matrix;       \n"
"attribute vec4 a_position;     \n"
"attribute vec2 a_texture;      \n"
"varying   vec2 v_texture;      \n"
    "void main() {                  \n"
    "  gl_Position = a_position;    \n"
"  v_texture  = a_texture;      \n"
    "}                              \n";
static const char gFragmentShader[] =
    "precision mediump      float;                     \n"
"varying      vec2  v_texture;                     \n"
"uniform sampler2D  u_texture;                     \n"
    "void main() {                                     \n"
    "  gl_FragColor = texture2D(u_texture,v_texture);  \n"
    "}                                                 \n";
GLuint loadShader(GLenum shaderType, const char* pSource) {
    GLuint shader = glCreateShader(shaderType);
    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        glCompileShader(shader);
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen) {
                char* buf = (char*) malloc(infoLen);
                if (buf) {
                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
                    LOGE("Could not compile shader %d:\n%s\n",
                            shaderType, buf);
                    free(buf);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}
GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
    if (!vertexShader) {
        return 0;
    }


    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
    if (!pixelShader) {
        return 0;
    }


    GLuint program = glCreateProgram();
    if (program) {
        glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if(linkStatus != GL_TRUE){
            GLint bufLength = 0;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
            if (bufLength) {
                char* buf = (char*) malloc(bufLength);
                if (buf) {
                    glGetProgramInfoLog(program, bufLength, NULL, buf);
                    LOGE("Could not link program:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteProgram(program);
            program = 0;
        }
    }
    return program;
}
GLuint textureObjectIds;


void loadTexture(int col,int row,uchar* P_pixels){
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
checkGlError("glPixelStorei");
glGenTextures(1,&textureObjectIds);                                         //生成贴图
checkGlError("glGenTextures");
glBindTexture(GL_TEXTURE_2D,textureObjectIds);                              //绑定贴图
checkGlError("glBindTexture");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,col,row,0,GL_RGB,GL_UNSIGNED_BYTE,P_pixels);//装载贴图
    checkGlError("glTexImage2D");
    glGenerateMipmap(GL_TEXTURE_2D);//生成贴图所有必要的级别
    checkGlError("glGenerateMipmap");
}


GLuint gProgram;


GLuint u_matrix;
GLuint u_texture;


GLuint a_position;
GLuint a_texture;


float projectionMatrix[16];


bool setupGraphics(int width, int height, int col,int row,uchar* P_pixels) {
    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);


    LOGI("setupGraphics(%d, %d)", width, height);
    gProgram = createProgram(gVertexShader, gFragmentShader);
    if (!gProgram) {
        LOGE("Could not create program.");
        return false;
    }
    loadTexture(col,row,P_pixels);


    u_matrix = glGetUniformLocation(gProgram, "u_matrix");
        checkGlError("glGetUniformLocation");
    u_texture = glGetUniformLocation(gProgram, "u_texture");
        checkGlError("glGetUniformLocation");


    a_position = glGetAttribLocation(gProgram,"a_position");
        checkGlError("glGetAttribLocation");
    LOGI("glGetAttribLocation(\"vPosition\") = %d\n",
    a_position);
    a_texture = glGetAttribLocation(gProgram,"a_texture");
            checkGlError("glGetAttribLocation");
    glViewport(0, 0, width, height);
    checkGlError("glViewport");
    return true;
}
const GLfloat gImageVertices[] = {
0.0f,  0.0f,
-0.9f, -0.9f,
0.9f, -0.9f,
0.9f,  0.9f,
        -0.9f,  0.9f,
        -0.9f, -0.9f,
};
const GLfloat gImageFragment[]={
0.5f,  0.5f,
0.0f,  1.0f,
1.0f,  1.0f,
1.0f,  0.0f,
0.0f,  0.0f,
0.0f,  1.0f
};
void renderFrame(GLuint textureObjectIds) {
    static float grey;
    grey += 0.01f;
    if (grey > 1.0f) {
        grey = 0.0f;
    }
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    glUseProgram(gProgram);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureObjectIds);
    glUniform1f(u_texture,0);
    glVertexAttribPointer(a_position, 2, GL_FLOAT, GL_FALSE, 0, gImageVertices);
    glEnableVertexAttribArray(a_position);
    glVertexAttribPointer(a_texture,  2, GL_FLOAT, GL_FALSE, 0, gImageFragment);
    glEnableVertexAttribArray(a_texture);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
}


extern "C" {
    JNIEXPORT void JNICALL Java_com_example_showimage_GL2JNILib_init(
    JNIEnv * env, jobject obj,  jint width, jint height, int col,int row,jintArray buf);
    JNIEXPORT void JNICALL Java_com_example_showimage_GL2JNILib_step(
    JNIEnv * env, jobject obj);
};


JNIEXPORT void JNICALL Java_com_example_showimage_GL2JNILib_init(
JNIEnv * env, jobject obj,  int width, int height, int col,int row,jintArray buf)
{
jint* cbuf;
cbuf=env->GetIntArrayElements(buf,NULL);


 Mat myimg4(row,col,CV_8UC4,cbuf);
 Mat myimg3(row,col,CV_8UC3);
 int i=0;
 int count=row*col;
 for(i=0;i
 {
 myimg3.data[3*i]  = myimg4.data[4*i+2];
 myimg3.data[3*i+1]= myimg4.data[4*i+1];
 myimg3.data[3*i+2]= myimg4.data[4*i];
 }
 setupGraphics(width,height,col,row,myimg3.data);
}
JNIEXPORT void JNICALL Java_com_example_showimage_GL2JNILib_step(
JNIEnv * env, jobject obj)
{
renderFrame(textureObjectIds);
}
在jni中新建文件Android.mk,并添加代码如下:

LOCAL_PATH:= $(call my-dir)


include $(CLEAR_VARS)


OPENCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off
OPENCV_LIB_TYPE:=STATIC


include E:\Backup\ADT\OpenCV-2.4.8\sdk\native\jni\OpenCV.mk
 //Opencv布局文件存在的目录


LOCAL_MODULE    := gl2jni
LOCAL_CFLAGS    := -Werror
LOCAL_SRC_FILES := image.cpp
LOCAL_LDLIBS    += -llog -lGLESv2   //添加OpenGL ES 2.0链接库


include $(BUILD_SHARED_LIBRARY)
在jni中新建文件Application.mk,并添加如下代码:

APP_STL:=gnustl_static  
APP_CPPFLAGS:=-frtti -fexceptions  
APP_ABI:=armeabi-v7a x86
APP_PLATFORM:=android-9

在文件AndroidManifest.xml中添加一行代码如下:

在Android中的jni里使用OpenGL ES 渲染一幅图片_第2张图片

运行程序得到结果如下:

在Android中的jni里使用OpenGL ES 渲染一幅图片_第3张图片


本程序的源码下载在:http://download.csdn.net/detail/lu_android/9628673

你可能感兴趣的:(在Android中的jni里使用OpenGL ES 渲染一幅图片)