package com.laifeng.sopcastsdk.video.effect;
import android.content.Context;
import android.graphics.PointF;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.text.TextUtils;
import com.laifeng.sopcastsdk.camera.CameraData;
import com.laifeng.sopcastsdk.camera.CameraHolder;
import com.laifeng.sopcastsdk.constant.SopCastConstant;
import com.laifeng.sopcastsdk.utils.SopCastLog;
import com.laifeng.sopcastsdk.video.GLSLFileUtils;
import com.laifeng.sopcastsdk.video.GlUtil;
import java.nio.FloatBuffer;
import java.util.LinkedList;
/**
* @Title: Effect
* @Package com.laifeng.sopcastsdk.video.effert
* @Description:
* @Author Jim
* @Date 16/9/14
* @Time 下午2:10
* @Version
*/
public abstract class Effect {
private final FloatBuffer mVtxBuf = GlUtil.createSquareVtx();
private final float[] mPosMtx = GlUtil.createIdentityMtx();
protected int mTextureId = -1;
private int mProgram = -1;
private int maPositionHandle = -1;
private int maTexCoordHandle = -1;
private int muPosMtxHandle = -1;
private int muTexMtxHandle = -1;
private final int[] mFboId = new int[]{0};
private final int[] mRboId = new int[]{0};
private final int[] mTexId = new int[]{0};
private int mWidth = -1;
private int mHeight = -1;
private float mAngle = 270;
private final LinkedList<Runnable> mRunOnDraw;
private String mVertex;
private String mFragment;
public Effect() {
mRunOnDraw = new LinkedList<>();
}
public void setShader(String vertex, String fragment) {
mVertex = vertex;
mFragment = fragment;
}
public void prepare() {
loadShaderAndParams(mVertex, mFragment);
initSize();
createEffectTexture();
}
public void setTextureId(int textureId) {
mTextureId = textureId;
}
private void loadShaderAndParams(String vertex, String fragment) {
if(TextUtils.isEmpty(vertex) || TextUtils.isEmpty(fragment)) {
vertex = SopCastConstant.SHARDE_NULL_VERTEX;
fragment = SopCastConstant.SHARDE_NULL_FRAGMENT;
SopCastLog.e(SopCastConstant.TAG, "Couldn't load the shader, so use the null shader!");
}
GlUtil.checkGlError("initSH_S");
mProgram = GlUtil.createProgram(vertex, fragment);
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "position");
maTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
muPosMtxHandle = GLES20.glGetUniformLocation(mProgram, "uPosMtx");
muTexMtxHandle = GLES20.glGetUniformLocation(mProgram, "uTexMtx");
loadOtherParams();
GlUtil.checkGlError("initSH_E");
}
protected void loadOtherParams() {
//do nothing
}
private void initSize() {
if(CameraHolder.instance().getState() != CameraHolder.State.PREVIEW) {
return;
}
CameraData cameraData = CameraHolder.instance().getCameraData();
int width = cameraData.cameraWidth;
int height = cameraData.cameraHeight;
if(CameraHolder.instance().isLandscape()) {
mWidth = Math.max(width, height);
mHeight = Math.min(width, height);
} else {
mWidth = Math.min(width, height);
mHeight = Math.max(width, height);
}
}
private void createEffectTexture() {
if(CameraHolder.instance().getState() != CameraHolder.State.PREVIEW) {
return;
}
GlUtil.checkGlError("initFBO_S");
GLES20.glGenFramebuffers(1, mFboId, 0);
GLES20.glGenRenderbuffers(1, mRboId, 0);
GLES20.glGenTextures(1, mTexId, 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, mRboId[0]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
GLES20.GL_DEPTH_COMPONENT16, mWidth, mHeight);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFboId[0]);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, mRboId[0]);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexId[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_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.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
mWidth, mHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mTexId[0], 0);
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) !=
GLES20.GL_FRAMEBUFFER_COMPLETE) {
throw new RuntimeException("glCheckFramebufferStatus()");
}
GlUtil.checkGlError("initFBO_E");
}
public int getEffertedTextureId() {
return mTexId[0];
}
protected void runOnDraw(final Runnable runnable) {
synchronized (mRunOnDraw) {
mRunOnDraw.addLast(runnable);
}
}
protected void runPendingOnDrawTasks() {
while (!mRunOnDraw.isEmpty()) {
mRunOnDraw.removeFirst().run();
}
}
public void draw(final float[] tex_mtx) {
if (-1 == mProgram || mTextureId == -1 || mWidth == -1) {
return;
}
GlUtil.checkGlError("draw_S");
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFboId[0]);
GLES20.glViewport(0, 0, mWidth, mHeight);
GLES20.glClearColor(0f, 0f, 0f, 1f);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(mProgram);
runPendingOnDrawTasks();
mVtxBuf.position(0);
GLES20.glVertexAttribPointer(maPositionHandle,
3, GLES20.GL_FLOAT, false, 4 * (3 + 2), mVtxBuf);
GLES20.glEnableVertexAttribArray(maPositionHandle);
mVtxBuf.position(3);
GLES20.glVertexAttribPointer(maTexCoordHandle,
2, GLES20.GL_FLOAT, false, 4 * (3 + 2), mVtxBuf);
GLES20.glEnableVertexAttribArray(maTexCoordHandle);
if(muPosMtxHandle>= 0)
GLES20.glUniformMatrix4fv(muPosMtxHandle, 1, false, mPosMtx, 0);
if(muTexMtxHandle>= 0)
GLES20.glUniformMatrix4fv(muTexMtxHandle, 1, false, tex_mtx, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GlUtil.checkGlError("draw_E");
}
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);
}
});
}
public void release() {
if (-1 != mProgram) {
GLES20.glDeleteProgram(mProgram);
mProgram = -1;
}
}
}