Android GlRectDrawer: Add fragment shader for RGB(A) textures

Add third shader type for RGB(A) and refactor according to the Rule of three.

BUG=webrtc:4742
R=hbos@webrtc.org

Review URL: https://codereview.webrtc.org/1311093005 .

Cr-Commit-Position: refs/heads/master@{#9843}
This commit is contained in:
Magnus Jedvert
2015-09-02 12:52:03 +02:00
parent d4563f4dde
commit ed4224fbda

View File

@ -36,6 +36,8 @@ import org.webrtc.GlUtil;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* Helper class to draw a quad that covers the entire viewport. Rotation, mirror, and cropping is
@ -76,6 +78,16 @@ public class GlRectDrawer {
+ " y + 1.77 * u, 1);\n"
+ "}\n";
private static final String RGB_FRAGMENT_SHADER_STRING =
"precision mediump float;\n"
+ "varying vec2 interp_tc;\n"
+ "\n"
+ "uniform sampler2D rgb_tex;\n"
+ "\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(rgb_tex, interp_tc);\n"
+ "}\n";
private static final String OES_FRAGMENT_SHADER_STRING =
"#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
@ -103,38 +115,18 @@ public class GlRectDrawer {
1.0f, 1.0f // Top right.
});
private GlShader oesShader;
private GlShader yuvShader;
// The keys are one of the fragments shaders above.
private final Map<String, GlShader> shaders = new IdentityHashMap<String, GlShader>();
private GlShader currentShader;
private float[] currentTexMatrix;
private int texMatrixLocation;
private void initGeometry(GlShader shader) {
shader.setVertexAttribArray("in_pos", 2, FULL_RECTANGLE_BUF);
shader.setVertexAttribArray("in_tc", 2, FULL_RECTANGLE_TEX_BUF);
}
/**
* Draw an OES texture frame with specified texture transformation matrix. Required resources are
* allocated at the first call to this function.
*/
public void drawOes(int oesTextureId, float[] texMatrix) {
// Lazy allocation.
if (oesShader == null) {
oesShader = new GlShader(VERTEX_SHADER_STRING, OES_FRAGMENT_SHADER_STRING);
oesShader.useProgram();
initGeometry(oesShader);
}
// Set GLES state to OES.
if (currentShader != oesShader) {
currentShader = oesShader;
oesShader.useProgram();
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
currentTexMatrix = null;
texMatrixLocation = oesShader.getUniformLocation("texMatrix");
}
prepareShader(OES_FRAGMENT_SHADER_STRING);
// updateTexImage() may be called from another thread in another EGL context, so we need to
// bind/unbind the texture in each draw call so that GLES understads it's a new texture.
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTextureId);
@ -142,40 +134,35 @@ public class GlRectDrawer {
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
}
/**
* Draw a RGB(A) texture frame with specified texture transformation matrix. Required resources
* are allocated at the first call to this function.
*/
public void drawRgb(int textureId, float[] texMatrix) {
prepareShader(RGB_FRAGMENT_SHADER_STRING);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
drawRectangle(texMatrix);
// Unbind the texture as a precaution.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
/**
* Draw a YUV frame with specified texture transformation matrix. Required resources are
* allocated at the first call to this function.
*/
public void drawYuv(int width, int height, int[] yuvTextures, float[] texMatrix) {
// Lazy allocation.
if (yuvShader == null) {
yuvShader = new GlShader(VERTEX_SHADER_STRING, YUV_FRAGMENT_SHADER_STRING);
yuvShader.useProgram();
// Set texture samplers.
GLES20.glUniform1i(yuvShader.getUniformLocation("y_tex"), 0);
GLES20.glUniform1i(yuvShader.getUniformLocation("u_tex"), 1);
GLES20.glUniform1i(yuvShader.getUniformLocation("v_tex"), 2);
GlUtil.checkNoGLES2Error("y/u/v_tex glGetUniformLocation");
initGeometry(yuvShader);
}
// Set GLES state to YUV.
if (currentShader != yuvShader) {
currentShader = yuvShader;
yuvShader.useProgram();
currentTexMatrix = null;
texMatrixLocation = yuvShader.getUniformLocation("texMatrix");
}
prepareShader(YUV_FRAGMENT_SHADER_STRING);
// Bind the textures.
for (int i = 0; i < 3; ++i) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
}
drawRectangle(texMatrix);
// Unbind the textures as a precaution..
for (int i = 0; i < 3; ++i) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
}
private void drawRectangle(float[] texMatrix) {
@ -189,17 +176,48 @@ public class GlRectDrawer {
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
private void prepareShader(String fragmentShader) {
// Lazy allocation.
if (!shaders.containsKey(fragmentShader)) {
final GlShader shader = new GlShader(VERTEX_SHADER_STRING, fragmentShader);
shaders.put(fragmentShader, shader);
shader.useProgram();
// Initialize fragment shader uniform values.
if (fragmentShader == YUV_FRAGMENT_SHADER_STRING) {
GLES20.glUniform1i(shader.getUniformLocation("y_tex"), 0);
GLES20.glUniform1i(shader.getUniformLocation("u_tex"), 1);
GLES20.glUniform1i(shader.getUniformLocation("v_tex"), 2);
} else if (fragmentShader == RGB_FRAGMENT_SHADER_STRING) {
GLES20.glUniform1i(shader.getUniformLocation("rgb_tex"), 0);
} else if (fragmentShader == OES_FRAGMENT_SHADER_STRING) {
GLES20.glUniform1i(shader.getUniformLocation("oes_tex"), 0);
} else {
throw new IllegalStateException("Unknown fragment shader: " + fragmentShader);
}
GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values.");
// Initialize vertex shader attributes.
shader.setVertexAttribArray("in_pos", 2, FULL_RECTANGLE_BUF);
shader.setVertexAttribArray("in_tc", 2, FULL_RECTANGLE_TEX_BUF);
}
// Update GLES state if shader is not already current.
final GlShader shader = shaders.get(fragmentShader);
if (currentShader != shader) {
currentShader = shader;
shader.useProgram();
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
currentTexMatrix = null;
texMatrixLocation = shader.getUniformLocation("texMatrix");
}
}
/**
* Release all GLES resources. This needs to be done manually, otherwise the resources are leaked.
*/
public void release() {
if (oesShader != null) {
oesShader.release();
oesShader = null;
}
if (yuvShader != null) {
yuvShader.release();
yuvShader = null;
for (GlShader shader : shaders.values()) {
shader.release();
}
shaders.clear();
}
}