1 /*
2  * Copyright (C) 2012 The Android Open Source Project
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 package com.android.gallery3d.glrenderer;
17 
18 import android.graphics.Bitmap;
19 import android.graphics.Rect;
20 import android.graphics.RectF;
21 import android.opengl.GLES20;
22 import android.opengl.GLUtils;
23 import android.opengl.Matrix;
24 import android.util.Log;
25 
26 import com.android.gallery3d.util.IntArray;
27 
28 import java.nio.Buffer;
29 import java.nio.ByteBuffer;
30 import java.nio.ByteOrder;
31 import java.nio.FloatBuffer;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 
35 public class GLES20Canvas implements GLCanvas {
36     // ************** Constants **********************
37     private static final String TAG = GLES20Canvas.class.getSimpleName();
38     private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
39     private static final float OPAQUE_ALPHA = 0.95f;
40 
41     private static final int COORDS_PER_VERTEX = 2;
42     private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
43 
44     private static final int COUNT_FILL_VERTEX = 4;
45     private static final int COUNT_LINE_VERTEX = 2;
46     private static final int COUNT_RECT_VERTEX = 4;
47     private static final int OFFSET_FILL_RECT = 0;
48     private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
49     private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
50 
51     private static final float[] BOX_COORDINATES = {
52             0, 0, // Fill rectangle
53             1, 0,
54             0, 1,
55             1, 1,
56             0, 0, // Draw line
57             1, 1,
58             0, 0, // Draw rectangle outline
59             0, 1,
60             1, 1,
61             1, 0,
62     };
63 
64     private static final float[] BOUNDS_COORDINATES = {
65         0, 0, 0, 1,
66         1, 1, 0, 1,
67     };
68 
69     private static final String POSITION_ATTRIBUTE = "aPosition";
70     private static final String COLOR_UNIFORM = "uColor";
71     private static final String MATRIX_UNIFORM = "uMatrix";
72     private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
73     private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
74     private static final String ALPHA_UNIFORM = "uAlpha";
75     private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
76 
77     private static final String DRAW_VERTEX_SHADER = ""
78             + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
79             + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
80             + "void main() {\n"
81             + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
82             + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
83             + "}\n";
84 
85     private static final String DRAW_FRAGMENT_SHADER = ""
86             + "precision mediump float;\n"
87             + "uniform vec4 " + COLOR_UNIFORM + ";\n"
88             + "void main() {\n"
89             + "  gl_FragColor = " + COLOR_UNIFORM + ";\n"
90             + "}\n";
91 
92     private static final String TEXTURE_VERTEX_SHADER = ""
93             + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
94             + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n"
95             + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
96             + "varying vec2 vTextureCoord;\n"
97             + "void main() {\n"
98             + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
99             + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
100             + "  vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
101             + "}\n";
102 
103     private static final String MESH_VERTEX_SHADER = ""
104             + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
105             + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
106             + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
107             + "varying vec2 vTextureCoord;\n"
108             + "void main() {\n"
109             + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
110             + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
111             + "  vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
112             + "}\n";
113 
114     private static final String TEXTURE_FRAGMENT_SHADER = ""
115             + "precision mediump float;\n"
116             + "varying vec2 vTextureCoord;\n"
117             + "uniform float " + ALPHA_UNIFORM + ";\n"
118             + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n"
119             + "void main() {\n"
120             + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
121             + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
122             + "}\n";
123 
124     private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
125             + "#extension GL_OES_EGL_image_external : require\n"
126             + "precision mediump float;\n"
127             + "varying vec2 vTextureCoord;\n"
128             + "uniform float " + ALPHA_UNIFORM + ";\n"
129             + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
130             + "void main() {\n"
131             + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
132             + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
133             + "}\n";
134 
135     private static final int INITIAL_RESTORE_STATE_SIZE = 8;
136     private static final int MATRIX_SIZE = 16;
137 
138     // Keep track of restore state
139     private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
140     private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
141     private IntArray mSaveFlags = new IntArray();
142 
143     private int mCurrentAlphaIndex = 0;
144     private int mCurrentMatrixIndex = 0;
145 
146     // Viewport size
147     private int mWidth;
148     private int mHeight;
149 
150     // Projection matrix
151     private float[] mProjectionMatrix = new float[MATRIX_SIZE];
152 
153     // Screen size for when we aren't bound to a texture
154     private int mScreenWidth;
155     private int mScreenHeight;
156 
157     // GL programs
158     private int mDrawProgram;
159     private int mTextureProgram;
160     private int mOesTextureProgram;
161     private int mMeshProgram;
162 
163     // GL buffer containing BOX_COORDINATES
164     private int mBoxCoordinates;
165 
166     // Handle indices -- common
167     private static final int INDEX_POSITION = 0;
168     private static final int INDEX_MATRIX = 1;
169 
170     // Handle indices -- draw
171     private static final int INDEX_COLOR = 2;
172 
173     // Handle indices -- texture
174     private static final int INDEX_TEXTURE_MATRIX = 2;
175     private static final int INDEX_TEXTURE_SAMPLER = 3;
176     private static final int INDEX_ALPHA = 4;
177 
178     // Handle indices -- mesh
179     private static final int INDEX_TEXTURE_COORD = 2;
180 
181     private abstract static class ShaderParameter {
182         public int handle;
183         protected final String mName;
184 
ShaderParameter(String name)185         public ShaderParameter(String name) {
186             mName = name;
187         }
188 
loadHandle(int program)189         public abstract void loadHandle(int program);
190     }
191 
192     private static class UniformShaderParameter extends ShaderParameter {
UniformShaderParameter(String name)193         public UniformShaderParameter(String name) {
194             super(name);
195         }
196 
197         @Override
loadHandle(int program)198         public void loadHandle(int program) {
199             handle = GLES20.glGetUniformLocation(program, mName);
200             checkError();
201         }
202     }
203 
204     private static class AttributeShaderParameter extends ShaderParameter {
AttributeShaderParameter(String name)205         public AttributeShaderParameter(String name) {
206             super(name);
207         }
208 
209         @Override
loadHandle(int program)210         public void loadHandle(int program) {
211             handle = GLES20.glGetAttribLocation(program, mName);
212             checkError();
213         }
214     }
215 
216     ShaderParameter[] mDrawParameters = {
217             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
218             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
219             new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
220     };
221     ShaderParameter[] mTextureParameters = {
222             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
223             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
224             new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
225             new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
226             new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
227     };
228     ShaderParameter[] mOesTextureParameters = {
229             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
230             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
231             new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
232             new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
233             new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
234     };
235     ShaderParameter[] mMeshParameters = {
236             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
237             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
238             new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
239             new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
240             new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
241     };
242 
243     private final IntArray mUnboundTextures = new IntArray();
244     private final IntArray mDeleteBuffers = new IntArray();
245 
246     // Keep track of statistics for debugging
247     private int mCountDrawMesh = 0;
248     private int mCountTextureRect = 0;
249     private int mCountFillRect = 0;
250     private int mCountDrawLine = 0;
251 
252     // Buffer for framebuffer IDs -- we keep track so we can switch the attached
253     // texture.
254     private int[] mFrameBuffer = new int[1];
255 
256     // Bound textures.
257     private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
258 
259     // Temporary variables used within calculations
260     private final float[] mTempMatrix = new float[32];
261     private final float[] mTempColor = new float[4];
262     private final RectF mTempSourceRect = new RectF();
263     private final RectF mTempTargetRect = new RectF();
264     private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
265     private final int[] mTempIntArray = new int[1];
266 
267     private static final GLId mGLId = new GLES20IdImpl();
268 
GLES20Canvas()269     public GLES20Canvas() {
270         Matrix.setIdentityM(mTempTextureMatrix, 0);
271         Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
272         mAlphas[mCurrentAlphaIndex] = 1f;
273         mTargetTextures.add(null);
274 
275         FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
276         mBoxCoordinates = uploadBuffer(boxBuffer);
277 
278         int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
279         int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
280         int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
281         int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
282         int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
283         int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
284                 OES_TEXTURE_FRAGMENT_SHADER);
285 
286         mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
287         mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
288                 mTextureParameters);
289         mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
290                 mOesTextureParameters);
291         mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
292         GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
293         checkError();
294     }
295 
createBuffer(float[] values)296     private static FloatBuffer createBuffer(float[] values) {
297         // First create an nio buffer, then create a VBO from it.
298         int size = values.length * FLOAT_SIZE;
299         FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
300                 .asFloatBuffer();
301         buffer.put(values, 0, values.length).position(0);
302         return buffer;
303     }
304 
assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params)305     private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
306         int program = GLES20.glCreateProgram();
307         checkError();
308         if (program == 0) {
309             throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
310         }
311         GLES20.glAttachShader(program, vertexShader);
312         checkError();
313         GLES20.glAttachShader(program, fragmentShader);
314         checkError();
315         GLES20.glLinkProgram(program);
316         checkError();
317         int[] mLinkStatus = mTempIntArray;
318         GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
319         if (mLinkStatus[0] != GLES20.GL_TRUE) {
320             Log.e(TAG, "Could not link program: ");
321             Log.e(TAG, GLES20.glGetProgramInfoLog(program));
322             GLES20.glDeleteProgram(program);
323             program = 0;
324         }
325         for (int i = 0; i < params.length; i++) {
326             params[i].loadHandle(program);
327         }
328         return program;
329     }
330 
loadShader(int type, String shaderCode)331     private static int loadShader(int type, String shaderCode) {
332         // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
333         // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
334         int shader = GLES20.glCreateShader(type);
335 
336         // add the source code to the shader and compile it
337         GLES20.glShaderSource(shader, shaderCode);
338         checkError();
339         GLES20.glCompileShader(shader);
340         checkError();
341 
342         return shader;
343     }
344 
345     @Override
setSize(int width, int height)346     public void setSize(int width, int height) {
347         mWidth = width;
348         mHeight = height;
349         GLES20.glViewport(0, 0, mWidth, mHeight);
350         checkError();
351         Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
352         Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
353         if (getTargetTexture() == null) {
354             mScreenWidth = width;
355             mScreenHeight = height;
356             Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
357             Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
358         }
359     }
360 
361     @Override
clearBuffer()362     public void clearBuffer() {
363         GLES20.glClearColor(0f, 0f, 0f, 1f);
364         checkError();
365         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
366         checkError();
367     }
368 
369     @Override
clearBuffer(float[] argb)370     public void clearBuffer(float[] argb) {
371         GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
372         checkError();
373         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
374         checkError();
375     }
376 
377     @Override
getAlpha()378     public float getAlpha() {
379         return mAlphas[mCurrentAlphaIndex];
380     }
381 
382     @Override
setAlpha(float alpha)383     public void setAlpha(float alpha) {
384         mAlphas[mCurrentAlphaIndex] = alpha;
385     }
386 
387     @Override
multiplyAlpha(float alpha)388     public void multiplyAlpha(float alpha) {
389         setAlpha(getAlpha() * alpha);
390     }
391 
392     @Override
translate(float x, float y, float z)393     public void translate(float x, float y, float z) {
394         Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
395     }
396 
397     // This is a faster version of translate(x, y, z) because
398     // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
399     // (3) we unroll the loop
400     @Override
translate(float x, float y)401     public void translate(float x, float y) {
402         int index = mCurrentMatrixIndex;
403         float[] m = mMatrices;
404         m[index + 12] += m[index + 0] * x + m[index + 4] * y;
405         m[index + 13] += m[index + 1] * x + m[index + 5] * y;
406         m[index + 14] += m[index + 2] * x + m[index + 6] * y;
407         m[index + 15] += m[index + 3] * x + m[index + 7] * y;
408     }
409 
410     @Override
scale(float sx, float sy, float sz)411     public void scale(float sx, float sy, float sz) {
412         Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
413     }
414 
415     @Override
rotate(float angle, float x, float y, float z)416     public void rotate(float angle, float x, float y, float z) {
417         if (angle == 0f) {
418             return;
419         }
420         float[] temp = mTempMatrix;
421         Matrix.setRotateM(temp, 0, angle, x, y, z);
422         float[] matrix = mMatrices;
423         int index = mCurrentMatrixIndex;
424         Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
425         System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
426     }
427 
428     @Override
multiplyMatrix(float[] matrix, int offset)429     public void multiplyMatrix(float[] matrix, int offset) {
430         float[] temp = mTempMatrix;
431         float[] currentMatrix = mMatrices;
432         int index = mCurrentMatrixIndex;
433         Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
434         System.arraycopy(temp, 0, currentMatrix, index, 16);
435     }
436 
437     @Override
save()438     public void save() {
439         save(SAVE_FLAG_ALL);
440     }
441 
442     @Override
save(int saveFlags)443     public void save(int saveFlags) {
444         boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
445         if (saveAlpha) {
446             float currentAlpha = getAlpha();
447             mCurrentAlphaIndex++;
448             if (mAlphas.length <= mCurrentAlphaIndex) {
449                 mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
450             }
451             mAlphas[mCurrentAlphaIndex] = currentAlpha;
452         }
453         boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
454         if (saveMatrix) {
455             int currentIndex = mCurrentMatrixIndex;
456             mCurrentMatrixIndex += MATRIX_SIZE;
457             if (mMatrices.length <= mCurrentMatrixIndex) {
458                 mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
459             }
460             System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
461         }
462         mSaveFlags.add(saveFlags);
463     }
464 
465     @Override
restore()466     public void restore() {
467         int restoreFlags = mSaveFlags.removeLast();
468         boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
469         if (restoreAlpha) {
470             mCurrentAlphaIndex--;
471         }
472         boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
473         if (restoreMatrix) {
474             mCurrentMatrixIndex -= MATRIX_SIZE;
475         }
476     }
477 
478     @Override
drawLine(float x1, float y1, float x2, float y2, GLPaint paint)479     public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
480         draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
481                 paint);
482         mCountDrawLine++;
483     }
484 
485     @Override
drawRect(float x, float y, float width, float height, GLPaint paint)486     public void drawRect(float x, float y, float width, float height, GLPaint paint) {
487         draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
488         mCountDrawLine++;
489     }
490 
draw(int type, int offset, int count, float x, float y, float width, float height, GLPaint paint)491     private void draw(int type, int offset, int count, float x, float y, float width, float height,
492             GLPaint paint) {
493         draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
494     }
495 
draw(int type, int offset, int count, float x, float y, float width, float height, int color, float lineWidth)496     private void draw(int type, int offset, int count, float x, float y, float width, float height,
497             int color, float lineWidth) {
498         prepareDraw(offset, color, lineWidth);
499         draw(mDrawParameters, type, count, x, y, width, height);
500     }
501 
prepareDraw(int offset, int color, float lineWidth)502     private void prepareDraw(int offset, int color, float lineWidth) {
503         GLES20.glUseProgram(mDrawProgram);
504         checkError();
505         if (lineWidth > 0) {
506             GLES20.glLineWidth(lineWidth);
507             checkError();
508         }
509         float[] colorArray = getColor(color);
510         boolean blendingEnabled = (colorArray[3] < 1f);
511         enableBlending(blendingEnabled);
512         if (blendingEnabled) {
513             GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
514             checkError();
515         }
516 
517         GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
518         setPosition(mDrawParameters, offset);
519         checkError();
520     }
521 
getColor(int color)522     private float[] getColor(int color) {
523         float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
524         float red = ((color >>> 16) & 0xFF) / 255f * alpha;
525         float green = ((color >>> 8) & 0xFF) / 255f * alpha;
526         float blue = (color & 0xFF) / 255f * alpha;
527         mTempColor[0] = red;
528         mTempColor[1] = green;
529         mTempColor[2] = blue;
530         mTempColor[3] = alpha;
531         return mTempColor;
532     }
533 
enableBlending(boolean enableBlending)534     private void enableBlending(boolean enableBlending) {
535         if (enableBlending) {
536             GLES20.glEnable(GLES20.GL_BLEND);
537             checkError();
538         } else {
539             GLES20.glDisable(GLES20.GL_BLEND);
540             checkError();
541         }
542     }
543 
setPosition(ShaderParameter[] params, int offset)544     private void setPosition(ShaderParameter[] params, int offset) {
545         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
546         checkError();
547         GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
548                 GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
549         checkError();
550         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
551         checkError();
552     }
553 
draw(ShaderParameter[] params, int type, int count, float x, float y, float width, float height)554     private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
555             float height) {
556         setMatrix(params, x, y, width, height);
557         int positionHandle = params[INDEX_POSITION].handle;
558         GLES20.glEnableVertexAttribArray(positionHandle);
559         checkError();
560         GLES20.glDrawArrays(type, 0, count);
561         checkError();
562         GLES20.glDisableVertexAttribArray(positionHandle);
563         checkError();
564     }
565 
setMatrix(ShaderParameter[] params, float x, float y, float width, float height)566     private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
567         Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
568         Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
569         Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
570         GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
571         checkError();
572     }
573 
574     @Override
fillRect(float x, float y, float width, float height, int color)575     public void fillRect(float x, float y, float width, float height, int color) {
576         draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
577                 color, 0f);
578         mCountFillRect++;
579     }
580 
581     @Override
drawTexture(BasicTexture texture, int x, int y, int width, int height)582     public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
583         if (width <= 0 || height <= 0) {
584             return;
585         }
586         copyTextureCoordinates(texture, mTempSourceRect);
587         mTempTargetRect.set(x, y, x + width, y + height);
588         convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
589         drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
590     }
591 
copyTextureCoordinates(BasicTexture texture, RectF outRect)592     private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
593         int left = 0;
594         int top = 0;
595         int right = texture.getWidth();
596         int bottom = texture.getHeight();
597         if (texture.hasBorder()) {
598             left = 1;
599             top = 1;
600             right -= 1;
601             bottom -= 1;
602         }
603         outRect.set(left, top, right, bottom);
604     }
605 
606     @Override
drawTexture(BasicTexture texture, RectF source, RectF target)607     public void drawTexture(BasicTexture texture, RectF source, RectF target) {
608         if (target.width() <= 0 || target.height() <= 0) {
609             return;
610         }
611         mTempSourceRect.set(source);
612         mTempTargetRect.set(target);
613 
614         convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
615         drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
616     }
617 
618     @Override
drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w, int h)619     public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
620             int h) {
621         if (w <= 0 || h <= 0) {
622             return;
623         }
624         mTempTargetRect.set(x, y, x + w, y + h);
625         drawTextureRect(texture, textureTransform, mTempTargetRect);
626     }
627 
drawTextureRect(BasicTexture texture, RectF source, RectF target)628     private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
629         setTextureMatrix(source);
630         drawTextureRect(texture, mTempTextureMatrix, target);
631     }
632 
setTextureMatrix(RectF source)633     private void setTextureMatrix(RectF source) {
634         mTempTextureMatrix[0] = source.width();
635         mTempTextureMatrix[5] = source.height();
636         mTempTextureMatrix[12] = source.left;
637         mTempTextureMatrix[13] = source.top;
638     }
639 
640     // This function changes the source coordinate to the texture coordinates.
641     // It also clips the source and target coordinates if it is beyond the
642     // bound of the texture.
convertCoordinate(RectF source, RectF target, BasicTexture texture)643     private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
644         int width = texture.getWidth();
645         int height = texture.getHeight();
646         int texWidth = texture.getTextureWidth();
647         int texHeight = texture.getTextureHeight();
648         // Convert to texture coordinates
649         source.left /= texWidth;
650         source.right /= texWidth;
651         source.top /= texHeight;
652         source.bottom /= texHeight;
653 
654         // Clip if the rendering range is beyond the bound of the texture.
655         float xBound = (float) width / texWidth;
656         if (source.right > xBound) {
657             target.right = target.left + target.width() * (xBound - source.left) / source.width();
658             source.right = xBound;
659         }
660         float yBound = (float) height / texHeight;
661         if (source.bottom > yBound) {
662             target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
663             source.bottom = yBound;
664         }
665     }
666 
drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target)667     private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
668         ShaderParameter[] params = prepareTexture(texture);
669         setPosition(params, OFFSET_FILL_RECT);
670         GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
671         checkError();
672         if (texture.isFlippedVertically()) {
673             save(SAVE_FLAG_MATRIX);
674             translate(0, target.centerY());
675             scale(1, -1, 1);
676             translate(0, -target.centerY());
677         }
678         draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
679                 target.width(), target.height());
680         if (texture.isFlippedVertically()) {
681             restore();
682         }
683         mCountTextureRect++;
684     }
685 
prepareTexture(BasicTexture texture)686     private ShaderParameter[] prepareTexture(BasicTexture texture) {
687         ShaderParameter[] params;
688         int program;
689         if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
690             params = mTextureParameters;
691             program = mTextureProgram;
692         } else {
693             params = mOesTextureParameters;
694             program = mOesTextureProgram;
695         }
696         prepareTexture(texture, program, params);
697         return params;
698     }
699 
prepareTexture(BasicTexture texture, int program, ShaderParameter[] params)700     private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
701         GLES20.glUseProgram(program);
702         checkError();
703         enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
704         GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
705         checkError();
706         texture.onBind(this);
707         GLES20.glBindTexture(texture.getTarget(), texture.getId());
708         checkError();
709         GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
710         checkError();
711         GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
712         checkError();
713     }
714 
715     @Override
drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer, int indexBuffer, int indexCount)716     public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
717             int indexBuffer, int indexCount) {
718         prepareTexture(texture, mMeshProgram, mMeshParameters);
719 
720         GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
721         checkError();
722 
723         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
724         checkError();
725         int positionHandle = mMeshParameters[INDEX_POSITION].handle;
726         GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
727                 VERTEX_STRIDE, 0);
728         checkError();
729 
730         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
731         checkError();
732         int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
733         GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
734                 false, VERTEX_STRIDE, 0);
735         checkError();
736         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
737         checkError();
738 
739         GLES20.glEnableVertexAttribArray(positionHandle);
740         checkError();
741         GLES20.glEnableVertexAttribArray(texCoordHandle);
742         checkError();
743 
744         setMatrix(mMeshParameters, x, y, 1, 1);
745         GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
746         checkError();
747 
748         GLES20.glDisableVertexAttribArray(positionHandle);
749         checkError();
750         GLES20.glDisableVertexAttribArray(texCoordHandle);
751         checkError();
752         GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
753         checkError();
754         mCountDrawMesh++;
755     }
756 
757     @Override
drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h)758     public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
759         copyTextureCoordinates(texture, mTempSourceRect);
760         mTempTargetRect.set(x, y, x + w, y + h);
761         drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
762     }
763 
764     @Override
drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target)765     public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
766         if (target.width() <= 0 || target.height() <= 0) {
767             return;
768         }
769         save(SAVE_FLAG_ALPHA);
770 
771         float currentAlpha = getAlpha();
772         float cappedRatio = Math.min(1f, Math.max(0f, ratio));
773 
774         float textureAlpha = (1f - cappedRatio) * currentAlpha;
775         setAlpha(textureAlpha);
776         drawTexture(texture, source, target);
777 
778         float colorAlpha = cappedRatio * currentAlpha;
779         setAlpha(colorAlpha);
780         fillRect(target.left, target.top, target.width(), target.height(), toColor);
781 
782         restore();
783     }
784 
785     @Override
unloadTexture(BasicTexture texture)786     public boolean unloadTexture(BasicTexture texture) {
787         boolean unload = texture.isLoaded();
788         if (unload) {
789             synchronized (mUnboundTextures) {
790                 mUnboundTextures.add(texture.getId());
791             }
792         }
793         return unload;
794     }
795 
796     @Override
deleteBuffer(int bufferId)797     public void deleteBuffer(int bufferId) {
798         synchronized (mUnboundTextures) {
799             mDeleteBuffers.add(bufferId);
800         }
801     }
802 
803     @Override
deleteRecycledResources()804     public void deleteRecycledResources() {
805         synchronized (mUnboundTextures) {
806             IntArray ids = mUnboundTextures;
807             if (mUnboundTextures.size() > 0) {
808                 mGLId.glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
809                 ids.clear();
810             }
811 
812             ids = mDeleteBuffers;
813             if (ids.size() > 0) {
814                 mGLId.glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
815                 ids.clear();
816             }
817         }
818     }
819 
820     @Override
dumpStatisticsAndClear()821     public void dumpStatisticsAndClear() {
822         String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
823                 mCountTextureRect, mCountFillRect, mCountDrawLine);
824         mCountDrawMesh = 0;
825         mCountTextureRect = 0;
826         mCountFillRect = 0;
827         mCountDrawLine = 0;
828         Log.d(TAG, line);
829     }
830 
831     @Override
endRenderTarget()832     public void endRenderTarget() {
833         RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
834         RawTexture texture = getTargetTexture();
835         setRenderTarget(oldTexture, texture);
836         restore(); // restore matrix and alpha
837     }
838 
839     @Override
beginRenderTarget(RawTexture texture)840     public void beginRenderTarget(RawTexture texture) {
841         save(); // save matrix and alpha and blending
842         RawTexture oldTexture = getTargetTexture();
843         mTargetTextures.add(texture);
844         setRenderTarget(oldTexture, texture);
845     }
846 
getTargetTexture()847     private RawTexture getTargetTexture() {
848         return mTargetTextures.get(mTargetTextures.size() - 1);
849     }
850 
setRenderTarget(BasicTexture oldTexture, RawTexture texture)851     private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
852         if (oldTexture == null && texture != null) {
853             GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
854             checkError();
855             GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
856             checkError();
857         } else if (oldTexture != null && texture == null) {
858             GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
859             checkError();
860             GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
861             checkError();
862         }
863 
864         if (texture == null) {
865             setSize(mScreenWidth, mScreenHeight);
866         } else {
867             setSize(texture.getWidth(), texture.getHeight());
868 
869             if (!texture.isLoaded()) {
870                 texture.prepare(this);
871             }
872 
873             GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
874                     texture.getTarget(), texture.getId(), 0);
875             checkError();
876 
877             checkFramebufferStatus();
878         }
879     }
880 
checkFramebufferStatus()881     private static void checkFramebufferStatus() {
882         int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
883         if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
884             String msg = "";
885             switch (status) {
886                 case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
887                     msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
888                     break;
889                 case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
890                     msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
891                     break;
892                 case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
893                     msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
894                     break;
895                 case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
896                     msg = "GL_FRAMEBUFFER_UNSUPPORTED";
897                     break;
898             }
899             throw new RuntimeException(msg + ":" + Integer.toHexString(status));
900         }
901     }
902 
903     @Override
setTextureParameters(BasicTexture texture)904     public void setTextureParameters(BasicTexture texture) {
905         int target = texture.getTarget();
906         GLES20.glBindTexture(target, texture.getId());
907         checkError();
908         GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
909         GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
910         GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
911         GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
912     }
913 
914     @Override
initializeTextureSize(BasicTexture texture, int format, int type)915     public void initializeTextureSize(BasicTexture texture, int format, int type) {
916         int target = texture.getTarget();
917         GLES20.glBindTexture(target, texture.getId());
918         checkError();
919         int width = texture.getTextureWidth();
920         int height = texture.getTextureHeight();
921         GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
922     }
923 
924     @Override
initializeTexture(BasicTexture texture, Bitmap bitmap)925     public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
926         int target = texture.getTarget();
927         GLES20.glBindTexture(target, texture.getId());
928         checkError();
929         GLUtils.texImage2D(target, 0, bitmap, 0);
930     }
931 
932     @Override
texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, int format, int type)933     public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
934             int format, int type) {
935         int target = texture.getTarget();
936         GLES20.glBindTexture(target, texture.getId());
937         checkError();
938         GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
939     }
940 
941     @Override
uploadBuffer(FloatBuffer buf)942     public int uploadBuffer(FloatBuffer buf) {
943         return uploadBuffer(buf, FLOAT_SIZE);
944     }
945 
946     @Override
uploadBuffer(ByteBuffer buf)947     public int uploadBuffer(ByteBuffer buf) {
948         return uploadBuffer(buf, 1);
949     }
950 
uploadBuffer(Buffer buffer, int elementSize)951     private int uploadBuffer(Buffer buffer, int elementSize) {
952         mGLId.glGenBuffers(1, mTempIntArray, 0);
953         checkError();
954         int bufferId = mTempIntArray[0];
955         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
956         checkError();
957         GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
958                 GLES20.GL_STATIC_DRAW);
959         checkError();
960         return bufferId;
961     }
962 
checkError()963     public static void checkError() {
964         int error = GLES20.glGetError();
965         if (error != 0) {
966             Throwable t = new Throwable();
967             Log.e(TAG, "GL error: " + error, t);
968         }
969     }
970 
971     @SuppressWarnings("unused")
printMatrix(String message, float[] m, int offset)972     private static void printMatrix(String message, float[] m, int offset) {
973         StringBuilder b = new StringBuilder(message);
974         for (int i = 0; i < MATRIX_SIZE; i++) {
975             b.append(' ');
976             if (i % 4 == 0) {
977                 b.append('\n');
978             }
979             b.append(m[offset + i]);
980         }
981         Log.v(TAG, b.toString());
982     }
983 
984     @Override
recoverFromLightCycle()985     public void recoverFromLightCycle() {
986         GLES20.glViewport(0, 0, mWidth, mHeight);
987         GLES20.glDisable(GLES20.GL_DEPTH_TEST);
988         GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
989         checkError();
990     }
991 
992     @Override
getBounds(Rect bounds, int x, int y, int width, int height)993     public void getBounds(Rect bounds, int x, int y, int width, int height) {
994         Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
995         Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
996         Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE, mTempMatrix, 0, BOUNDS_COORDINATES, 0);
997         Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE + 4, mTempMatrix, 0, BOUNDS_COORDINATES, 4);
998         bounds.left = Math.round(mTempMatrix[MATRIX_SIZE]);
999         bounds.right = Math.round(mTempMatrix[MATRIX_SIZE + 4]);
1000         bounds.top = Math.round(mTempMatrix[MATRIX_SIZE + 1]);
1001         bounds.bottom = Math.round(mTempMatrix[MATRIX_SIZE + 5]);
1002         bounds.sort();
1003     }
1004 
1005     @Override
getGLId()1006     public GLId getGLId() {
1007         return mGLId;
1008     }
1009 }
1010