From c2a93b6ba1e23cf112b58c1422b5c4444f83ca1b Mon Sep 17 00:00:00 2001 From: Leo Ma Date: Fri, 7 Apr 2017 11:30:00 +0800 Subject: [PATCH] Simplify GLES vertex setting Signed-off-by: Leo Ma --- .../seu/magicfilter/base/GPUImageFilter.java | 414 ++++++++++++++++++ .../com/seu/magicfilter/utils/Rotation.java | 54 --- .../utils/TextureRotationUtil.java | 96 ---- 3 files changed, 414 insertions(+), 150 deletions(-) create mode 100644 library/src/main/java/com/seu/magicfilter/base/GPUImageFilter.java delete mode 100644 library/src/main/java/com/seu/magicfilter/utils/Rotation.java delete mode 100644 library/src/main/java/com/seu/magicfilter/utils/TextureRotationUtil.java diff --git a/library/src/main/java/com/seu/magicfilter/base/GPUImageFilter.java b/library/src/main/java/com/seu/magicfilter/base/GPUImageFilter.java new file mode 100644 index 0000000..1c21f17 --- /dev/null +++ b/library/src/main/java/com/seu/magicfilter/base/GPUImageFilter.java @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2012 CyberAgent + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.seu.magicfilter.base.gpuimage; + +import android.content.Context; +import android.graphics.PointF; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; + +import com.seu.magicfilter.utils.MagicFilterType; +import com.seu.magicfilter.utils.OpenGLUtils; + +import net.ossrs.yasea.R; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.LinkedList; + +public class GPUImageFilter { + + private boolean mIsInitialized; + private Context mContext; + private MagicFilterType mType = MagicFilterType.NONE; + private final LinkedList mRunOnDraw; + private final int mVertexShaderId; + private final int mFragmentShaderId; + + private int mGLProgId; + private int mGLPositionIndex; + private int mGLInputImageTextureIndex; + private int mGLTextureCoordinateIndex; + private int mGLTextureTransformIndex; + + protected int mInputWidth; + protected int mInputHeight; + protected int mOutputWidth; + protected int mOutputHeight; + protected FloatBuffer mGLCubeBuffer; + protected FloatBuffer mGLTextureBuffer; + + private int[] mGLCubeId; + private int[] mGLTextureCoordinateId; + private float[] mGLTextureTransformMatrix; + + private int[] mGLFboId; + private int[] mGLFboTexId; + private IntBuffer mGLFboBuffer; + + public GPUImageFilter() { + this(MagicFilterType.NONE); + } + + public GPUImageFilter(MagicFilterType type) { + this(type, R.raw.vertex, R.raw.fragment); + } + + public GPUImageFilter(MagicFilterType type, int fragmentShaderId) { + this(type, R.raw.vertex, fragmentShaderId); + } + + public GPUImageFilter(MagicFilterType type, int vertexShaderId, int fragmentShaderId) { + mType = type; + mRunOnDraw = new LinkedList<>(); + mVertexShaderId = vertexShaderId; + mFragmentShaderId = fragmentShaderId; + } + + public void init(Context context) { + mContext = context; + onInit(); + onInitialized(); + } + + protected void onInit() { + initVbo(); + loadSamplerShader(); + } + + protected void onInitialized() { + mIsInitialized = true; + } + + public final void destroy() { + mIsInitialized = false; + destroyFboTexture(); + destoryVbo(); + GLES20.glDeleteProgram(mGLProgId); + onDestroy(); + } + + protected void onDestroy() { + } + + public void onInputSizeChanged(final int width, final int height) { + mInputWidth = width; + mInputHeight = height; + initFboTexture(width, height); + } + + public void onDisplaySizeChanged(final int width, final int height) { + mOutputWidth = width; + mOutputHeight = height; + } + + private void loadSamplerShader() { + mGLProgId = OpenGLUtils.loadProgram(OpenGLUtils.readShaderFromRawResource(getContext(), mVertexShaderId), + OpenGLUtils.readShaderFromRawResource(getContext(), mFragmentShaderId)); + mGLPositionIndex = GLES20.glGetAttribLocation(mGLProgId, "position"); + mGLTextureCoordinateIndex = GLES20.glGetAttribLocation(mGLProgId,"inputTextureCoordinate"); + mGLTextureTransformIndex = GLES20.glGetUniformLocation(mGLProgId, "textureTransform"); + mGLInputImageTextureIndex = GLES20.glGetUniformLocation(mGLProgId, "inputImageTexture"); + } + + private void initVbo() { + final float VEX_CUBE[] = { + -1.0f, -1.0f, // Bottom left. + 1.0f, -1.0f, // Bottom right. + -1.0f, 1.0f, // Top left. + 1.0f, 1.0f, // Top right. + }; + + final float TEX_COORD[] = { + 0.0f, 0.0f, // Bottom left. + 1.0f, 0.0f, // Bottom right. + 0.0f, 1.0f, // Top left. + 1.0f, 1.0f // Top right. + }; + + mGLCubeBuffer = ByteBuffer.allocateDirect(VEX_CUBE.length * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mGLCubeBuffer.put(VEX_CUBE).position(0); + + mGLTextureBuffer = ByteBuffer.allocateDirect(TEX_COORD.length * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mGLTextureBuffer.put(TEX_COORD).position(0); + + mGLCubeId = new int[1]; + mGLTextureCoordinateId = new int[1]; + + GLES20.glGenBuffers(1, mGLCubeId, 0); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLCubeId[0]); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mGLCubeBuffer.capacity() * 4, mGLCubeBuffer, GLES20.GL_STATIC_DRAW); + + GLES20.glGenBuffers(1, mGLTextureCoordinateId, 0); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLTextureCoordinateId[0]); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mGLTextureBuffer.capacity() * 4, mGLTextureBuffer, GLES20.GL_STATIC_DRAW); + } + + private void destoryVbo() { + if (mGLCubeId != null) { + GLES20.glDeleteBuffers(1, mGLCubeId, 0); + mGLCubeId = null; + } + if (mGLTextureCoordinateId != null) { + GLES20.glDeleteBuffers(1, mGLTextureCoordinateId, 0); + mGLTextureCoordinateId = null; + } + } + + private void initFboTexture(int width, int height) { + if (mGLFboId != null && (mInputWidth != width || mInputHeight != height)) { + destroyFboTexture(); + } + + mGLFboId = new int[1]; + mGLFboTexId = new int[1]; + mGLFboBuffer = IntBuffer.allocate(width * height); + + GLES20.glGenFramebuffers(1, mGLFboId, 0); + GLES20.glGenTextures(1, mGLFboTexId, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mGLFboTexId[0]); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mGLFboId[0]); + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mGLFboTexId[0], 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + } + + private void destroyFboTexture() { + if (mGLFboTexId != null) { + GLES20.glDeleteTextures(1, mGLFboTexId, 0); + mGLFboTexId = null; + } + if (mGLFboId != null) { + GLES20.glDeleteFramebuffers(1, mGLFboId, 0); + mGLFboId = null; + } + } + + public int onDrawFrame(final int textureId, final FloatBuffer cubeBuffer, final FloatBuffer textureBuffer) { + if (!mIsInitialized) { + return OpenGLUtils.NOT_INIT; + } + + GLES20.glUseProgram(mGLProgId); + runPendingOnDrawTasks(); + + GLES20.glEnableVertexAttribArray(mGLPositionIndex); + GLES20.glVertexAttribPointer(mGLPositionIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, cubeBuffer); + + GLES20.glEnableVertexAttribArray(mGLTextureCoordinateIndex); + GLES20.glVertexAttribPointer(mGLTextureCoordinateIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, textureBuffer); + + if (textureId != OpenGLUtils.NO_TEXTURE) { + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); + GLES20.glUniform1i(mGLInputImageTextureIndex, 0); + } + + onDrawArraysPre(); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + onDrawArraysAfter(); + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + + GLES20.glDisableVertexAttribArray(mGLPositionIndex); + GLES20.glDisableVertexAttribArray(mGLTextureCoordinateIndex); + + return OpenGLUtils.ON_DRAWN; + } + + public int onDrawFrame(int cameraTextureId) { + if (!mIsInitialized) { + return OpenGLUtils.NOT_INIT; + } + + if (mGLFboId == null) { + return OpenGLUtils.NO_TEXTURE; + } + + GLES20.glUseProgram(mGLProgId); + runPendingOnDrawTasks(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLCubeId[0]); + GLES20.glEnableVertexAttribArray(mGLPositionIndex); + GLES20.glVertexAttribPointer(mGLPositionIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, 0); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLTextureCoordinateId[0]); + GLES20.glEnableVertexAttribArray(mGLTextureCoordinateIndex); + GLES20.glVertexAttribPointer(mGLTextureCoordinateIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, 0); + + GLES20.glUniformMatrix4fv(mGLTextureTransformIndex, 1, false, mGLTextureTransformMatrix, 0); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTextureId); + GLES20.glUniform1i(mGLInputImageTextureIndex, 0); + + onDrawArraysPre(); + + GLES20.glViewport(0, 0, mInputWidth, mInputHeight); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mGLFboId[0]); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glReadPixels(0, 0, mInputWidth, mInputHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mGLFboBuffer); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + onDrawArraysAfter(); + + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); + + GLES20.glDisableVertexAttribArray(mGLPositionIndex); + GLES20.glDisableVertexAttribArray(mGLTextureCoordinateIndex); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + + return mGLFboTexId[0]; + } + + protected void onDrawArraysPre() {} + + protected void onDrawArraysAfter() {} + + private void runPendingOnDrawTasks() { + while (!mRunOnDraw.isEmpty()) { + mRunOnDraw.removeFirst().run(); + } + } + + public int getProgram() { + return mGLProgId; + } + + public IntBuffer getGLFboBuffer() { + return mGLFboBuffer; + } + + protected Context getContext() { + return mContext; + } + + protected MagicFilterType getFilterType() { + return mType; + } + + public void setTextureTransformMatrix(float[] mtx){ + mGLTextureTransformMatrix = mtx; + } + + protected void setInteger(final int location, final int intValue) { + runOnDraw(new Runnable() { + @Override + public void run() { + GLES20.glUniform1i(location, intValue); + } + }); + } + + protected void setFloat(final int location, final float floatValue) { + runOnDraw(new Runnable() { + @Override + public void run() { + GLES20.glUniform1f(location, floatValue); + } + }); + } + + protected void setFloatVec2(final int location, final float[] arrayValue) { + runOnDraw(new Runnable() { + @Override + public void run() { + GLES20.glUniform2fv(location, 1, FloatBuffer.wrap(arrayValue)); + } + }); + } + + protected void setFloatVec3(final int location, final float[] arrayValue) { + runOnDraw(new Runnable() { + @Override + public void run() { + GLES20.glUniform3fv(location, 1, FloatBuffer.wrap(arrayValue)); + } + }); + } + + protected void setFloatVec4(final int location, final float[] arrayValue) { + runOnDraw(new Runnable() { + @Override + public void run() { + GLES20.glUniform4fv(location, 1, FloatBuffer.wrap(arrayValue)); + } + }); + } + + protected void setFloatArray(final int location, final float[] arrayValue) { + runOnDraw(new Runnable() { + @Override + public void run() { + GLES20.glUniform1fv(location, arrayValue.length, FloatBuffer.wrap(arrayValue)); + } + }); + } + + protected void setPoint(final int location, final PointF point) { + runOnDraw(new Runnable() { + + @Override + public void run() { + float[] vec2 = new float[2]; + vec2[0] = point.x; + vec2[1] = point.y; + GLES20.glUniform2fv(location, 1, vec2, 0); + } + }); + } + + protected void setUniformMatrix3f(final int location, final float[] matrix) { + runOnDraw(new Runnable() { + + @Override + public void run() { + GLES20.glUniformMatrix3fv(location, 1, false, matrix, 0); + } + }); + } + + protected void setUniformMatrix4f(final int location, final float[] matrix) { + runOnDraw(new Runnable() { + + @Override + public void run() { + GLES20.glUniformMatrix4fv(location, 1, false, matrix, 0); + } + }); + } + + protected void runOnDraw(final Runnable runnable) { + synchronized (mRunOnDraw) { + mRunOnDraw.addLast(runnable); + } + } +} + diff --git a/library/src/main/java/com/seu/magicfilter/utils/Rotation.java b/library/src/main/java/com/seu/magicfilter/utils/Rotation.java deleted file mode 100644 index 105cc78..0000000 --- a/library/src/main/java/com/seu/magicfilter/utils/Rotation.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2012 CyberAgent - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.seu.magicfilter.utils; - -public enum Rotation { - NORMAL, ROTATION_90, ROTATION_180, ROTATION_270; - - /** - * Retrieves the int representation of the Rotation. - * - * @return 0, 90, 180 or 270 - */ - public int asInt() { - switch (this) { - case NORMAL: return 0; - case ROTATION_90: return 90; - case ROTATION_180: return 180; - case ROTATION_270: return 270; - default: throw new IllegalStateException("Unknown Rotation!"); - } - } - - /** - * Create a Rotation from an integer. Needs to be either 0, 90, 180 or 270. - * - * @param rotation 0, 90, 180 or 270 - * @return Rotation object - */ - public static Rotation fromInt(int rotation) { - switch (rotation) { - case 0: return NORMAL; - case 90: return ROTATION_90; - case 180: return ROTATION_180; - case 270: return ROTATION_270; - case 360: return NORMAL; - default: throw new IllegalStateException( - rotation + " is an unknown rotation. Needs to be either 0, 90, 180 or 270!"); - } - } -} diff --git a/library/src/main/java/com/seu/magicfilter/utils/TextureRotationUtil.java b/library/src/main/java/com/seu/magicfilter/utils/TextureRotationUtil.java deleted file mode 100644 index 4b28fea..0000000 --- a/library/src/main/java/com/seu/magicfilter/utils/TextureRotationUtil.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2012 CyberAgent - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.seu.magicfilter.utils; - -public class TextureRotationUtil { - - public static final float TEXTURE_NO_ROTATION[] = { - 0.0f, 1.0f, - 1.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f, - }; - - public static final float TEXTURE_ROTATED_90[] = { - 1.0f, 1.0f, - 1.0f, 0.0f, - 0.0f, 1.0f, - 0.0f, 0.0f, - }; - public static final float TEXTURE_ROTATED_180[] = { - 1.0f, 0.0f, - 0.0f, 0.0f, - 1.0f, 1.0f, - 0.0f, 1.0f, - }; - public static final float TEXTURE_ROTATED_270[] = { - 0.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 0.0f, - 1.0f, 1.0f, - }; - - public static final float CUBE[] = { - -1.0f, -1.0f, - 1.0f, -1.0f, - -1.0f, 1.0f, - 1.0f, 1.0f, - }; - - private TextureRotationUtil() {} - - public static float[] getRotation(final Rotation rotation, final boolean flipHorizontal, - final boolean flipVertical) { - float[] rotatedTex; - switch (rotation) { - case ROTATION_90: - rotatedTex = TEXTURE_ROTATED_90; - break; - case ROTATION_180: - rotatedTex = TEXTURE_ROTATED_180; - break; - case ROTATION_270: - rotatedTex = TEXTURE_ROTATED_270; - break; - case NORMAL: - default: - rotatedTex = TEXTURE_NO_ROTATION; - break; - } - if (flipHorizontal) { - rotatedTex = new float[]{ - flip(rotatedTex[0]), rotatedTex[1], - flip(rotatedTex[2]), rotatedTex[3], - flip(rotatedTex[4]), rotatedTex[5], - flip(rotatedTex[6]), rotatedTex[7], - }; - } - if (flipVertical) { - rotatedTex = new float[]{ - rotatedTex[0], flip(rotatedTex[1]), - rotatedTex[2], flip(rotatedTex[3]), - rotatedTex[4], flip(rotatedTex[5]), - rotatedTex[6], flip(rotatedTex[7]), - }; - } - return rotatedTex; - } - - private static float flip(final float i) { - return i == 0.0f ? 1.0f : 0.0f; - } -}