Skip to content

大师兄的博客

专注无聊20年,数过的小羊连起来可以绕地球两圈

Menu
  • 首页
  • 技术交流
    • 原创
    • WebGL教程
    • js小程序在线演示
    • 摘要总结
  • 小评论
    • 动漫短评
    • 其他短评
  • 日志/心情
    • My Piano
    • 絮絮叨叨
    • 长篇大论
  • 关于我
    • 留言板
Menu

Android下使用NDK(C++)+GLES2.0进行后台绘图,保存到bitmap并交给java层处理

Posted on 2013 年 12 月 28 日2015 年 3 月 13 日 by 糖 玄奘

前段时间为了这个问题纠结了很久,总算找到了还算不错的解决方法,现总结如下。

首先要明白我说的是一个什么东西:

我们要使用OpenGL进行后台处理,但并不需要实时显示到我们app的窗口上,而仅仅存储在Bitmap中。涉及的应用场景有很多比如:你需要做一个图像处理的app,你希望界面上最多有个image view之类的东西显示待处理/处理好的图片。所以你不可能会为了后台处理而在前台界面上挂上一个glSurfaceView或者glTextureView之类的东西上去。

因为对Android并不熟悉,我在谷歌和百度上查找了很久,甚至跑到stackoverflow.com上去提问,总算找到可行的方案:PBufferSurface 和 PixmapSurface

但是找到的方案都没有例子,大概像这样用的人比较少,描述到它们的时候都是一句话带过,所以本文将详细描述一下。

安卓下使用EGL来创建context。首先是khronos官方文档描述: eglCreatePbufferSurface 以及 eglCreatePixmapSurface

看了这两个函数,你大概就明白了。那么我们应该使用哪个呢?

首先要明白你的需求,假如你是要处理图像的话,显然不管你用哪个,你最终都必须绘制到FBO里面再取出来的。为什么? 因为你在创建context的时候是不知道你要处理的图像的大小的(……如果你要每次加载图片就重新初始化,那……我不管了)。所以就算你用了pixmap surface,直接绘制的话也会因为图像大小而纠结不已。

所以本文直接选择了pbuffer,效率比pixmap更高。

那么我们要做的就很简单了,首先使用adt创建一个demo吧,在主activity的onCreate方法前面加上: @SuppressLint(“NewApi”).  如果已经加了的话就不用管了。

为了便于管理,我们新建一个java文件把所有的初始化写到一个class里面:

GLHelpFunctions.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package org.wysaid.ndkopenglbackdraw;
 
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;
 
import android.graphics.Bitmap;
import android.util.Log;
 
public class GLHelpFunctions {
 
public static native void getGLBackDrawImage(Bitmap bm);
 
// use GLES2.0.
static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
static int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
static private int[] version = new int[2];
static EGLConfig[] configs = new EGLConfig[1];
static int[] num_config = new int[1];
 
static int[] configSpec = { EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
EGL10.EGL_RED_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_ALPHA_SIZE, 8, EGL10.EGL_NONE };
// eglCreatePbufferSurface used this config
static int attribListPbuffer[] = {
// The NDK code would never draw to Pbuffer, so it's not neccessary to
// match anything.
EGL10.EGL_WIDTH, 32, EGL10.EGL_HEIGHT, 32, EGL10.EGL_NONE };
static EGL10 mEgl;
static GL10 gl;
static javax.microedition.khronos.egl.EGLSurface mEglPBSurface;
static EGLContext mEglContext;
static EGLConfig mEglConfig;
static EGLDisplay mEglDisplay;
 
static public void initEGL() {
mEgl = (EGL10) EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
mEgl.eglInitialize(mEglDisplay, version);
mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, num_config);
mEglConfig = configs[0];
mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig,
EGL10.EGL_NO_CONTEXT, attrib_list);
if (mEglContext == EGL10.EGL_NO_CONTEXT) {
Log.d("ERROR:", "eglCreateContext Failed!");
}
mEglPBSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig,
attribListPbuffer);
if (mEglPBSurface == EGL10.EGL_NO_SURFACE) {
Log.d("ERROR:", "eglCreatePbufferSurface Failed!");
}
 
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglPBSurface, mEglPBSurface, mEglContext)) {
Log.d("ERROR:", "eglMakeCurrent failed:" + mEgl.eglGetError());
}
// You can do some works using OpenGL with java code. But this demo would do that within NDK.
gl = (GL10) mEglContext.getGL();
}
 
static public void enableEGL() {
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglPBSurface, mEglPBSurface, mEglContext))
{
Log.d("ERROR:", "eglMakeCurrent failed:" + mEgl.eglGetError());
}
}
}

然后在创建的时候调用 initEGL, 使用后台绘图之前调用enableEGL即可。

当然,也许你还是可能会遇到各种各样的问题,所以给出一个完整的demo以供参考:

点击跳转到demo下载页面。 <– 如果打不开,请把地址的https换成http,谷歌的服务器比较扯蛋。

来一张demo效果图吧。界面上只有一个button和一个imageView。

OpenGLBackDraw

 

5 thoughts on “Android下使用NDK(C++)+GLES2.0进行后台绘图,保存到bitmap并交给java层处理”

  1. JM_TD说道:
    2014 年 1 月 22 日 上午 11:36

    实在是太好了,现在正常了,多谢多谢!

  2. JM_TD说道:
    2014 年 1 月 21 日 下午 6:29

    大师兄,为啥虚拟机上能正常显示,到了真机上就没有显示了?

    1. wysaid说道:
      2014 年 1 月 21 日 下午 6:32

      嗯,当时也发现了,但是已经上传了,就懒得管了,你们如果看一下shader代码的话应该能看出来的。GLES不默认gl_FragColor.a为1
      你在Fragment shader末尾加上一句 gl_FragColor.a = 1.0; 就ok了

      1. JM_TD说道:
        2014 年 1 月 21 日 下午 6:44

        多谢大师兄回复。不过刚刚加了还是显示不了……

        1. wysaid说道:
          2014 年 1 月 21 日 下午 6:53

          好像是我记错了,是vertex shader里面不同,我重传了一份,你重新下载一下,还是上面的地址

Comments are closed.


转载本Blog文章请注明出处:
wysaid.org

2023年 3月
日 一 二 三 四 五 六
 1234
567891011
12131415161718
19202122232425
262728293031  
« 7月    

评论

  • luo.la发表在《使用OpenAL打开麦克风录音并实时回放(类似K歌效果)》
  • 罗拉发表在《使用OpenAL打开麦克风录音并实时回放(类似K歌效果)》
  • 大喜发表在《使用OpenAL打开麦克风录音并实时回放(类似K歌效果)》
  • 罗拉套图网发表在《使用OpenAL打开麦克风录音并实时回放(类似K歌效果)》
  • 爱就爱啦发表在《使用OpenAL打开麦克风录音并实时回放(类似K歌效果)》

归档

分类

TAG

Android c C++ domain EGE host iOS JavaScript NDK OpenAL OpenGL Slideshow WebGL教程 WGE 人脸识别 分形 动漫 在线演示 小程序 常识 数学 游戏 源代码 滤镜 算法 表白 视频 评论 谷歌娘 钢琴 音乐
© 2023 大师兄的博客 | Powered by Superbs Personal Blog theme