在Android中用ndk层egl/opengl es显示java层打开的图像

本文介绍了如何在Android的java层打开一张图片,并在native层进行渲染的方法。可以分为两个部分:
1. 在java层打开图片并通过jni接口将图像数据传给native层;
2. 在ndk层通过egl和opengl es来渲染图像;

首先,我们建立一个空的工程,并在activity_main.xml文件中添加SurfaceView:

<SurfaceView android:id="@+id/SurfaceView01"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>

然后,在工程目录下创建jni目录,并添加如下文件:
Application.mk

APP_ABI := all

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := eglbitmapdemo
LOCAL_SRC_FILES := eglbitmapdemo-jni.cpp \
                   EGLEngine.cpp \
                   GLRender.cpp

LOCAL_LDLIBS    := -llog -lGLESv2 -lEGL -landroid -ljnigraphics

include $(BUILD_SHARED_LIBRARY) 

eglbitmapdemo-jni.cpp

#include <jni.h>
#include <android/bitmap.h>
#include "EGLEngine.h"
#include "JNILog.h"

EGLEngine *eglEngine = NULL;

#ifdef __cplusplus
extern "C" {
#endif

int
Java_com_example_eglbitmapdemo_MainActivity_setupGraphic( JNIEnv* env, jobject thiz, jobject jsurface)
{
    int ret = 0;
    ANativeWindow *nativeWindow = NULL;

    LOGI("Java_com_example_hellojni_HelloJni_setupGraphic enter");

    nativeWindow = ANativeWindow_fromSurface(env, jsurface);

    if (eglEngine) {
        return 0;
    }

    eglEngine = new EGLEngine();
    if (!eglEngine) {
        goto failed;
    }

    ret = eglEngine->init(nativeWindow);
    if (ret) {
        goto failed;
    }

    return 0;
failed:
    if (eglEngine)
        delete eglEngine;
    return -1;
}


int
Java_com_example_eglbitmapdemo_MainActivity_showBitmap(JNIEnv* env, jobject thiz, jobject jbitmap)
{
    int ret = 0;
    EGLEngine *egl = NULL;
    AndroidBitmapInfo bitmapInfo;
    void *pixels = NULL;
    int imgWidth = 2;
    int imgHeight = 2;

    LOGI("Java_com_example_hellojni_HelloJni_showBitmap enter");

    if ((ret = AndroidBitmap_getInfo(env, jbitmap, &bitmapInfo)) < 0) {
        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
        return -1;
    }

    LOGI("Java_com_example_hellojni_HelloJni_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) {
        LOGE("Java_com_example_hellojni_HelloJni_showBitmap invalid rgb format");
        return -1;
    }

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

    eglEngine->loadImage(pixels, imgWidth, imgHeight);
    eglEngine->draw();

    AndroidBitmap_unlockPixels(env, jbitmap);

    return 0;
}

#ifdef __cplusplus
}
#endif

EGLEngine.cpp

/*
 * EGLEngine.cpp
 *
 *  Created on: 2015103日
 *      Author: leon
 */
#include "EGLEngine.h"


EGLEngine::EGLEngine() {
    mWin = NULL;
}

EGLEngine::~EGLEngine() {

}

int EGLEngine::init(ANativeWindow * surface) {
    if (!surface) {
        return -1;
    }

    mWin = surface;

    GLint majorVersion;
    GLint minorVersion;

    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if(EGL_NO_DISPLAY == mEglDisplay) {
        return -1;
    }

    if(!eglInitialize(mEglDisplay, &majorVersion, &minorVersion)) {
        return -1;
    }

    EGLint config_attribs[] = {
        EGL_BLUE_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_NONE
    };

    int num_configs = 0;
    EGLConfig   eglConfig;
    if(!eglChooseConfig(mEglDisplay, config_attribs, &eglConfig, 1, &num_configs))
    {
        return -1;
    }

    mEglSurface = eglCreateWindowSurface(mEglDisplay, eglConfig, mWin, NULL);
    if(EGL_NO_SURFACE == mEglSurface) {
        return -1;
    }

    if(!eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &mSurfaceWidth) ||
       !eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &mSurfaceHeight)) {
        return -1;
    }

    EGLint context_attrib[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };

    mEglContext = eglCreateContext(mEglDisplay, eglConfig, EGL_NO_CONTEXT, context_attrib);
    if(EGL_NO_CONTEXT == mEglContext) {
        return -1;
    }

    if(!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
        return -1;
    }

    glViewport(0, 0, mSurfaceWidth, mSurfaceHeight);
    glClearColor(1.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glDisable(GL_CULL_FACE);

    LOGI("EGLEngine init success: surface width %d, surface height %d", mSurfaceWidth, mSurfaceHeight);

    imageRender = new GLRender();

    return 0;
}

