1 /* 2 * Copyright 2018 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.heifwriter; 18 19 import androidx.annotation.IntDef; 20 import android.opengl.GLES11Ext; 21 import android.opengl.GLES20; 22 import android.opengl.Matrix; 23 import android.util.Log; 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.nio.FloatBuffer; 27 28 /** 29 * GL program and supporting functions for textured 2D shapes. 30 * 31 * (Contains mostly code borrowed from Grafika) 32 * 33 * @hide 34 */ 35 public class Texture2dProgram { 36 private static final String TAG = "Texture2dProgram"; 37 38 /** Identity matrix for general use. Don't modify or life will get weird. */ 39 public static final float[] IDENTITY_MATRIX; 40 41 /** 42 * Following matrix is for texturing from bitmap. We set up the frame rects 43 * to work with SurfaceTexture's texcoords and texmatrix, but then the bitmap 44 * texturing must flip it vertically. (Don't modify or life gets weird too.) 45 */ 46 public static final float[] V_FLIP_MATRIX; 47 48 static { 49 IDENTITY_MATRIX = new float[16]; Matrix.setIdentityM(IDENTITY_MATRIX, 0)50 Matrix.setIdentityM(IDENTITY_MATRIX, 0); 51 52 V_FLIP_MATRIX = new float[16]; Matrix.setIdentityM(V_FLIP_MATRIX, 0)53 Matrix.setIdentityM(V_FLIP_MATRIX, 0); Matrix.translateM(V_FLIP_MATRIX, 0, 0.0f, 1.0f, 0.0f)54 Matrix.translateM(V_FLIP_MATRIX, 0, 0.0f, 1.0f, 0.0f); Matrix.scaleM(V_FLIP_MATRIX, 0, 1.0f, -1.0f, 1.0f)55 Matrix.scaleM(V_FLIP_MATRIX, 0, 1.0f, -1.0f, 1.0f); 56 } 57 58 public static final int TEXTURE_2D = 0; 59 public static final int TEXTURE_EXT = 1; 60 61 /** @hide */ 62 @IntDef({ 63 TEXTURE_2D, 64 TEXTURE_EXT, 65 }) 66 @Retention(RetentionPolicy.SOURCE) 67 public @interface ProgramType {} 68 69 // Simple vertex shader, used for all programs. 70 private static final String VERTEX_SHADER = 71 "uniform mat4 uMVPMatrix;\n" + 72 "uniform mat4 uTexMatrix;\n" + 73 "attribute vec4 aPosition;\n" + 74 "attribute vec4 aTextureCoord;\n" + 75 "varying vec2 vTextureCoord;\n" + 76 "void main() {\n" + 77 " gl_Position = uMVPMatrix * aPosition;\n" + 78 " vTextureCoord = (uTexMatrix * aTextureCoord).xy;\n" + 79 "}\n"; 80 81 // Simple fragment shader for use with "normal" 2D textures. 82 private static final String FRAGMENT_SHADER_2D = 83 "precision mediump float;\n" + 84 "varying vec2 vTextureCoord;\n" + 85 "uniform sampler2D sTexture;\n" + 86 "void main() {\n" + 87 " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 88 "}\n"; 89 90 // Simple fragment shader for use with external 2D textures (e.g. what we get from 91 // SurfaceTexture). 92 private static final String FRAGMENT_SHADER_EXT = 93 "#extension GL_OES_EGL_image_external : require\n" + 94 "precision mediump float;\n" + 95 "varying vec2 vTextureCoord;\n" + 96 "uniform samplerExternalOES sTexture;\n" + 97 "void main() {\n" + 98 " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 99 "}\n"; 100 101 private @ProgramType int mProgramType; 102 103 // Handles to the GL program and various components of it. 104 private int mProgramHandle; 105 private int muMVPMatrixLoc; 106 private int muTexMatrixLoc; 107 private int maPositionLoc; 108 private int maTextureCoordLoc; 109 private int mTextureTarget; 110 111 /** 112 * Prepares the program in the current EGL context. 113 */ Texture2dProgram(@rogramType int programType)114 public Texture2dProgram(@ProgramType int programType) { 115 mProgramType = programType; 116 117 switch (programType) { 118 case TEXTURE_2D: 119 mTextureTarget = GLES20.GL_TEXTURE_2D; 120 mProgramHandle = createProgram(VERTEX_SHADER, FRAGMENT_SHADER_2D); 121 break; 122 case TEXTURE_EXT: 123 mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; 124 mProgramHandle = createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT); 125 break; 126 default: 127 throw new RuntimeException("Unhandled type " + programType); 128 } 129 if (mProgramHandle == 0) { 130 throw new RuntimeException("Unable to create program"); 131 } 132 Log.d(TAG, "Created program " + mProgramHandle + " (" + programType + ")"); 133 134 // get locations of attributes and uniforms 135 136 maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition"); 137 checkLocation(maPositionLoc, "aPosition"); 138 maTextureCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord"); 139 checkLocation(maTextureCoordLoc, "aTextureCoord"); 140 muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix"); 141 checkLocation(muMVPMatrixLoc, "uMVPMatrix"); 142 muTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix"); 143 checkLocation(muTexMatrixLoc, "uTexMatrix"); 144 } 145 146 /** 147 * Releases the program. 148 * <p> 149 * The appropriate EGL context must be current (i.e. the one that was used to create 150 * the program). 151 */ release()152 public void release() { 153 Log.d(TAG, "deleting program " + mProgramHandle); 154 GLES20.glDeleteProgram(mProgramHandle); 155 mProgramHandle = -1; 156 } 157 158 /** 159 * Returns the program type. 160 */ getProgramType()161 public @ProgramType int getProgramType() { 162 return mProgramType; 163 } 164 165 /** 166 * Creates a texture object suitable for use with this program. 167 * <p> 168 * On exit, the texture will be bound. 169 */ createTextureObject()170 public int createTextureObject() { 171 int[] textures = new int[1]; 172 GLES20.glGenTextures(1, textures, 0); 173 checkGlError("glGenTextures"); 174 175 int texId = textures[0]; 176 GLES20.glBindTexture(mTextureTarget, texId); 177 checkGlError("glBindTexture " + texId); 178 179 GLES20.glTexParameterf(mTextureTarget, GLES20.GL_TEXTURE_MIN_FILTER, 180 GLES20.GL_NEAREST); 181 GLES20.glTexParameterf(mTextureTarget, GLES20.GL_TEXTURE_MAG_FILTER, 182 (mTextureTarget == GLES20.GL_TEXTURE_2D) ? GLES20.GL_NEAREST : GLES20.GL_LINEAR); 183 GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_S, 184 GLES20.GL_CLAMP_TO_EDGE); 185 GLES20.glTexParameteri(mTextureTarget, GLES20.GL_TEXTURE_WRAP_T, 186 GLES20.GL_CLAMP_TO_EDGE); 187 checkGlError("glTexParameter"); 188 189 return texId; 190 } 191 192 /** 193 * Issues the draw call. Does the full setup on every call. 194 * 195 * @param mvpMatrix The 4x4 projection matrix. 196 * @param vertexBuffer Buffer with vertex position data. 197 * @param firstVertex Index of first vertex to use in vertexBuffer. 198 * @param vertexCount Number of vertices in vertexBuffer. 199 * @param coordsPerVertex The number of coordinates per vertex (e.g. x,y is 2). 200 * @param vertexStride Width, in bytes, of the position data for each vertex (often 201 * vertexCount * sizeof(float)). 202 * @param texMatrix A 4x4 transformation matrix for texture coords. (Primarily intended 203 * for use with SurfaceTexture.) 204 * @param texBuffer Buffer with vertex texture data. 205 * @param texStride Width, in bytes, of the texture data for each vertex. 206 */ draw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex, int vertexCount, int coordsPerVertex, int vertexStride, float[] texMatrix, FloatBuffer texBuffer, int textureId, int texStride)207 public void draw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex, 208 int vertexCount, int coordsPerVertex, int vertexStride, 209 float[] texMatrix, FloatBuffer texBuffer, int textureId, int texStride) { 210 checkGlError("draw start"); 211 212 // Select the program. 213 GLES20.glUseProgram(mProgramHandle); 214 checkGlError("glUseProgram"); 215 216 // Set the texture. 217 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 218 GLES20.glBindTexture(mTextureTarget, textureId); 219 220 // Copy the model / view / projection matrix over. 221 GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0); 222 checkGlError("glUniformMatrix4fv"); 223 224 // Copy the texture transformation matrix over. 225 GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0); 226 checkGlError("glUniformMatrix4fv"); 227 228 // Enable the "aPosition" vertex attribute. 229 GLES20.glEnableVertexAttribArray(maPositionLoc); 230 checkGlError("glEnableVertexAttribArray"); 231 232 // Connect vertexBuffer to "aPosition". 233 GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex, 234 GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); 235 checkGlError("glVertexAttribPointer"); 236 237 // Enable the "aTextureCoord" vertex attribute. 238 GLES20.glEnableVertexAttribArray(maTextureCoordLoc); 239 checkGlError("glEnableVertexAttribArray"); 240 241 // Connect texBuffer to "aTextureCoord". 242 GLES20.glVertexAttribPointer(maTextureCoordLoc, 2, 243 GLES20.GL_FLOAT, false, texStride, texBuffer); 244 checkGlError("glVertexAttribPointer"); 245 246 // Draw the rect. 247 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount); 248 checkGlError("glDrawArrays"); 249 250 // Done -- disable vertex array, texture, and program. 251 GLES20.glDisableVertexAttribArray(maPositionLoc); 252 GLES20.glDisableVertexAttribArray(maTextureCoordLoc); 253 GLES20.glBindTexture(mTextureTarget, 0); 254 GLES20.glUseProgram(0); 255 } 256 257 /** 258 * Creates a new program from the supplied vertex and fragment shaders. 259 * 260 * @return A handle to the program, or 0 on failure. 261 */ createProgram(String vertexSource, String fragmentSource)262 public static int createProgram(String vertexSource, String fragmentSource) { 263 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 264 if (vertexShader == 0) { 265 return 0; 266 } 267 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 268 if (pixelShader == 0) { 269 return 0; 270 } 271 272 int program = GLES20.glCreateProgram(); 273 checkGlError("glCreateProgram"); 274 if (program == 0) { 275 Log.e(TAG, "Could not create program"); 276 } 277 GLES20.glAttachShader(program, vertexShader); 278 checkGlError("glAttachShader"); 279 GLES20.glAttachShader(program, pixelShader); 280 checkGlError("glAttachShader"); 281 GLES20.glLinkProgram(program); 282 int[] linkStatus = new int[1]; 283 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 284 if (linkStatus[0] != GLES20.GL_TRUE) { 285 Log.e(TAG, "Could not link program: "); 286 Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 287 GLES20.glDeleteProgram(program); 288 program = 0; 289 } 290 return program; 291 } 292 293 /** 294 * Compiles the provided shader source. 295 * 296 * @return A handle to the shader, or 0 on failure. 297 */ loadShader(int shaderType, String source)298 public static int loadShader(int shaderType, String source) { 299 int shader = GLES20.glCreateShader(shaderType); 300 checkGlError("glCreateShader type=" + shaderType); 301 GLES20.glShaderSource(shader, source); 302 GLES20.glCompileShader(shader); 303 int[] compiled = new int[1]; 304 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 305 if (compiled[0] == 0) { 306 Log.e(TAG, "Could not compile shader " + shaderType + ":"); 307 Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader)); 308 GLES20.glDeleteShader(shader); 309 shader = 0; 310 } 311 return shader; 312 } 313 314 /** 315 * Checks to see if the location we obtained is valid. GLES returns -1 if a label 316 * could not be found, but does not set the GL error. 317 * <p> 318 * Throws a RuntimeException if the location is invalid. 319 */ checkLocation(int location, String label)320 public static void checkLocation(int location, String label) { 321 if (location < 0) { 322 throw new RuntimeException("Unable to locate '" + label + "' in program"); 323 } 324 } 325 326 /** 327 * Checks to see if a GLES error has been raised. 328 */ checkGlError(String op)329 public static void checkGlError(String op) { 330 int error = GLES20.glGetError(); 331 if (error != GLES20.GL_NO_ERROR) { 332 String msg = op + ": glError 0x" + Integer.toHexString(error); 333 Log.e(TAG, msg); 334 throw new RuntimeException(msg); 335 } 336 } 337 } 338