Android OpenGLES2.0(十五)——利用EGL后台处理图像

版权声明:欢迎转载,转载请保留文章出处。 https://blog.csdn.net/junzia/article/details/56008902

Android OpenGLES2.0(十二)——FBO离屏渲染中,记录了采用FBO进行离屏渲染的方式来进行后台处理图像,但是在使用时,示例中还是用到了GLSurfaceView控件来提供GL环境。怎样完全抛开GLSurfaceView来进行图像处理呢?

OpenGLES和EGL

OpenGL(全写Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。

EGL是介于诸如OpenGL 或OpenVG的Khronos渲染API与底层本地平台窗口系统的接口。它被用于处理图形管理、表面/缓冲捆绑、渲染同步及支援使用其他Khronos API进行的高效、加速、混合模式2D和3D渲染。

由上可以理解为,OpenGLES的使用,离不开EGL。而我们在使用GLSurfaceView时并不需要和EGL有所交集,是因为GLSurfaceView在内部做好了相关的工作,为我们使用OpenGLES创造好了环境。所以GLSurfaceView可以当做一个EGL使用的示例来看。

EGL使用步骤

GLSurfaceView就不去去分析了,直接给出结论。Android中EGL的使用步骤为:
1. 取得EGL实例
2. 选择Display
3. 选择Config
4. 创建Surface
5. 创建Context
6. 指定当前的环境为绘制环境

代码实现

根据EGL的使用步骤,先来创建一个EGL环境创建的帮助类,代码相关说明在类中给出了注释:

public class EGLHelper {

    public EGL10 mEgl;
    public EGLDisplay mEglDisplay;
    public EGLConfig mEglConfig;
    public EGLSurface mEglSurface;
    public EGLContext mEglContext;
    public GL10 mGL;

    private static final int EGL_CONTEXT_CLIENT_VERSION=0x3098;

    public static final int SURFACE_PBUFFER=1;
    public static final int SURFACE_PIM=2;
    public static final int SURFACE_WINDOW=3;

    private int surfaceType=SURFACE_PBUFFER;
    private Object surface_native_obj;

    private int red=8;
    private int green=8;
    private int blue=8;
    private int alpha=8;
    private int depth=16;
    private int renderType=4;
    private int bufferType=EGL10.EGL_SINGLE_BUFFER;
    private EGLContext shareContext=EGL10.EGL_NO_CONTEXT;


    public void config(int red,int green,int blue,int alpha,int depth,int renderType){
        this.red=red;
        this.green=green;
        this.blue=blue;
        this.alpha=alpha;
        this.depth=depth;
        this.renderType=renderType;
    }

    public void setSurfaceType(int type,Object ... obj){
        this.surfaceType=type;
        if(obj!=null){
            this.surface_native_obj=obj[0];
        }
    }

    public GlError eglInit(int width,int height){
        int[] attributes = new int[] {
                EGL10.EGL_RED_SIZE, red,  //指定RGB中的R大小(bits)
                EGL10.EGL_GREEN_SIZE, green, //指定G大小
                EGL10.EGL_BLUE_SIZE, blue,  //指定B大小
                EGL10.EGL_ALPHA_SIZE, alpha, //指定Alpha大小,以上四项实际上指定了像素格式
                EGL10.EGL_DEPTH_SIZE, depth, //指定深度缓存(Z Buffer)大小
                EGL10.EGL_RENDERABLE_TYPE, renderType, //指定渲染api版本, EGL14.EGL_OPENGL_ES2_BIT
                EGL10.EGL_NONE };  //总是以EGL10.EGL_NONE结尾

        //获取Display
        mEgl= (EGL10)EGLContext.getEGL();
        mEglDisplay=mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

        int[] version=new int[2];    //主版本号和副版本号
        mEgl.eglInitialize(mEglDisplay,version);
        //选择Config
        int[] configNum=new int[1];
        mEgl.eglChooseConfig(mEglDisplay,attributes,null,0,configNum);
        if(configNum[0]==0){
            return GlError.ConfigErr;
        }
        EGLConfig[] c=new EGLConfig[configNum[0]];
        mEgl.eglChooseConfig(mEglDisplay,attributes,c,configNum[0],configNum);
        mEglConfig=c[0];
        //创建Surface
        int[] surAttr=new int[]{
            EGL10.EGL_WIDTH,width,
            EGL10.EGL_HEIGHT,height,
            EGL10.EGL_NONE
        };
        mEglSurface=createSurface(surAttr);
        //创建Context
        int[] contextAttr=new int[]{
            EGL_CONTEXT_CLIENT_VERSION,2,
            EGL10.EGL_NONE
        };
        mEglContext=mEgl.eglCreateContext(mEglDisplay,mEglConfig,shareContext,contextAttr);
        makeCurrent();
        return GlError.OK;
    }

    public void makeCurrent(){
        mEgl.eglMakeCurrent(mEglDisplay,mEglSurface,mEglSurface,mEglContext);
        mGL= (GL10)mEglContext.getGL();
    }

    public void destroy(){
        mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
            EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
        mEgl.eglTerminate(mEglDisplay);
    }

    private EGLSurface createSurface(int[] attr){
        switch (surfaceType){
            case SURFACE_WINDOW:
                return mEgl.eglCreateWindowSurface(mEglDisplay,mEglConfig,surface_native_obj,attr);
            case SURFACE_PIM:
                return mEgl.eglCreatePixmapSurface(mEglDisplay,mEglConfig,surface_native_obj,attr);
            default:
                return mEgl.eglCreatePbufferSurface(mEglDisplay,mEglConfig,attr);
        }
    }

}

利用帮助类,实现一个可以设置Filter的后台图像处理类:

public class GLES20BackEnv {

    private int mWidth;
    private int mHeight;
    private EGLHelper mEGLHelper;

    final static String TAG = "GLES20BackEnv";
    final static boolean LIST_CONFIGS = false;

    private AFilter mFilter;
    Bitmap mBitmap;
    String mThreadOwner;

    public GLES20BackEnv(int width,int height){
        this.mWidth=width;
        this.mHeight=height;
        mEGLHelper=new EGLHelper();
        mEGLHelper.eglInit(width,height);
    }

    public void setThreadOwner(String threadOwner){
        this.mThreadOwner=threadOwner;
    }

    public void setFilter(final AFilter filter) {
        mFilter = filter;

        // Does this thread own the OpenGL context?
        if (!Thread.currentThread().getName().equals(mThreadOwner)) {
            Log.e(TAG, "setRenderer: This thread does not own the OpenGL context.");
            return;
        }
        // Call the renderer initialization routines
        mFilter.create();
        mFilter.size(mWidth, mHeight);
    }

    public Bitmap getBitmap() {
        if (mFilter == null) {
            Log.e(TAG, "getBitmap: Renderer was not set.");
            return null;
        }
        if (!Thread.currentThread().getName().equals(mThreadOwner)) {
            Log.e(TAG, "getBitmap: This thread does not own the OpenGL context.");
            return null;
        }
        mFilter.setTextureId(createTexture(mBitmap));
        mFilter.draw();
        return convertToBitmap();
    }

    public void destroy() {
        mEGLHelper.destroy();
    }


    private Bitmap convertToBitmap() {
        int[] iat = new int[mWidth * mHeight];
        IntBuffer ib = IntBuffer.allocate(mWidth * mHeight);
        mEGLHelper.mGL.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
            ib);
        int[] ia = ib.array();

        // Convert upside down mirror-reversed image to right-side up normal
        // image.
        for (int i = 0; i < mHeight; i++) {
            System.arraycopy(ia, i * mWidth, iat, (mHeight - i - 1) * mWidth, mWidth);
        }
        Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        bitmap.copyPixelsFromBuffer(IntBuffer.wrap(iat));
        return bitmap;
    }

    public void setInput(Bitmap bitmap){
        this.mBitmap=bitmap;
    }

    private int createTexture(Bitmap bmp){
        int[] texture=new int[1];
        if(bmp!=null&&!bmp.isRecycled()){
            //生成纹理
            GLES20.glGenTextures(1,texture,0);
            //生成纹理
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,texture[0]);
            //设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
            //设置放大过滤为使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
            //设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
            //设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
            //根据以上指定的参数,生成一个2D纹理
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
            return texture[0];
        }
        return 0;
    }

}

然后就可以利用这个后台图像处理的工具类,直接处理图像了:

mBackEnv=new GLES20BackEnv(mBmpWidth,mBmpHeight);
mBackEnv.setThreadOwner(getMainLooper().getThread().getName());
mBackEnv.setFilter(new GrayFilter(getResources()));
mBackEnv.setInput(bmp);
saveBitmap(mBackEnv.getBitmap());

源码

所有的代码全部在一个项目中,托管在Github上——Android OpenGLES 2.0系列博客的Demo


欢迎转载,转载请保留文章出处。湖广午王的博客[http://blog.csdn.net/junzia/article/details/56008902]


猜你喜欢

转载自blog.csdn.net/junzia/article/details/56008902