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