/* * 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. *

* 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. *

* 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. *

* 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); } } }