Sample之MediaEffects

来源:互联网 发布:江苏悠迅网络是干嘛的 编辑:程序博客网 时间:2024/05/16 08:16

Sample源码阅读学习

Google地址:https://developer.android.com/samples/MediaEffects/project.html

我的注释代码:https://github.com/CL-window/Media-Effects-Framework.git

本项目是 展示OpenGL ES 2.0的纹理图像帧的各种滤镜效果,

主要是 Effect,GLSurfaceView,GLES20的使用,具体都在代码的注释里,贴上主要的代码


// https://code.tutsplus.com/tutorials/how-to-use-android-media-effects-with-opengl-es--cms-23650public class MediaEffectsFragment extends Fragment implements GLSurfaceView.Renderer {    private static final String STATE_CURRENT_EFFECT = "current_effect";    private GLSurfaceView mEffectView;    private int[] mTextures = new int[2];    // Media Effects Framework    private EffectContext mEffectContext; // 用于 getFactory()  EffectFactory    private Effect mEffect;    private TextureRenderer mTexRenderer = new TextureRenderer();    private int mImageWidth;    private int mImageHeight;    private boolean mInitialized = false;    private int mCurrentEffect;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setHasOptionsMenu(true);// 使用菜单    }    @Override    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,                             @Nullable Bundle savedInstanceState) {        return inflater.inflate(R.layout.fragment_media_effects, container, false);    }    @Override    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {        mEffectView = (GLSurfaceView) view.findViewById(R.id.effectsview);        mEffectView.setEGLContextClientVersion(2);// 用OpenGLES 2.0        mEffectView.setRenderer(this);// Renderer        mEffectView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);// 只在绘制数据发生改变时才绘制view        // 下面这段代码处理横竖屏时,会重新 onCreate,配合onSaveInstanceState        if (null != savedInstanceState && savedInstanceState.containsKey(STATE_CURRENT_EFFECT)) {            setCurrentEffect(savedInstanceState.getInt(STATE_CURRENT_EFFECT));        } else {            setCurrentEffect(R.id.none);        }    }    @Override    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {        inflater.inflate(R.menu.media_effects, menu);// 菜单view的初始化    }    // 菜单的item点击事件    @Override    public boolean onOptionsItemSelected(MenuItem item) {        setCurrentEffect(item.getItemId());        mEffectView.requestRender();// 手动调用 onDrawFrame        return true;    }    // fragment销毁前,保存需要保存的信息,当前选择的预览模式    @Override    public void onSaveInstanceState(Bundle outState) {        outState.putInt(STATE_CURRENT_EFFECT, mCurrentEffect);    }    // Renderer    @Override    public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {        // Nothing to do here 仅调用一次,用于设置view的OpenGLES环境    }    // 如果view的几何形状发生变化了就调用,例如当竖屏变为横屏时    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {        if (mTexRenderer != null) {            mTexRenderer.updateViewSize(width, height);        }    }    // 每次View被重绘时被调用,重点    @Override    public void onDrawFrame(GL10 gl) {        if (!mInitialized) {            //Only need to do this once  初始化            mEffectContext = EffectContext.createWithCurrentGlContext();            mTexRenderer.init();            loadTextures();// 加载图片文理            mInitialized = true;        }        if (mCurrentEffect != R.id.none) {            //if an effect is chosen initialize it and apply it to the texture            initEffect();            applyEffect();        }        renderResult();    }    private void setCurrentEffect(int effect) {        mCurrentEffect = effect;    }    private void loadTextures() {        // Generate textures        GLES20.glGenTextures(2, mTextures, 0);//  initialize the array        // Load input bitmap        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.puppy);        mImageWidth = bitmap.getWidth();        mImageHeight = bitmap.getHeight();        mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);        // Upload to texture        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]); // activate the texture at index 0        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);        // Set texture parameters        GLToolbox.initTexParams();    }    private void initEffect() {        EffectFactory effectFactory = mEffectContext.getFactory();        if (mEffect != null) {            mEffect.release();        }        // Initialize the correct effect based on the selected menu/action item        switch (mCurrentEffect) {            case R.id.none:                break;            case R.id.autofix:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_AUTOFIX);                mEffect.setParameter("scale", 0.5f);                break;            case R.id.bw:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_BLACKWHITE);                mEffect.setParameter("black", .1f);                mEffect.setParameter("white", .7f);                break;            case R.id.brightness:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_BRIGHTNESS);                mEffect.setParameter("brightness", 2.0f);                break;            case R.id.contrast:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_CONTRAST);                mEffect.setParameter("contrast", 1.4f);                break;            case R.id.crossprocess:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_CROSSPROCESS);                break;            case R.id.documentary:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_DOCUMENTARY);                break;            case R.id.duotone:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE);                mEffect.setParameter("first_color", Color.YELLOW);                mEffect.setParameter("second_color", Color.DKGRAY);                break;            case R.id.filllight:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FILLLIGHT);                mEffect.setParameter("strength", .8f);                break;            case R.id.fisheye:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FISHEYE);                mEffect.setParameter("scale", .5f);                break;            case R.id.flipvert:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FLIP);                mEffect.setParameter("vertical", true);                break;            case R.id.fliphor:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_FLIP);                mEffect.setParameter("horizontal", true);                break;            case R.id.grain:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_GRAIN);                mEffect.setParameter("strength", 1.0f);                break;            case R.id.grayscale:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_GRAYSCALE);                break;            case R.id.lomoish:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_LOMOISH);                break;            case R.id.negative:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_NEGATIVE);                break;            case R.id.posterize:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_POSTERIZE);                break;            case R.id.rotate:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_ROTATE);                mEffect.setParameter("angle", 180);                break;            case R.id.saturate:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_SATURATE);                mEffect.setParameter("scale", .5f);                break;            case R.id.sepia:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_SEPIA);                break;            case R.id.sharpen:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_SHARPEN);                break;            case R.id.temperature:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_TEMPERATURE);                mEffect.setParameter("scale", .9f);                break;            case R.id.tint:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_TINT);                mEffect.setParameter("tint", Color.MAGENTA);                break;            case R.id.vignette:                mEffect = effectFactory.createEffect(EffectFactory.EFFECT_VIGNETTE);                mEffect.setParameter("scale", .5f);                break;            default:                break;        }    }    private void applyEffect() {        // apply(int inputTexId, int width, int height, int outputTexId)        mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);    }    private void renderResult() {        if (mCurrentEffect != R.id.none) {            // if no effect is chosen, just render the original bitmap            mTexRenderer.renderTexture(mTextures[1]);        } else {            // render the result of applyEffect()            mTexRenderer.renderTexture(mTextures[0]);        }    }}


