Skip to content

狗子窝

没事就吠一下

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

2025 年 5 月
日 一 二 三 四 五 六
 123
45678910
11121314151617
18192021222324
25262728293031
« 2 月    

评论

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

归档

分类

TAG

Android c C++ domain EGE host iOS JavaScript NDK OpenAL OpenGL Slideshow WebGL教程 WGE 人脸识别 作文 分形 动漫 在线演示 小程序 常识 数学 游戏 源代码 滤镜 算法 老文翻新 表白 视频 评论 谷歌娘 酸腐文章 钢琴 音乐
© 2025 狗子窝 | Powered by Superbs Personal Blog theme