GLES20Canvas.java revision 6eb33768a15e2b4cc647bc55474568cf710876db
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.ui;
17
18import android.graphics.Bitmap;
19import android.graphics.RectF;
20import android.opengl.GLES20;
21import android.opengl.GLUtils;
22import android.opengl.Matrix;
23import android.util.Log;
24
25import com.android.gallery3d.common.Utils;
26import com.android.gallery3d.util.IntArray;
27
28import java.nio.Buffer;
29import java.nio.ByteBuffer;
30import java.nio.ByteOrder;
31import java.nio.FloatBuffer;
32import java.util.ArrayList;
33import java.util.Arrays;
34
35import javax.microedition.khronos.opengles.GL11;
36import javax.microedition.khronos.opengles.GL11ExtensionPack;
37
38public class GLES20Canvas extends GLCanvas implements GLId {
39    // ************** Constants **********************
40    private static final String TAG = GLES20Canvas.class.getSimpleName();
41    private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
42    private static final float OPAQUE_ALPHA = 0.95f;
43
44    private static final int COORDS_PER_VERTEX = 2;
45    private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
46
47    private static final int COUNT_FILL_VERTEX = 4;
48    private static final int COUNT_LINE_VERTEX = 2;
49    private static final int COUNT_RECT_VERTEX = 4;
50    private static final int OFFSET_FILL_RECT = 0;
51    private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
52    private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
53
54    private static final float[] BOX_COORDINATES = {
55            0, 0, // Fill rectangle
56            1, 0,
57            0, 1,
58            1, 1,
59            0, 0, // Draw line
60            1, 1,
61            0, 0, // Draw rectangle outline
62            0, 1,
63            1, 1,
64            1, 0,
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.a *= " + 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.a *= " + 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    private ArrayList<Blending> mBlendings = new ArrayList<Blending>();
141
142    private int mCurrentAlphaIndex = 0;
143    private int mCurrentMatrixIndex = 0;
144
145    // Viewport size
146    private int mWidth;
147    private int mHeight;
148
149    // Projection matrix
150    private float[] mProjectionMatrix = new float[MATRIX_SIZE];
151
152    // Screen size for when we aren't bound to a texture
153    private int mScreenWidth;
154    private int mScreenHeight;
155
156    // GL programs
157    private int mDrawProgram;
158    private int mTextureProgram;
159    private int mOesTextureProgram;
160    private int mMeshProgram;
161
162    // GL buffer containing BOX_COORDINATES
163    private int mBoxCoordinates;
164
165    // Handle indices -- common
166    private static final int INDEX_POSITION = 0;
167    private static final int INDEX_MATRIX = 1;
168
169    // Handle indices -- draw
170    private static final int INDEX_COLOR = 2;
171
172    // Handle indices -- texture
173    private static final int INDEX_TEXTURE_MATRIX = 2;
174    private static final int INDEX_TEXTURE_SAMPLER = 3;
175    private static final int INDEX_ALPHA = 4;
176
177    // Handle indices -- mesh
178    private static final int INDEX_TEXTURE_COORD = 2;
179
180    private abstract static class ShaderParameter {
181        public int handle;
182        protected final String mName;
183
184        public ShaderParameter(String name) {
185            mName = name;
186        }
187
188        public abstract void loadHandle(int program);
189    }
190
191    private static class UniformShaderParameter extends ShaderParameter {
192        public UniformShaderParameter(String name) {
193            super(name);
194        }
195
196        @Override
197        public void loadHandle(int program) {
198            handle = GLES20.glGetUniformLocation(program, mName);
199            checkError();
200        }
201    }
202
203    private static class AttributeShaderParameter extends ShaderParameter {
204        public AttributeShaderParameter(String name) {
205            super(name);
206        }
207
208        @Override
209        public void loadHandle(int program) {
210            handle = GLES20.glGetAttribLocation(program, mName);
211            checkError();
212        }
213    }
214
215    ShaderParameter[] mDrawParameters = {
216            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
217            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
218            new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
219    };
220    ShaderParameter[] mTextureParameters = {
221            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
222            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
223            new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
224            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
225            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
226    };
227    ShaderParameter[] mOesTextureParameters = {
228            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
229            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
230            new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
231            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
232            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
233    };
234    ShaderParameter[] mMeshParameters = {
235            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
236            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
237            new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
238            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
239            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
240    };
241
242    private final IntArray mUnboundTextures = new IntArray();
243    private final IntArray mDeleteBuffers = new IntArray();
244
245    // Keep track of statistics for debugging
246    private int mCountDrawMesh = 0;
247    private int mCountTextureRect = 0;
248    private int mCountFillRect = 0;
249    private int mCountDrawLine = 0;
250
251    private int mNextTextureId = 1;
252
253    // Buffer for framebuffer IDs -- we keep track so we can switch the attached
254    // texture.
255    private int[] mFrameBuffer = new int[1];
256
257    // Bound textures.
258    private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
259
260    // Temporary variables used within calculations
261    private final float[] mTempMatrix = new float[32];
262    private final float[] mTempColor = new float[4];
263    private final RectF mTempSourceRect = new RectF();
264    private final RectF mTempTargetRect = new RectF();
265    private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
266    private final int[] mTempIntArray = new int[1];
267
268    public GLES20Canvas() {
269        Matrix.setIdentityM(mTempTextureMatrix, 0);
270        Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
271        mAlphas[mCurrentAlphaIndex] = 1f;
272        mTargetTextures.add(null);
273    }
274
275    @Override
276    public void initialize(GL11 gl) {
277        FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
278        mBoxCoordinates = uploadBuffer(boxBuffer);
279
280        int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
281        int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
282        int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
283        int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
284        int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
285        int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
286                OES_TEXTURE_FRAGMENT_SHADER);
287
288        mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
289        mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
290                mTextureParameters);
291        mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
292                mOesTextureParameters);
293        mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
294
295        mBlendings.clear();
296        mBlendings.add(null);
297        setBlending(Blending.Mix);
298    }
299
300    private static FloatBuffer createBuffer(float[] values) {
301        // First create an nio buffer, then create a VBO from it.
302        int size = values.length * FLOAT_SIZE;
303        FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
304                .asFloatBuffer();
305        buffer.put(values, 0, values.length).position(0);
306        return buffer;
307    }
308
309    private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
310        int program = GLES20.glCreateProgram();
311        checkError();
312        if (program == 0) {
313            throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
314        }
315        GLES20.glAttachShader(program, vertexShader);
316        checkError();
317        GLES20.glAttachShader(program, fragmentShader);
318        checkError();
319        GLES20.glLinkProgram(program);
320        checkError();
321        int[] mLinkStatus = mTempIntArray;
322        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
323        if (mLinkStatus[0] != GLES20.GL_TRUE) {
324            Log.e(TAG, "Could not link program: ");
325            Log.e(TAG, GLES20.glGetProgramInfoLog(program));
326            GLES20.glDeleteProgram(program);
327            program = 0;
328        }
329        for (int i = 0; i < params.length; i++) {
330            params[i].loadHandle(program);
331        }
332        return program;
333    }
334
335    private static int loadShader(int type, String shaderCode) {
336        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
337        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
338        int shader = GLES20.glCreateShader(type);
339
340        // add the source code to the shader and compile it
341        GLES20.glShaderSource(shader, shaderCode);
342        checkError();
343        GLES20.glCompileShader(shader);
344        checkError();
345
346        return shader;
347    }
348
349    @Override
350    public void setSize(int width, int height) {
351        mWidth = width;
352        mHeight = height;
353        GLES20.glViewport(0, 0, mWidth, mHeight);
354        checkError();
355        Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
356        Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
357        if (getTargetTexture() == null) {
358            mScreenWidth = width;
359            mScreenHeight = height;
360            Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
361            Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
362        }
363    }
364
365    @Override
366    public void clearBuffer() {
367        GLES20.glClearColor(0f, 0f, 0f, 1f);
368        checkError();
369        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
370        checkError();
371    }
372
373    @Override
374    public void clearBuffer(float[] argb) {
375        GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
376        checkError();
377        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
378        checkError();
379    }
380
381    @Override
382    public float getAlpha() {
383        return mAlphas[mCurrentAlphaIndex];
384    }
385
386    @Override
387    public void setAlpha(float alpha) {
388        mAlphas[mCurrentAlphaIndex] = alpha;
389    }
390
391    @Override
392    public void multiplyAlpha(float alpha) {
393        setAlpha(getAlpha() * alpha);
394    }
395
396    @Override
397    public void translate(float x, float y, float z) {
398        Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
399    }
400
401    // This is a faster version of translate(x, y, z) because
402    // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
403    // (3) we unroll the loop
404    @Override
405    public void translate(float x, float y) {
406        int index = mCurrentMatrixIndex;
407        float[] m = mMatrices;
408        m[index + 12] += m[index + 0] * x + m[index + 4] * y;
409        m[index + 13] += m[index + 1] * x + m[index + 5] * y;
410        m[index + 14] += m[index + 2] * x + m[index + 6] * y;
411        m[index + 15] += m[index + 3] * x + m[index + 7] * y;
412    }
413
414    @Override
415    public void scale(float sx, float sy, float sz) {
416        Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
417    }
418
419    @Override
420    public void rotate(float angle, float x, float y, float z) {
421        if (angle == 0f) {
422            return;
423        }
424        float[] temp = mTempMatrix;
425        Matrix.setRotateM(temp, 0, angle, x, y, z);
426        float[] matrix = mMatrices;
427        int index = mCurrentMatrixIndex;
428        Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
429        System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
430    }
431
432    @Override
433    public void multiplyMatrix(float[] matrix, int offset) {
434        float[] temp = mTempMatrix;
435        float[] currentMatrix = mMatrices;
436        int index = mCurrentMatrixIndex;
437        Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
438        System.arraycopy(temp, 0, currentMatrix, index, 16);
439    }
440
441    @Override
442    public void save() {
443        save(SAVE_FLAG_ALL);
444    }
445
446    @Override
447    public void save(int saveFlags) {
448        boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
449        if (saveAlpha) {
450            float currentAlpha = getAlpha();
451            mCurrentAlphaIndex++;
452            if (mAlphas.length <= mCurrentAlphaIndex) {
453                mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
454            }
455            mAlphas[mCurrentAlphaIndex] = currentAlpha;
456        }
457        boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
458        if (saveMatrix) {
459            int currentIndex = mCurrentMatrixIndex;
460            mCurrentMatrixIndex += MATRIX_SIZE;
461            if (mMatrices.length <= mCurrentMatrixIndex) {
462                mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
463            }
464            System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
465        }
466        boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
467        if (saveBlending) {
468            mBlendings.add(mBlendings.get(mBlendings.size() - 1));
469        }
470        mSaveFlags.add(saveFlags);
471    }
472
473    @Override
474    public void restore() {
475        int restoreFlags = mSaveFlags.removeLast();
476        boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
477        if (restoreAlpha) {
478            mCurrentAlphaIndex--;
479        }
480        boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
481        if (restoreMatrix) {
482            mCurrentMatrixIndex -= MATRIX_SIZE;
483        }
484        boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
485        if (restoreBlending) {
486            setBlending(mBlendings.get(mBlendings.size() - 2));
487            mBlendings.remove(mBlendings.size() - 1);
488        }
489    }
490
491    @Override
492    public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
493        draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
494                paint);
495        mCountDrawLine++;
496    }
497
498    @Override
499    public void drawRect(float x, float y, float width, float height, GLPaint paint) {
500        draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
501        mCountDrawLine++;
502    }
503
504    private void draw(int type, int offset, int count, float x, float y, float width, float height,
505            GLPaint paint) {
506        draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
507    }
508
509    private void draw(int type, int offset, int count, float x, float y, float width, float height,
510            int color, float lineWidth) {
511        prepareDraw(offset, color, lineWidth);
512        draw(mDrawParameters, type, count, x, y, width, height);
513    }
514
515    private void prepareDraw(int offset, int color, float lineWidth) {
516        GLES20.glUseProgram(mDrawProgram);
517        checkError();
518        if (lineWidth > 0) {
519            GLES20.glLineWidth(lineWidth);
520            checkError();
521        }
522        float[] colorArray = getColor(color);
523        boolean blendingEnabled = (colorArray[3] < 1f);
524        enableBlending(blendingEnabled);
525        if (blendingEnabled) {
526            GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
527            checkError();
528        }
529
530        GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
531        setPosition(mDrawParameters, offset);
532        checkError();
533    }
534
535    private float[] getColor(int color) {
536        float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
537        float red = ((color >>> 16) & 0xFF) / 255f * alpha;
538        float green = ((color >>> 8) & 0xFF) / 255f * alpha;
539        float blue = (color & 0xFF) / 255f * alpha;
540        mTempColor[0] = red;
541        mTempColor[1] = green;
542        mTempColor[2] = blue;
543        mTempColor[3] = alpha;
544        return mTempColor;
545    }
546
547    private void enableBlending(boolean enableBlending) {
548        if (enableBlending) {
549            GLES20.glEnable(GLES20.GL_BLEND);
550            checkError();
551        } else {
552            GLES20.glDisable(GLES20.GL_BLEND);
553            checkError();
554        }
555    }
556
557    private void setPosition(ShaderParameter[] params, int offset) {
558        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
559        checkError();
560        GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
561                GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
562        checkError();
563    }
564
565    private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
566            float height) {
567        setMatrix(params, x, y, width, height);
568        int positionHandle = params[INDEX_POSITION].handle;
569        GLES20.glEnableVertexAttribArray(positionHandle);
570        checkError();
571        GLES20.glDrawArrays(type, 0, count);
572        checkError();
573        GLES20.glDisableVertexAttribArray(positionHandle);
574        checkError();
575    }
576
577    private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
578        Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
579        Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
580        Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
581        GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
582        checkError();
583    }
584
585    @Override
586    public void fillRect(float x, float y, float width, float height, int color) {
587        draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
588                color, 0f);
589        mCountFillRect++;
590    }
591
592    @Override
593    public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
594        if (width <= 0 || height <= 0) {
595            return;
596        }
597        copyTextureCoordinates(texture, mTempSourceRect);
598        mTempTargetRect.set(x, y, x + width, y + height);
599        convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
600        drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
601    }
602
603    private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
604        int left = 0;
605        int top = 0;
606        int right = texture.getWidth();
607        int bottom = texture.getHeight();
608        if (texture.hasBorder()) {
609            left = 1;
610            top = 1;
611            right -= 1;
612            bottom -= 1;
613        }
614        outRect.set(left, top, right, bottom);
615    }
616
617    @Override
618    public void drawTexture(BasicTexture texture, RectF source, RectF target) {
619        if (target.width() <= 0 || target.height() <= 0) {
620            return;
621        }
622        mTempSourceRect.set(source);
623        mTempTargetRect.set(target);
624
625        convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
626        drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
627    }
628
629    @Override
630    public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
631            int h) {
632        if (w <= 0 || h <= 0) {
633            return;
634        }
635        mTempTargetRect.set(x, y, x + w, y + h);
636        drawTextureRect(texture, textureTransform, mTempTargetRect);
637    }
638
639    private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
640        setTextureMatrix(source);
641        drawTextureRect(texture, mTempTextureMatrix, target);
642    }
643
644    private void setTextureMatrix(RectF source) {
645        mTempTextureMatrix[0] = source.width();
646        mTempTextureMatrix[5] = source.height();
647        mTempTextureMatrix[12] = source.left;
648        mTempTextureMatrix[13] = source.top;
649    }
650
651    // This function changes the source coordinate to the texture coordinates.
652    // It also clips the source and target coordinates if it is beyond the
653    // bound of the texture.
654    private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
655        int width = texture.getWidth();
656        int height = texture.getHeight();
657        int texWidth = texture.getTextureWidth();
658        int texHeight = texture.getTextureHeight();
659        // Convert to texture coordinates
660        source.left /= texWidth;
661        source.right /= texWidth;
662        source.top /= texHeight;
663        source.bottom /= texHeight;
664
665        // Clip if the rendering range is beyond the bound of the texture.
666        float xBound = (float) width / texWidth;
667        if (source.right > xBound) {
668            target.right = target.left + target.width() * (xBound - source.left) / source.width();
669            source.right = xBound;
670        }
671        float yBound = (float) height / texHeight;
672        if (source.bottom > yBound) {
673            target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
674            source.bottom = yBound;
675        }
676    }
677
678    private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
679        ShaderParameter[] params = prepareTexture(texture);
680        setPosition(params, OFFSET_FILL_RECT);
681        GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
682        checkError();
683        draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
684                target.width(), target.height());
685        mCountTextureRect++;
686    }
687
688    private ShaderParameter[] prepareTexture(BasicTexture texture) {
689        ShaderParameter[] params;
690        int program;
691        if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
692            params = mTextureParameters;
693            program = mTextureProgram;
694        } else {
695            params = mOesTextureParameters;
696            program = mOesTextureProgram;
697        }
698        prepareTexture(texture, program, params);
699        return params;
700    }
701
702    private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
703        GLES20.glUseProgram(program);
704        checkError();
705        enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
706        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
707        checkError();
708        texture.onBind(this);
709        GLES20.glBindTexture(texture.getTarget(), texture.getId());
710        checkError();
711        GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
712        checkError();
713        GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
714        checkError();
715    }
716
717    @Override
718    public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
719            int indexBuffer, int indexCount) {
720        prepareTexture(texture, mMeshProgram, mMeshParameters);
721
722        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
723        checkError();
724
725        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
726        checkError();
727        int positionHandle = mMeshParameters[INDEX_POSITION].handle;
728        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
729                VERTEX_STRIDE, 0);
730        checkError();
731
732        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
733        checkError();
734        int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
735        GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
736                false, VERTEX_STRIDE, 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
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
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
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
797    public void deleteBuffer(int bufferId) {
798        synchronized (mUnboundTextures) {
799            mDeleteBuffers.add(bufferId);
800        }
801    }
802
803    @Override
804    public void deleteRecycledResources() {
805        synchronized (mUnboundTextures) {
806            IntArray ids = mUnboundTextures;
807            if (mUnboundTextures.size() > 0) {
808                glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
809                ids.clear();
810            }
811
812            ids = mDeleteBuffers;
813            if (ids.size() > 0) {
814                glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
815                ids.clear();
816            }
817        }
818    }
819
820    @Override
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
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
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
847    private RawTexture getTargetTexture() {
848        return mTargetTextures.get(mTargetTextures.size() - 1);
849    }
850
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
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
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
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
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
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
942    public int uploadBuffer(FloatBuffer buf) {
943        return uploadBuffer(buf, FLOAT_SIZE);
944    }
945
946    @Override
947    public int uploadBuffer(ByteBuffer buf) {
948        return uploadBuffer(buf, 1);
949    }
950
951    private int uploadBuffer(Buffer buffer, int elementSize) {
952        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
963    @Override
964    public void setBlending(Blending blending) {
965        Blending currentBlending = mBlendings.get(mBlendings.size() - 1);
966        if (currentBlending == blending) {
967            return; // nothing to change
968        }
969        mBlendings.set(mBlendings.size() - 1, blending);
970        int srcFunc = GLES20.GL_ONE;
971        int dstFunc;
972        switch (blending) {
973            case Additive:
974                dstFunc = GLES20.GL_ONE;
975                break;
976            case Mix:
977                dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
978                break;
979            default:
980                Utils.fail("Unknown blend: " + blending);
981                dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
982                break;
983        }
984        GLES20.glBlendFunc(srcFunc, dstFunc);
985        checkError();
986    }
987
988    @Override
989    public int generateTexture() {
990        // Can use anything as a lock. No need to create a new object.
991        synchronized (mTempIntArray) {
992            return mNextTextureId++;
993        }
994    }
995
996    @Override
997    public void glGenBuffers(int n, int[] buffers, int offset) {
998        GLES20.glGenBuffers(n, buffers, offset);
999        checkError();
1000    }
1001
1002    @Override
1003    public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
1004        GLES20.glDeleteTextures(n, textures, offset);
1005        checkError();
1006    }
1007
1008    @Override
1009    public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
1010        GLES20.glDeleteBuffers(n, buffers, offset);
1011        checkError();
1012    }
1013
1014    @Override
1015    public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
1016        GLES20.glDeleteFramebuffers(n, buffers, offset);
1017        checkError();
1018    }
1019
1020    @Override
1021    public void enableStencil() {
1022        GLES20.glEnable(GLES20.GL_STENCIL_TEST);
1023    }
1024
1025    @Override
1026    public void disableStencil() {
1027        GLES20.glDisable(GLES20.GL_STENCIL_TEST);
1028    }
1029
1030    @Override
1031    public void clearStencilBuffer() {
1032        GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT);
1033    }
1034
1035    @Override
1036    public void updateStencil(boolean update) {
1037        int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP;
1038        GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp);
1039    }
1040
1041    @Override
1042    public void drawOnlyOutsideStencil(boolean onlyOutside) {
1043        int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS;
1044        GLES20.glStencilFunc(func, 1, 1);
1045    }
1046
1047    private static void checkError() {
1048        int error = GLES20.glGetError();
1049        if (error != 0) {
1050            Throwable t = new Throwable();
1051            Log.e(TAG, "GL error: " + error, t);
1052        }
1053    }
1054
1055    @SuppressWarnings("unused")
1056    private static void printMatrix(String message, float[] m, int offset) {
1057        StringBuilder b = new StringBuilder(message);
1058        for (int i = 0; i < MATRIX_SIZE; i++) {
1059            b.append(' ');
1060            if (i % 4 == 0) {
1061                b.append('\n');
1062            }
1063            b.append(m[offset + i]);
1064        }
1065        Log.v(TAG, b.toString());
1066    }
1067
1068}
1069