public class TextureRenderer {    private int mProgram;    private int mTexSamplerHandle;    private int mTexCoordHandle;    private int mPosCoordHandle;    private FloatBuffer mTexVertices;    private FloatBuffer mPosVertices;    private int mViewWidth;    private int mViewHeight;    private int mTexWidth;    private int mTexHeight;    // The C code for the vertex shader    private static final String VERTEX_SHADER =        "attribute vec4 a_position;\n" +        "attribute vec2 a_texcoord;\n" +        "varying vec2 v_texcoord;\n" +        "void main() {\n" +        "  gl_Position = a_position;\n" +        "  v_texcoord = a_texcoord;\n" +        "}\n";    // C code for the fragment shader    private static final String FRAGMENT_SHADER =        "precision mediump float;\n" +        "uniform sampler2D tex_sampler;\n" +        "varying vec2 v_texcoord;\n" +        "void main() {\n" +        "  gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" +        "}\n";    private static final float[] TEX_VERTICES = {            0.0f, 1.0f,            1.0f, 1.0f,            0.0f, 0.0f,            1.0f, 0.0f    };    private static final float[] POS_VERTICES = {            -1.0f, -1.0f,// bottom left            1.0f, -1.0f, // bottom right            -1.0f, 1.0f, // top left            1.0f, 1.0f   // top right    };/** * TEX_VERTICES 和 POS_VERTICES 是处理坐标系的,2D的矩形,四个点 * OpenGL世界坐标系 \                     Android UI坐标系                  OpenGL文理坐标系 *          (-1,1)  \     (1,1)         (0,0)           (1,0)               \ (0,1)     (1,1) *           \------\------\              \-------------\------>            \------------\ *           \      \(0,0) \              \             \                   \            \ *      -----\------\------\------>       \             \                   \            \ *           \      \      \              \             \                   \            \ *           \------\------\              \-------------\                   \------------\------> *         (-1,-1)  \     (1,-1)          \(0,1)       (1,1)              (0,0)       (1,0) * */    private static final int FLOAT_SIZE_BYTES = 4; // a float uses 4 bytes    public void init() {        // Create program        mProgram = GLToolbox.createProgram(VERTEX_SHADER, FRAGMENT_SHADER);        // Bind attributes and uniforms        // get a handle to the constant uTexture mentioned in the fragment shader code.        mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram,                "tex_sampler");        // get a handle to the variables a_position and a_texcoord mentioned in the vertex shader code.        mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord");        mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position");        // Setup coordinate buffers        /* ByteBuffer.allocateDirect:   create buffer        * ByteBuffer.nativeOrder:       determine the byte order        * asFloatBuffer:                convert the ByteBuffer instance into a FloatBuffer        * put:                          load the array into the buffer        * position:                     make sure that the buffer is read from the beginning        * */        mTexVertices = ByteBuffer.allocateDirect(                TEX_VERTICES.length * FLOAT_SIZE_BYTES)                .order(ByteOrder.nativeOrder()).asFloatBuffer();        mTexVertices.put(TEX_VERTICES).position(0);        mPosVertices = ByteBuffer.allocateDirect(                POS_VERTICES.length * FLOAT_SIZE_BYTES)                .order(ByteOrder.nativeOrder()).asFloatBuffer();        mPosVertices.put(POS_VERTICES).position(0);    }    public void tearDown() {        GLES20.glDeleteProgram(mProgram);    }    public void updateTextureSize(int texWidth, int texHeight) {        mTexWidth = texWidth;        mTexHeight = texHeight;        computeOutputVertices();    }    public void updateViewSize(int viewWidth, int viewHeight) {        mViewWidth = viewWidth;        mViewHeight = viewHeight;        computeOutputVertices();    }    public void renderTexture(int texId) {        // Bind default FBO ,create a named frame buffer object        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);        // Use our shader program ,using the program we just linked.        GLES20.glUseProgram(mProgram);        GLToolbox.checkGlError("glUseProgram");        // Set viewport        GLES20.glViewport(0, 0, mViewWidth, mViewHeight);        GLToolbox.checkGlError("glViewport");        // Disable blending        GLES20.glDisable(GLES20.GL_BLEND);// to disable the blending of colors while rendering        // Set the vertex attributes        /*        * glVertexAttribPointer  associate the aPosition  handles with the textureBuffer        * */        GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false,                0, mTexVertices);        GLES20.glEnableVertexAttribArray(mTexCoordHandle);        GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false,                0, mPosVertices);        GLES20.glEnableVertexAttribArray(mPosCoordHandle);        GLToolbox.checkGlError("vertex attribute setup");        // Set the input texture        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);        GLToolbox.checkGlError("glActiveTexture");        // glBindTexture  bind the texture (passed as an argument to the draw method) to the fragment shader.        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);        GLToolbox.checkGlError("glBindTexture");        GLES20.glUniform1i(mTexSamplerHandle, 0);        // Draw        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);        // glClear :  Clear the contents of the GLSurfaceView        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);        // glDrawArrays : draw the two triangles (and thus the square).        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);    }    private void computeOutputVertices() {        if (mPosVertices != null) {            float imgAspectRatio = mTexWidth / (float)mTexHeight;            float viewAspectRatio = mViewWidth / (float)mViewHeight;            float relativeAspectRatio = viewAspectRatio / imgAspectRatio;            float x0, y0, x1, y1;            if (relativeAspectRatio > 1.0f) {                x0 = -1.0f / relativeAspectRatio;                y0 = -1.0f;                x1 = 1.0f / relativeAspectRatio;                y1 = 1.0f;            } else {                x0 = -1.0f;                y0 = -relativeAspectRatio;                x1 = 1.0f;                y1 = relativeAspectRatio;            }            float[] coords = new float[] { x0, y0, x1, y0, x0, y1, x1, y1 };            mPosVertices.put(coords).position(0);        }    }}

GLSurfaceView 结合 OpenGLES使用步骤(个人总结,如果觉得不对,欢迎指正):
1.找到view,初始化
mEffectView = (GLSurfaceView) view.findViewById(R.id.effectsview);
mEffectView.setEGLContextClientVersion(2);// 用OpenGLES 2.0
mEffectView.setRenderer(this);// Renderer
mEffectView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);// 只在绘制数据发生改变时才绘制view
2. 实现 Renderer 的三个函数
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {
// 仅调用一次,用于设置view的OpenGLES环境
}
// 如果view的几何形状发生变化了就调用,例如当竖屏变为横屏时
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {

}
// 每次View被重绘时被调用,重点
@Override
public void onDrawFrame(GL10 gl) {

}
3. 用来显示图片的是 TextureRenderer onDrawFrame 里 初始化,只一次
这里 create a vertex shader create a fragment shader
int shader = GLES20.glCreateShader(shaderType);// create a shader object and return a reference
GLES20.glShaderSource(shader, source);// associate the shader code (source) with the shader.
GLES20.glCompileShader(shader);// compile the shader code
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);

