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 */
16package com.android.gallery3d.glrenderer;
17
18import android.graphics.Bitmap;
19import android.graphics.Rect;
20import android.graphics.RectF;
21import android.opengl.GLES20;
22import android.opengl.GLUtils;
23import android.opengl.Matrix;
24import android.util.Log;
25
26import java.nio.Buffer;
27import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
29import java.nio.FloatBuffer;
30import java.util.ArrayList;
31import java.util.Arrays;
32
33public 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
183        public ShaderParameter(String name) {
184            mName = name;
185        }
186
187        public abstract void loadHandle(int program);
188    }
189
190    private static class UniformShaderParameter extends ShaderParameter {
191        public UniformShaderParameter(String name) {
192            super(name);
193        }
194
195        @Override
196        public void loadHandle(int program) {
197            handle = GLES20.glGetUniformLocation(program, mName);
198            checkError();
199        }
200    }
201
202    private static class AttributeShaderParameter extends ShaderParameter {
203        public AttributeShaderParameter(String name) {
204            super(name);
205        }
206
207        @Override
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
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
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
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
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
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
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
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
376    public float getAlpha() {
377        return mAlphas[mCurrentAlphaIndex];
378    }
379
380    @Override
381    public void setAlpha(float alpha) {
382        mAlphas[mCurrentAlphaIndex] = alpha;
383    }
384
385    @Override
386    public void multiplyAlpha(float alpha) {
387        setAlpha(getAlpha() * alpha);
388    }
389
390    @Override
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
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
409    public void scale(float sx, float sy, float sz) {
410        Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
411    }
412
413    @Override
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
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
436    public void save() {
437        save(SAVE_FLAG_ALL);
438    }
439
440    @Override
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
626    private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
627        setTextureMatrix(source);
628        drawTextureRect(texture, mTempTextureMatrix, target);
629    }
630
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.
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
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
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
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
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
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
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
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
796    public void deleteBuffer(int bufferId) {
797        synchronized (mUnboundTextures) {
798            mDeleteBuffers.add(bufferId);
799        }
800    }
801
802    @Override
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
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
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
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
846    private RawTexture getTargetTexture() {
847        return mTargetTextures.get(mTargetTextures.size() - 1);
848    }
849
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
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
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
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
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
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
941    public int uploadBuffer(FloatBuffer buf) {
942        return uploadBuffer(buf, FLOAT_SIZE);
943    }
944
945    @Override
946    public int uploadBuffer(ByteBuffer buf) {
947        return uploadBuffer(buf, 1);
948    }
949
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
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")
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
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
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
1005    public GLId getGLId() {
1006        return mGLId;
1007    }
1008}
1009