void EGLEngine::release() {
    if (mEglDisplay != EGL_NO_DISPLAY) {
        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        if (mEglContext != EGL_NO_CONTEXT) {
            eglDestroyContext(mEglDisplay, mEglContext);
        }
        if (mEglSurface != EGL_NO_SURFACE) {
            eglDestroySurface(mEglDisplay, mEglSurface);

        }

        if (!eglTerminate(mEglDisplay)) {

        }
    }

    mEglDisplay = EGL_NO_DISPLAY;
    mEglContext = EGL_NO_CONTEXT;
    mEglSurface = EGL_NO_SURFACE;
}

int EGLEngine::loadImage(void *pixels, int width, int height) {
    int ret = 0;

    LOGI("EGLEngine::loadImage width %d, height %d", width, height);

    ret = imageRender->setup(pixels, width, height);
    if (ret) {
        LOGE("EGLEngine::loadImage failed");
        return -1;
    }

    LOGI("EGLEngine::loadImage done");

    return 0;
}

int EGLEngine::draw() {
    int ret = 0;

    ret = imageRender->draw();
    if (ret) {
        LOGE("EGLEngine::draw failed");
        return -1;
    }

    eglSwapBuffers(mEglDisplay, mEglSurface);

    LOGI("EGLEngine::draw done");

    return 0;
}

GLRender.cpp

/*
 * GLRender.cpp
 *
 *  Created on: 2015年10月4日
 *      Author: leon
 */

#include "GLRender.h"


GLRender::GLRender():
mIndices(NULL),
mVertex(NULL),
mVertexSize(0),
mIndicesSize(0){
    mVertexShader = STRINGIZE(
        attribute vec4 a_position;
        attribute vec2 a_tex_coord_in;
        varying vec2 v_tex_coord_out;

        void main(void) {
            v_tex_coord_out = a_tex_coord_in;
            gl_Position = a_position;
        }
    );

    mFragmentShader = STRINGIZE(
        precision highp float;
        varying highp vec2 v_tex_coord_out;
        uniform sampler2D s_texture;
        void main() {
            vec4 mzs = texture2D(s_texture, v_tex_coord_out);
            gl_FragColor = vec4(mzs.rgb, 0.0);
        }
    );
}

GLRender::~GLRender() {
    if (mGlVShader) {
        glDetachShader(mGlProgram, mGlVShader);
        glDeleteShader(mGlVShader);
        mGlVShader = 0;
    }
    if (mGlFShader) {
        glDetachShader(mGlProgram, mGlFShader);
        glDeleteShader(mGlFShader);
        mGlFShader = 0;
    }
    if (mGlProgram) {
        glDeleteProgram(mGlProgram);
        mGlProgram = 0;
    }
}

int GLRender::setup(void *pixels, int width, int height) {
    LOGI("GLRender::setup enter, pixels %x, width %d, height %d", pixels, width, height);

    mGlProgram = createProgram(mVertexShader, mFragmentShader);
    if (!mGlProgram) {
        LOGE("GLRender::setup createProgram failed");
        return -1;
    }

    glUseProgram(mGlProgram);

    if (setupVertex()) {
        LOGE("GLRender::setup setupVertex failed");
        return -1;
    }

    if (setupTexture()) {
        LOGE("GLRender::setup setupTexture failed");
        return -1;
    }

    if (loadImage((uint8_t *)pixels, width, height)) {
        LOGE("GLRender::setup loadImage failed");
        return -1;
    }

    LOGI("GLRender::setup done");

    return 0;
}

GLuint GLRender::createProgram(const char *vshaderSrc, const char *fshaderSrc) {
    GLuint programObject;
    GLint  linked;

    mGlVShader = loadShader(GL_VERTEX_SHADER, vshaderSrc);
    if (!mGlVShader) {
        LOGE("vertex shader initialize wrong. ");
        return (0);
    }
    mGlFShader = loadShader(GL_FRAGMENT_SHADER, fshaderSrc);
    if (!mGlFShader)
    {
        LOGE("fragment shader initialize wrong. ");
        return (0);
    }

    //Create the program object
    programObject = glCreateProgram();

    if (programObject == 0)
        return (0);

    glAttachShader(programObject, mGlVShader);
    glAttachShader(programObject, mGlFShader);

    //Link the program
    glLinkProgram(programObject);

    //Check the link status
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);

    if(!linked)
    {
        GLint infoLen;

        glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
        LOGI("Error linking length: %d", infoLen);

        if(infoLen > 1)
        {
            char* infoLog = (char*)malloc(infoLen);

            glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
            LOGE("Error linking program: %s, %s", infoLog, glGetString(glGetError()));


            free(infoLog);
        }

        glDeleteProgram(programObject);
        return (0);
    }

    //Store the program object

    return (programObject);
}