创建好后
GLES20.glAttachShader(program, vertexShader);// attach the shaders to the program.
GLES20.glAttachShader(program, pixelShader);
GLES20.glLinkProgram(program); // link the program
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);

// Bind attributes and uniforms
// get a handle to the constant uTexture mentioned in the fragment shader code.
mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram,"tex_sampler");
// get a handle to the variables a_position and a_texcoord mentioned in the vertex shader code.
mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord");
mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position");

/* ByteBuffer.allocateDirect: create buffer
* ByteBuffer.nativeOrder: determine the byte order
* asFloatBuffer: convert the ByteBuffer instance into a FloatBuffer
* put: load the array into the buffer
* position: make sure that the buffer is read from the beginning
* */
mTexVertices = ByteBuffer.allocateDirect(
TEX_VERTICES.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTexVertices.put(TEX_VERTICES).position(0);
mPosVertices = ByteBuffer.allocateDirect(
POS_VERTICES.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mPosVertices.put(POS_VERTICES).position(0);
到这里,GLES20与 TextureRenderer 就绑定好了

4.onDrawFrame 里 GLES20 初始化,只一次
// Generate textures
GLES20.glGenTextures(2, mTextures, 0);// initialize the array

// Upload bitmap to texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]); // activate the texture at index 0
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

// set various properties that decide how the texture is rendered
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
至此,GLES20初始化完成,图片也设置进去了

5.需要显示出来
// Bind default FBO ,create a named frame buffer object
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

// Use our shader program ,using the program we just linked.
GLES20.glUseProgram(mProgram);
// Set viewport
GLES20.glViewport(0, 0, mViewWidth, mViewHeight);
// Disable blending
GLES20.glDisable(GLES20.GL_BLEND);// to disable the blending of colors while rendering

// Set the vertex attributes
/*
* glVertexAttribPointer associate the aPosition handles with the textureBuffer
* */
GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false,
0, mTexVertices);
GLES20.glEnableVertexAttribArray(mTexCoordHandle);

GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false,
0, mPosVertices);
GLES20.glEnableVertexAttribArray(mPosCoordHandle);

// Set the input texture
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
// glBindTexture bind the texture (passed as an argument to the draw method) to the fragment shader.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
GLES20.glUniform1i(mTexSamplerHandle, 0);

// Draw
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// glClear : Clear the contents of the GLSurfaceView
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// glDrawArrays : draw the two triangles (and thus the square).
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
done.
6.当滤镜改变时,手动通知GLSurfaceView mEffectView.requestRender();// 手动调用 onDrawFrame
// apply(int inputTexId, int width, int height, int outputTexId)
mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);

0 0