blob: 86e284a481a7f3ca0a9d6d7613b8c8b53da5b160 [file] [log] [blame]
/*
* Copyright 2018 Google Inc. All rights reserved.
*
* 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 androidx.heifwriter;
import androidx.annotation.IntDef;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.FloatBuffer;
/**
* GL program and supporting functions for textured 2D shapes.
*
* (Contains mostly code borrowed from Grafika)
*
* @hide
*/
public class Texture2dProgram {
private static final String TAG = "Texture2dProgram";
/** Identity matrix for general use. Don't modify or life will get weird. */
public static final float[] IDENTITY_MATRIX;
/**
* Following matrix is for texturing from bitmap. We set up the frame rects
* to work with SurfaceTexture's texcoords and texmatrix, but then the bitmap
* texturing must flip it vertically. (Don't modify or life gets weird too.)
*/
public static final float[] V_FLIP_MATRIX;
static {
IDENTITY_MATRIX = new float[16];
Matrix.setIdentityM(IDENTITY_MATRIX, 0);
V_FLIP_MATRIX = new float[16];
Matrix.setIdentityM(V_FLIP_MATRIX, 0);
Matrix.translateM(V_FLIP_MATRIX, 0, 0.0f, 1.0f, 0.0f);
Matrix.scaleM(V_FLIP_MATRIX, 0, 1.0f, -1.0f, 1.0f);
}
public static final int TEXTURE_2D = 0;
public static final int TEXTURE_EXT = 1;
/** @hide */
@IntDef({
TEXTURE_2D,
TEXTURE_EXT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProgramType {}
// Simple vertex shader, used for all programs.
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uTexMatrix;\n" +
"attribute vec4 aPosition;\n" +
"attribute vec4 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
" vTextureCoord = (uTexMatrix * aTextureCoord).xy;\n" +
"}\n";
// Simple fragment shader for use with "normal" 2D textures.
private static final String FRAGMENT_SHADER_2D =
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform sampler2D sTexture;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
"}\n";
// Simple fragment shader for use with external 2D textures (e.g. what we get from
// SurfaceTexture).
private static final String FRAGMENT_SHADER_EXT =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform samplerExternalOES sTexture;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
"}\n";
private @ProgramType int mProgramType;
// Handles to the GL program and various components of it.
private int mProgramHandle;
private int muMVPMatrixLoc;
private int muTexMatrixLoc;
private int maPositionLoc;
private int maTextureCoordLoc;
private int mTextureTarget;
/**
* Prepares the program in the current EGL context.
*/
public Texture2dProgram(@ProgramType int programType) {
mProgramType = programType;
switch (programType) {
case TEXTURE_2D:
mTextureTarget = GLES20.GL_TEXTURE_2D;
mProgramHandle = createProgram(VERTEX_SHADER, FRAGMENT_SHADER_2D);
break;
case TEXTURE_EXT:
mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
mProgramHandle = createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT);
break;
default:
throw new RuntimeException("Unhandled type " + programType);
}
if (mProgramHandle == 0) {
throw new RuntimeException("Unable to create program");
}
Log.d(TAG, "Created program " + mProgramHandle + " (" + programType + ")");
// get locations of attributes and uniforms
maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
checkLocation(maPositionLoc, "aPosition");
maTextureCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord");
checkLocation(maTextureCoordLoc, "aTextureCoord");
muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
checkLocation(muMVPMatrixLoc, "uMVPMatrix");
muTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix");
checkLocation(muTexMatrixLoc, "uTexMatrix");
}
/**
* Releases the program.
* <p>
* The appropriate EGL context must be current (i.e. the one that was used to create
* the program).
*/
public void release() {
Log.d(TAG, "deleting program " + mProgramHandle);
GLES20.glDeleteProgram(mProgramHandle);
mProgramHandle = -1;
}
/**
* Returns the program type.
*/
public @ProgramType int getProgramType() {
return mProgramType;
}
/**
* Creates a texture object suitable for use with this program.
* <p>
* On exit, the texture will be bound.
*/
public int createTextureObject() {
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
checkGlError("glGenTextures");
int texId = textures[0];
GLES20.glBindTexture(mTextureTarget, texId);
checkGlError("glBindTexture " + texId);
GLES20.glTexParameterf(mTextureTarget, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameterf(mTextureTarget, GLES20.GL_TEXTURE_MAG_FILTER,
(mTextureTarget == GLES20.GL_TEXTURE_2D) ? GLES20.GL_NEAREST : GLES20.GL_LINEAR);
GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
checkGlError("glTexParameter");
return texId;
}
/**
* Issues the draw call. Does the full setup on every call.
*
* @param mvpMatrix The 4x4 projection matrix.
* @param vertexBuffer Buffer with vertex position data.
* @param firstVertex Index of first vertex to use in vertexBuffer.
* @param vertexCount Number of vertices in vertexBuffer.
* @param coordsPerVertex The number of coordinates per vertex (e.g. x,y is 2).
* @param vertexStride Width, in bytes, of the position data for each vertex (often
* vertexCount * sizeof(float)).
* @param texMatrix A 4x4 transformation matrix for texture coords. (Primarily intended
* for use with SurfaceTexture.)
* @param texBuffer Buffer with vertex texture data.
* @param texStride Width, in bytes, of the texture data for each vertex.
*/
public void draw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex,
int vertexCount, int coordsPerVertex, int vertexStride,
float[] texMatrix, FloatBuffer texBuffer, int textureId, int texStride) {
checkGlError("draw start");
// Select the program.
GLES20.glUseProgram(mProgramHandle);
checkGlError("glUseProgram");
// Set the texture.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(mTextureTarget, textureId);
// Copy the model / view / projection matrix over.
GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0);
checkGlError("glUniformMatrix4fv");
// Copy the texture transformation matrix over.
GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0);
checkGlError("glUniformMatrix4fv");
// Enable the "aPosition" vertex attribute.
GLES20.glEnableVertexAttribArray(maPositionLoc);
checkGlError("glEnableVertexAttribArray");
// Connect vertexBuffer to "aPosition".
GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex,
GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
checkGlError("glVertexAttribPointer");
// Enable the "aTextureCoord" vertex attribute.
GLES20.glEnableVertexAttribArray(maTextureCoordLoc);
checkGlError("glEnableVertexAttribArray");
// Connect texBuffer to "aTextureCoord".
GLES20.glVertexAttribPointer(maTextureCoordLoc, 2,
GLES20.GL_FLOAT, false, texStride, texBuffer);
checkGlError("glVertexAttribPointer");
// Draw the rect.
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount);
checkGlError("glDrawArrays");
// Done -- disable vertex array, texture, and program.
GLES20.glDisableVertexAttribArray(maPositionLoc);
GLES20.glDisableVertexAttribArray(maTextureCoordLoc);
GLES20.glBindTexture(mTextureTarget, 0);
GLES20.glUseProgram(0);
}
/**
* Creates a new program from the supplied vertex and fragment shaders.
*
* @return A handle to the program, or 0 on failure.
*/
public static int createProgram(String vertexSource, String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES20.glCreateProgram();
checkGlError("glCreateProgram");
if (program == 0) {
Log.e(TAG, "Could not create program");
}
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
return program;
}
/**
* Compiles the provided shader source.
*
* @return A handle to the shader, or 0 on failure.
*/
public static int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
checkGlError("glCreateShader type=" + shaderType);
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
}
/**
* Checks to see if the location we obtained is valid. GLES returns -1 if a label
* could not be found, but does not set the GL error.
* <p>
* Throws a RuntimeException if the location is invalid.
*/
public static void checkLocation(int location, String label) {
if (location < 0) {
throw new RuntimeException("Unable to locate '" + label + "' in program");
}
}
/**
* Checks to see if a GLES error has been raised.
*/
public static void checkGlError(String op) {
int error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
String msg = op + ": glError 0x" + Integer.toHexString(error);
Log.e(TAG, msg);
throw new RuntimeException(msg);
}
}
}