GLuint GLRender::loadShader(GLenum type, const char *shaderSrc) {
    GLuint shader;
    shader = glCreateShader(type);
    if (shader) {
        glShaderSource(shader,1, &shaderSrc, NULL);
        glCompileShader(shader);
        GLint compile;
        compile = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compile);
        if (!compile) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if(infoLen > 1)
            {
                char* infoLog = (char*)malloc(infoLen);

                glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
                LOGI( "Error compiling shader: %s", infoLog);
                free(infoLog);
            }

            glDeleteShader(shader);
            return (0);
        }
    } else
    {
        return (0);
    }
    return (shader);
}

int GLRender::setupVertex() {
    Vertex vertices[4];
    GLubyte indices[] = {0, 1, 2, 2, 3, 0};
    GLuint a_position;
    GLuint a_tex_coord_in;

    mVertex = (Vertex *)calloc(sizeof(Vertex)*4, 1);
    if (!mVertex) {
        goto failed;
    }
    mIndices = (GLubyte *)calloc(sizeof(GLubyte)*6, 1);
    if (!mIndices) {
        goto failed;
    }

    vertices[0] = { {1.0f, -1.0f, 0.0f}, {1 ,1}};
    vertices[1] = { {1.0f, 1.0f, 0.0f}, {1,0}};
    vertices[2] = { {-1.0f, 1.0f, 0.0f}, {0,0}};
    vertices[3] = { {-1.0f, -1.0f, 0.0f}, {0,1}};

    memcpy(mVertex, vertices, sizeof(vertices));
    memcpy(mIndices, indices, sizeof(indices));
    mVertexSize = sizeof(vertices);
    mIndicesSize = sizeof(indices);

    glGenBuffers(1, &mVertexBuf);
    glGenBuffers(1, &mIndicesBuf);

    a_position = glGetAttribLocation(mGlProgram, "a_position");
    a_tex_coord_in = glGetAttribLocation(mGlProgram, "a_tex_coord_in");

    glEnableVertexAttribArray(a_position);
    glEnableVertexAttribArray(a_tex_coord_in);

    glBindBuffer(GL_ARRAY_BUFFER, mVertexBuf);
    glBufferData(GL_ARRAY_BUFFER, mVertexSize, mVertex, GL_STATIC_DRAW);

    glVertexAttribPointer(a_position, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
    glVertexAttribPointer(a_tex_coord_in, 2, GL_FLOAT, GL_TRUE, sizeof(Vertex),
            (GLvoid*) (sizeof(float) * 3));

    return 0;
failed:
    if (mVertex) {
        free(mVertex);
        mVertex = NULL;
    }

    if (mIndices) {
        free(mIndices);
        mIndices = NULL;
    }

    return -1;
}

int GLRender::setupTexture() {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glActiveTexture(GL_TEXTURE0);

    glGenTextures(1, &mTextureId);
    glBindTexture(GL_TEXTURE_2D, mTextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    return 0;
}

int GLRender::loadImage(uint8_t *pixels, int width, int height) {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, mTextureId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    GLuint location = glGetUniformLocation(mGlProgram, "s_texture");
    glUniform1i(location, 0);

    return 0;
}

int GLRender::draw() {
    glUseProgram(mGlProgram);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    return 0;
}

用ndk-build编译出jni动态库文件。

将要显示的图像拷贝到assets目录下。

编辑MainActivity.java代码如下:

package com.example.eglbitmapdemo;

import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.SurfaceView01);

        SurfaceHolder surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            public void surfaceChanged(SurfaceHolder holder, int format,
                    int width, int height) {

            }

            public void surfaceCreated(SurfaceHolder holder) {
                AssetManager manager = getAssets();
                InputStream open;
                Bitmap bitmap;
                try {
                    open = manager.open("dog.jpg");
                    bitmap = BitmapFactory.decodeStream(open);
                    setupGraphic(holder.getSurface());                  
                    showBitmap(bitmap);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }                           
            }

            public void surfaceDestroyed(SurfaceHolder holder) {

            }
        });
    }

    public native int setupGraphic(Surface surface);    
    public native int showBitmap(Bitmap bitmap);

    static {
        System.loadLibrary("eglbitmapdemo");
    }
}

至此,我们的主要代码就结束了。
它的工作原理是,通过java代码生成图像的Bitmap对象,通过jni接口将SurfaceView对象和Bitmap对象传给native代码,native代码从传入的Bitmap对象中获取rgb数据,并通过egl和opengl es进行渲染。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值