• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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