自定义GLSurfaceView

 第一步:创建EGLUtil工具类,主要是封装了EGL环境的创建函数

package com.leilu.mycamera;


import android.opengl.EGLExt;
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.egl.EGLSurface;

import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY;
import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;

/**
 * 创建EGL环境的工具类,根据GLSurfaceView源码,创建EGL环境主要分为以下几步
 * 1.获取EGL对象
 * 2.调用eglGetDisplay方法获取EGLDisplay,然后调用eglInitialize方法初始化
 * 3.调用eglChooseConfig进行配置
 * 4.调用eglCreateContext方法创建EGLContext
 * 5.调用eglCreateWindowSurface方法创建EGLSurface
 * 6.调用eglMakeCurrent方法绑定EGLSurface到EGLContext
 * 7.绘制完成后调用eglSwapBuffers进行缓冲区交换
 * 

* 1.所有的方法方法调用都需要在同一个线程里面实现 * 2.需要注意的问题:由于surface可能改变,所以在surface改变的时候需要重新调用 * createWindowSurface方法和eglMakeCurrent方法进行EGLSurface的重新创建,否则可能 * 不能正常显示 *

* Created by ll on 2018/9/10. */ public class EGLUtil { public static final int VERSION_2 = 2; public static final int VERSION_3 = 3; private EGLContext mEglContext = EGL_NO_CONTEXT; private EGLSurface mEglSurface = EGL_NO_SURFACE; private EGLDisplay mEglDisplay = EGL_NO_DISPLAY; private EGL10 mEGL; private EGLConfig mEGLConfig; private int mVersion = VERSION_2; public EGLUtil() { this(VERSION_2); } public EGLUtil(int version) { if (version == VERSION_2 || version == VERSION_3) { mVersion = version; } } public boolean init(EGLContext shareContext) { mEGL = (EGL10) EGLContext.getEGL(); mEglDisplay = mEGL.eglGetDisplay(EGL_DEFAULT_DISPLAY); if (mEglDisplay == EGL_NO_DISPLAY) { Log.i("==", "eglGetDisplay failed!"); return false; } if (!mEGL.eglInitialize(mEglDisplay, null)) { Log.i("==", "eglInitialize failed!"); return false; } if (!chooseConfig()) { mEglDisplay = EGL_NO_DISPLAY; return false; } if (!createEGLContext(shareContext)) { mEglDisplay = EGL_NO_DISPLAY; mEglContext = EGL_NO_CONTEXT; return false; } return true; } private boolean chooseConfig() { int[] num_config = new int[1]; int type = EGL_OPENGL_ES2_BIT; if (mVersion == VERSION_3) { type = EGLExt.EGL_OPENGL_ES3_BIT_KHR; } int[] attributeList = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, // EGL_ALPHA_SIZE, 8, // EGL_DEPTH_SIZE, 16, // EGL_STENCIL_SIZE, 8, EGL_RENDERABLE_TYPE, type, EGL_NONE }; if (!mEGL.eglChooseConfig(mEglDisplay, attributeList, null, 0, num_config)) { Log.i("==", "eglChooseConfig failed!"); return false; } int numConfigs = num_config[0]; if (numConfigs <= 0) { Log.i("==", "eglChooseConfig failed:numConfigs <= 0!"); return false; } EGLConfig[] configs = new EGLConfig[numConfigs]; if (!mEGL.eglChooseConfig(mEglDisplay, attributeList, configs, numConfigs, num_config)) { Log.i("==", "eglChooseConfig#2 failed"); } mEGLConfig = configs[0]; return true; } public boolean createEGLContext(EGLContext shareContext) { if (mEglDisplay == EGL_NO_DISPLAY || mEGLConfig == null) { return false; } int attrib_list[] = { EGL_CONTEXT_CLIENT_VERSION, mVersion, EGL_NONE }; if (shareContext != null) { mEglContext = mEGL.eglCreateContext(mEglDisplay, mEGLConfig, shareContext, attrib_list); } else { mEglContext = mEGL.eglCreateContext(mEglDisplay, mEGLConfig, EGL_NO_CONTEXT, attrib_list); } if (mEglContext == EGL_NO_CONTEXT) { mEglDisplay = EGL_NO_DISPLAY; Log.i("==", "eglCreateContext failed"); return false; } return true; } public boolean eglMakeCurrent() { if (mEglDisplay == EGL_NO_DISPLAY || mEglContext == EGL_NO_CONTEXT) { return false; } if (!mEGL.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { Log.i("==", "eglMakeCurrent failed"); return false; } return true; } public boolean createWindowSurface(Object surface) { releaseWindowSurface(); if (mEglDisplay == EGL_NO_DISPLAY) { return false; } mEglSurface = mEGL.eglCreateWindowSurface(mEglDisplay, mEGLConfig, surface, null); if (mEglSurface == EGL_NO_SURFACE) { Log.i("==", "eglCreateWindowSurface failed"); return false; } return true; } public boolean eglSwapBuffers() { if (mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE) { return mEGL.eglSwapBuffers(mEglDisplay, mEglSurface); } return false; } public boolean releaseWindowSurface() { if (mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE) { if (mEGL.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { return mEGL.eglDestroySurface(mEglDisplay, mEglSurface); } } return false; } public void release() { if (mEglDisplay != EGL_NO_DISPLAY) { mEGL.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); mEGL.eglDestroyContext(mEglDisplay, mEglContext); mEGL.eglDestroySurface(mEglDisplay, mEglSurface); mEGL.eglTerminate(mEglDisplay); } mEglDisplay = EGL_NO_DISPLAY; mEglContext = EGL_NO_CONTEXT; mEglSurface = EGL_NO_SURFACE; mEGLConfig = null; } public EGLContext getEGLContext() { return mEglContext; } }

第二步:创建EGLThread,因为opengl是基于状态的,所有的操作都需要在同一个线程中完成,所以需要创建一个专门给opengl

运行的线程

package com.leilu.mycamera;

import java.lang.ref.SoftReference;

import javax.microedition.khronos.egl.EGLContext;

/**
 * EGL线程,负责EGL环境的创建和渲染相关的回调
 * 

* 使用步骤: * 1.调用setSurface方法设置相应的surface(如果surface或者其大小发生了改变,则需要再次调用此方法,其他 * 方法不必重新调用) * 2.如果要共享其他EGLContext,则调用setEGLContext方法进行共享EGLContext的设置(非必须) * 3.调用setOnEGLThreadListener方法设置监听器(非必须) * 4.调用setRenderMode方法设置渲染模式(非必须) * 5.调用start方法开启EGL线程 * 6.调用stop方法停止EGL线程 *

* 补充: * 1.如果需要获取EGLContext,则调用getEGLContext方法获取,可能返回null * 2.调用isRunning方法可以判断EGL线程是否正在运行 * 3.如果是RENDERMODE_WHEN_DIRTY模式,则每次绘制完毕需要手动调用requestRender方法进行刷新 * Created by ll on 2018/9/11. */ public class EGLThread { public static final int RENDERMODE_WHEN_DIRTY = 0;// 手动刷新模式 public static final int RENDERMODE_CONTINUOUSLY = 1;// 不断刷新模式 private WorkThread mWorkThread;// EGL环境的线程 private boolean mIsRunning;// EGL线程是一个不断循环的线程,用来判断是否应该退出循环 private OnEGLThreadListener mOnEGLThreadListener;// 渲染监听 private int mRenderMode = RENDERMODE_CONTINUOUSLY;// 默认是不断刷新模式 private Object mSurface; private boolean mIsSurfaceChanged;// 当surface或者其大小发生改变的时候此属性为true private int mWidth, mHeight;// surface的宽高 private EGLContext mSharedEGLContext;// 共享的EGL上下文 /** * 设置surface,如果surface发生改变,则需要再次调用以绑定新的surface * * @param surface Surface或者SurfaceTexture * @param width * @param height */ public void setSurface(Object surface, int width, int height) { if (surface != null) { mSurface = surface; mWidth = width; mHeight = height; mIsSurfaceChanged = true; requestRender(); } } /** * 设置渲染模式 * RENDERMODE_WHEN_DIRTY或者RENDERMODE_CONTINUOUSLY * * @param renderMode */ public void setRenderMode(int renderMode) { if (renderMode == RENDERMODE_CONTINUOUSLY || renderMode == RENDERMODE_WHEN_DIRTY) { mRenderMode = renderMode; } } /** * 设置共享的EGLContext,此方法必须在start方法前调用 * * @param eglContext */ public synchronized void setEGLContext(EGLContext eglContext) { if (mWorkThread != null) { throw new IllegalStateException("The GLThread is running!"); } mSharedEGLContext = eglContext; } /** * EGL线程是否正在运行 * * @return */ public boolean isRunning() { return mIsRunning; } /** * 开启EGL线程 */ public synchronized void start() { if (mIsRunning) { return; } if (mSurface == null) { throw new IllegalStateException("The surface is null!"); } if (mWorkThread != null) { throw new IllegalStateException("The GLThread is running!"); } mIsRunning = true; mWorkThread = new WorkThread(new SoftReference<>(this)); mWorkThread.start(); } /** * 停止EGL线程 */ public synchronized void stop() { mIsRunning = false; requestRender(); if (mWorkThread != null) { mWorkThread.interrupt(); } } /** * 设置渲染监听 * * @param onEGLThreadListener */ public void setOnEGLThreadListener(OnEGLThreadListener onEGLThreadListener) { mOnEGLThreadListener = onEGLThreadListener; } /** * 如果是手动刷新模式,则需要调用此方法进行刷新 */ public void requestRender() { if (mWorkThread != null && mRenderMode == RENDERMODE_WHEN_DIRTY) { mWorkThread.requestRender(); } } /** * 获取EGLContext * * @return */ public EGLContext getEGLContext() { if (mWorkThread != null) { return mWorkThread.getEGLContext(); } return null; } private static class WorkThread extends Thread { // 同步锁,如果是RENDERMODE_WHEN_DIRTY模式,则会wait,当调用 // requestRender的时候就会调用notify private final Object renderModeLock = new Object(); private SoftReference softReference; private EGLUtil eglUtil;// 创建EGL环境的工具类 private boolean isCreate;// 用来表示是否是第一次调用onCreate方法 private boolean isFirstDraw;// 是否是第一次绘制 // 由于第一次绘制的时候总是显示不出来(原因未知),所以这里加一个变量来控制, // 如果是第一次绘制,则调用两次onDrawFrame方法,绘制完成后置为false private boolean isNeedDoubleDraw = true; public WorkThread(SoftReference softReference) { this.softReference = softReference; this.eglUtil = new EGLUtil(); } // 如果是RENDERMODE_WHEN_DIRTY模式,则会wait // 调用此方法取消wait public void requestRender() { synchronized (renderModeLock) { renderModeLock.notifyAll(); } } @Override public void run() { isCreate = true; isFirstDraw = true; if (eglUtil.init(softReference.get().mSharedEGLContext)) {// 如果EGL初始化成功 if (createWindowSurface(softReference.get())) { while (softReference.get() != null && softReference.get().mIsRunning) { EGLThread eglThread = softReference.get(); // 如果是手动刷新模式,则判断是否是第一次绘制,如果不是则diaoyngwait方法阻塞, // 等待用户手动调用requestRender的时候取消阻塞 if (eglThread.mRenderMode == RENDERMODE_WHEN_DIRTY) { if (!isFirstDraw) { synchronized (renderModeLock) { try { renderModeLock.wait(); } catch (InterruptedException e) { break; } } } isFirstDraw = false; } // onCreate if (isCreate) { if (eglThread.mOnEGLThreadListener != null) { eglThread.mOnEGLThreadListener.onCreate(); } isCreate = false; } // onSurfaceChanged if (eglThread.mIsSurfaceChanged) { if (!createWindowSurface(eglThread)) { break; } if (eglThread.mOnEGLThreadListener != null) { eglThread.mOnEGLThreadListener.onSurfaceChanged(eglThread.mSurface, eglThread.mWidth, eglThread.mHeight); } eglThread.mIsSurfaceChanged = false; } // onDrawFrame if (!draw(softReference.get())) { break; } } } // release eglUtil.release(); EGLThread eglThread = softReference.get(); if (eglThread != null) { eglThread.release(); } } } private boolean createWindowSurface(EGLThread eglThread) { // 创建EGLSurface if (!eglUtil.createWindowSurface(eglThread.mSurface)) { return false; } // 绑定EGLSurface if (!eglUtil.eglMakeCurrent()) { return false; } return true; } public EGLContext getEGLContext() { return eglUtil.getEGLContext(); } private boolean draw(EGLThread eglThread) { if (eglThread != null && eglThread.mIsRunning) { if (eglThread.mOnEGLThreadListener != null) { eglThread.mOnEGLThreadListener.onDrawFrame(); // 由于第一次绘制的时候总是显示不出来(原因未知),所以这里加一个变量来控制, // 如果是第一次绘制,则调用两次onDrawFrame方法,绘制完成后置为false if (isNeedDoubleDraw) { eglThread.mOnEGLThreadListener.onDrawFrame(); isNeedDoubleDraw = false; } } eglUtil.eglSwapBuffers(); if (eglThread.mRenderMode == EGLThread.RENDERMODE_CONTINUOUSLY) { try { Thread.sleep(16); } catch (InterruptedException e) { return false; } } return true; } return false; } } private void release() { mIsSurfaceChanged = false; mSurface = null; mIsRunning = false; mWorkThread = null; mSharedEGLContext = null; mWidth = 0; mHeight = 0; mOnEGLThreadListener = null; mRenderMode = RENDERMODE_CONTINUOUSLY; } public interface OnEGLThreadListener { void onCreate(); void onSurfaceChanged(Object surface, int width, int height); void onDrawFrame(); } }

 第三步:构造一个MyGLSurfaceView

package com.leilu.mycamera;

import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import javax.microedition.khronos.egl.EGLContext;

/**
 * 1.实现EGL线程
 * 2.实现不断刷新模式和手动刷新模式
 * 3.可以共享EGLContext
 * Created by ll on 2018/9/11.
 */

public class MyGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private EGLThread mEGLThread;

    public MyGLSurfaceView(Context context) {
        this(context, null);
    }

    public MyGLSurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyGLSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        getHolder().addCallback(this);
        mEGLThread = new EGLThread();
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mEGLThread.setSurface(holder.getSurface(), width, height);
        if (!mEGLThread.isRunning()) {
            mEGLThread.start();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    /**
     * 手动刷新
     */
    public void requestRender() {
        if (mEGLThread != null) {
            mEGLThread.requestRender();
        }
    }


    // 设置刷新模式
    public void setRenderMode(int renderMode) {
        if (mEGLThread != null) {
            mEGLThread.setRenderMode(renderMode);
        }
    }


    /**
     * 设置render监听器
     *
     * @param listener
     */
    public void setRenderListener(EGLThread.OnEGLThreadListener listener) {
        if (mEGLThread != null) {
            mEGLThread.setOnEGLThreadListener(listener);
        }
    }


    /**
     * 设置共享的EGLContext
     *
     * @param shareContext
     */
    public void setEGLContexnt(EGLContext shareContext) {
        if (mEGLThread != null && mEGLThread.isRunning()) {
            if (mEGLThread.isRunning()) {
                throw new IllegalStateException("The EGLThread is Running!");
            }
            mEGLThread.setEGLContext(shareContext);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mEGLThread != null) {
            mEGLThread.stop();
        }
    }
}

 这样就可以在布局或者代码中使用MyGLSurafeView控件了

为什么需要自定义GLSurfaceView呢,系统不是默认有一个GLSurfaceView吗?

那是因为系统的GLSurfaceView满足不了某些需求:

1、共享EGLContext,假如需要共享纹理,GLSurfaceView没有提供此方法

2、通过EGLThread类,可以构造出不同种类的GLSurfaceView,比如照相机界面,上面是主渲染,下面有一排滤镜的画面,这就需要共享纹理来实现,使用EGLThread就能构造出自己想要的效果

你可能感兴趣的:(OpenglES)