1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2012 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.gallery3d.glrenderer;
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Bitmap;
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Rect;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.RectF;
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.opengl.GLES20;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.opengl.GLUtils;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.opengl.Matrix;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport com.android.gallery3d.util.IntArray;
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.Buffer;
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteBuffer;
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.ByteOrder;
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.nio.FloatBuffer;
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.ArrayList;
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.Arrays;
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapublic class GLES20Canvas implements GLCanvas {
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // ************** Constants **********************
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TAG = GLES20Canvas.class.getSimpleName();
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final float OPAQUE_ALPHA = 0.95f;
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int COORDS_PER_VERTEX = 2;
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int COUNT_FILL_VERTEX = 4;
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int COUNT_LINE_VERTEX = 2;
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int COUNT_RECT_VERTEX = 4;
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int OFFSET_FILL_RECT = 0;
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final float[] BOX_COORDINATES = {
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0, 0, // Fill rectangle
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            1, 0,
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0, 1,
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            1, 1,
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0, 0, // Draw line
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            1, 1,
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0, 0, // Draw rectangle outline
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            0, 1,
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            1, 1,
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            1, 0,
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final float[] BOUNDS_COORDINATES = {
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        0, 0, 0, 1,
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        1, 1, 0, 1,
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String POSITION_ATTRIBUTE = "aPosition";
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String COLOR_UNIFORM = "uColor";
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String MATRIX_UNIFORM = "uMatrix";
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String ALPHA_UNIFORM = "uAlpha";
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String DRAW_VERTEX_SHADER = ""
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "void main() {\n"
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "}\n";
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String DRAW_FRAGMENT_SHADER = ""
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "precision mediump float;\n"
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform vec4 " + COLOR_UNIFORM + ";\n"
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "void main() {\n"
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_FragColor = " + COLOR_UNIFORM + ";\n"
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "}\n";
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TEXTURE_VERTEX_SHADER = ""
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n"
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "varying vec2 vTextureCoord;\n"
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "void main() {\n"
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "}\n";
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String MESH_VERTEX_SHADER = ""
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "varying vec2 vTextureCoord;\n"
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "void main() {\n"
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "}\n";
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TEXTURE_FRAGMENT_SHADER = ""
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "precision mediump float;\n"
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "varying vec2 vTextureCoord;\n"
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform float " + ALPHA_UNIFORM + ";\n"
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n"
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "void main() {\n"
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "}\n";
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "#extension GL_OES_EGL_image_external : require\n"
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "precision mediump float;\n"
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "varying vec2 vTextureCoord;\n"
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform float " + ALPHA_UNIFORM + ";\n"
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "void main() {\n"
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            + "}\n";
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INITIAL_RESTORE_STATE_SIZE = 8;
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int MATRIX_SIZE = 16;
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Keep track of restore state
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private IntArray mSaveFlags = new IntArray();
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mCurrentAlphaIndex = 0;
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mCurrentMatrixIndex = 0;
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Viewport size
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mWidth;
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mHeight;
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Projection matrix
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private float[] mProjectionMatrix = new float[MATRIX_SIZE];
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Screen size for when we aren't bound to a texture
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mScreenWidth;
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mScreenHeight;
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // GL programs
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mDrawProgram;
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mTextureProgram;
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mOesTextureProgram;
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mMeshProgram;
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // GL buffer containing BOX_COORDINATES
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mBoxCoordinates;
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Handle indices -- common
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INDEX_POSITION = 0;
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INDEX_MATRIX = 1;
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Handle indices -- draw
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INDEX_COLOR = 2;
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Handle indices -- texture
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INDEX_TEXTURE_MATRIX = 2;
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INDEX_TEXTURE_SAMPLER = 3;
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INDEX_ALPHA = 4;
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Handle indices -- mesh
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int INDEX_TEXTURE_COORD = 2;
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private abstract static class ShaderParameter {
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public int handle;
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        protected final String mName;
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public ShaderParameter(String name) {
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mName = name;
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public abstract void loadHandle(int program);
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class UniformShaderParameter extends ShaderParameter {
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public UniformShaderParameter(String name) {
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            super(name);
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void loadHandle(int program) {
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            handle = GLES20.glGetUniformLocation(program, mName);
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class AttributeShaderParameter extends ShaderParameter {
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public AttributeShaderParameter(String name) {
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            super(name);
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void loadHandle(int program) {
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            handle = GLES20.glGetAttribLocation(program, mName);
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
213104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
214104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
215104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
216104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    ShaderParameter[] mDrawParameters = {
217104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
218104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
219104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
220104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
221104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    ShaderParameter[] mTextureParameters = {
222104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
223104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
224104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
225104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
226104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
227104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
228104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    ShaderParameter[] mOesTextureParameters = {
229104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
230104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
231104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
232104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
233104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
234104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
235104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    ShaderParameter[] mMeshParameters = {
236104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
237104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
238104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
239104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
240104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
241104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    };
242104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
243104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final IntArray mUnboundTextures = new IntArray();
244104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final IntArray mDeleteBuffers = new IntArray();
245104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
246104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Keep track of statistics for debugging
247104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mCountDrawMesh = 0;
248104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mCountTextureRect = 0;
249104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mCountFillRect = 0;
250104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int mCountDrawLine = 0;
251104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
252104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Buffer for framebuffer IDs -- we keep track so we can switch the attached
253104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // texture.
254104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int[] mFrameBuffer = new int[1];
255104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
256104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Bound textures.
257104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
258104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
259104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Temporary variables used within calculations
260104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final float[] mTempMatrix = new float[32];
261104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final float[] mTempColor = new float[4];
262104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final RectF mTempSourceRect = new RectF();
263104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final RectF mTempTargetRect = new RectF();
264104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
265104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private final int[] mTempIntArray = new int[1];
266104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
267104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final GLId mGLId = new GLES20IdImpl();
268104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
269104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public GLES20Canvas() {
270104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.setIdentityM(mTempTextureMatrix, 0);
271104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
272104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mAlphas[mCurrentAlphaIndex] = 1f;
273104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTargetTextures.add(null);
274104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
275104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
276104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mBoxCoordinates = uploadBuffer(boxBuffer);
277104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
278104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
279104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
280104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
281104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
282104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
283104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
284104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                OES_TEXTURE_FRAGMENT_SHADER);
285104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
286104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
287104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
288104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mTextureParameters);
289104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
290104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mOesTextureParameters);
291104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
292104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
293104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
294104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
295104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
296104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static FloatBuffer createBuffer(float[] values) {
297104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // First create an nio buffer, then create a VBO from it.
298104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int size = values.length * FLOAT_SIZE;
299104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
300104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                .asFloatBuffer();
301104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        buffer.put(values, 0, values.length).position(0);
302104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return buffer;
303104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
304104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
305104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
306104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int program = GLES20.glCreateProgram();
307104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
308104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (program == 0) {
309104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
310104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
311104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glAttachShader(program, vertexShader);
312104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
313104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glAttachShader(program, fragmentShader);
314104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
315104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glLinkProgram(program);
316104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
317104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int[] mLinkStatus = mTempIntArray;
318104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
319104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mLinkStatus[0] != GLES20.GL_TRUE) {
320104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, "Could not link program: ");
321104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, GLES20.glGetProgramInfoLog(program));
322104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glDeleteProgram(program);
323104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            program = 0;
324104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
325104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (int i = 0; i < params.length; i++) {
326104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            params[i].loadHandle(program);
327104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
328104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return program;
329104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
330104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
331104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static int loadShader(int type, String shaderCode) {
332104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
333104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
334104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int shader = GLES20.glCreateShader(type);
335104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
336104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // add the source code to the shader and compile it
337104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glShaderSource(shader, shaderCode);
338104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
339104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glCompileShader(shader);
340104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
341104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
342104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return shader;
343104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
344104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
345104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
346104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void setSize(int width, int height) {
347104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mWidth = width;
348104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mHeight = height;
349104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glViewport(0, 0, mWidth, mHeight);
350104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
351104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
352104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
353104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (getTargetTexture() == null) {
354104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mScreenWidth = width;
355104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mScreenHeight = height;
356104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
357104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
358104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
359104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
360104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
361104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
362104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void clearBuffer() {
363104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glClearColor(0f, 0f, 0f, 1f);
364104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
365104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
366104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
367104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
368104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
369104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
370104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void clearBuffer(float[] argb) {
371104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
372104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
373104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
374104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
375104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
376104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
377104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
378104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public float getAlpha() {
379104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mAlphas[mCurrentAlphaIndex];
380104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
381104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
382104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
383104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void setAlpha(float alpha) {
384104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mAlphas[mCurrentAlphaIndex] = alpha;
385104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
386104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
387104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
388104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void multiplyAlpha(float alpha) {
389104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setAlpha(getAlpha() * alpha);
390104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
391104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
392104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
393104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void translate(float x, float y, float z) {
394104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
395104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
396104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
397104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // This is a faster version of translate(x, y, z) because
398104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
399104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // (3) we unroll the loop
400104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
401104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void translate(float x, float y) {
402104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int index = mCurrentMatrixIndex;
403104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float[] m = mMatrices;
404104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        m[index + 12] += m[index + 0] * x + m[index + 4] * y;
405104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        m[index + 13] += m[index + 1] * x + m[index + 5] * y;
406104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        m[index + 14] += m[index + 2] * x + m[index + 6] * y;
407104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        m[index + 15] += m[index + 3] * x + m[index + 7] * y;
408104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
409104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
410104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
411104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void scale(float sx, float sy, float sz) {
412104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
413104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
414104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
415104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
416104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void rotate(float angle, float x, float y, float z) {
417104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (angle == 0f) {
418104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
419104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
420104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float[] temp = mTempMatrix;
421104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.setRotateM(temp, 0, angle, x, y, z);
422104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float[] matrix = mMatrices;
423104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int index = mCurrentMatrixIndex;
424104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
425104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
426104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
427104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
428104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
429104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void multiplyMatrix(float[] matrix, int offset) {
430104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float[] temp = mTempMatrix;
431104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float[] currentMatrix = mMatrices;
432104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int index = mCurrentMatrixIndex;
433104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
434104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        System.arraycopy(temp, 0, currentMatrix, index, 16);
435104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
436104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
437104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
438104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void save() {
439104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        save(SAVE_FLAG_ALL);
440104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
441104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
442104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
443104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void save(int saveFlags) {
444104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
445104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (saveAlpha) {
446104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            float currentAlpha = getAlpha();
447104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCurrentAlphaIndex++;
448104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mAlphas.length <= mCurrentAlphaIndex) {
449104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
450104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
451104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mAlphas[mCurrentAlphaIndex] = currentAlpha;
452104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
453104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
454104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (saveMatrix) {
455104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int currentIndex = mCurrentMatrixIndex;
456104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCurrentMatrixIndex += MATRIX_SIZE;
457104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mMatrices.length <= mCurrentMatrixIndex) {
458104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
459104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
460104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
461104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
462104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mSaveFlags.add(saveFlags);
463104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
464104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
465104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
466104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void restore() {
467104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int restoreFlags = mSaveFlags.removeLast();
468104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
469104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (restoreAlpha) {
470104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCurrentAlphaIndex--;
471104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
472104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
473104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (restoreMatrix) {
474104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mCurrentMatrixIndex -= MATRIX_SIZE;
475104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
476104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
477104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
478104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
479104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
480104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
481104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                paint);
482104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountDrawLine++;
483104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
484104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
485104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
486104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawRect(float x, float y, float width, float height, GLPaint paint) {
487104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
488104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountDrawLine++;
489104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
490104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
491104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void draw(int type, int offset, int count, float x, float y, float width, float height,
492104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLPaint paint) {
493104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
494104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
495104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
496104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void draw(int type, int offset, int count, float x, float y, float width, float height,
497104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int color, float lineWidth) {
498104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        prepareDraw(offset, color, lineWidth);
499104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        draw(mDrawParameters, type, count, x, y, width, height);
500104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
501104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
502104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void prepareDraw(int offset, int color, float lineWidth) {
503104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glUseProgram(mDrawProgram);
504104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
505104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (lineWidth > 0) {
506104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glLineWidth(lineWidth);
507104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
508104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
509104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float[] colorArray = getColor(color);
510104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean blendingEnabled = (colorArray[3] < 1f);
511104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        enableBlending(blendingEnabled);
512104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (blendingEnabled) {
513104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
514104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
515104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
516104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
517104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
518104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setPosition(mDrawParameters, offset);
519104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
520104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
521104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
522104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private float[] getColor(int color) {
523104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
524104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float red = ((color >>> 16) & 0xFF) / 255f * alpha;
525104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float green = ((color >>> 8) & 0xFF) / 255f * alpha;
526104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float blue = (color & 0xFF) / 255f * alpha;
527104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempColor[0] = red;
528104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempColor[1] = green;
529104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempColor[2] = blue;
530104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempColor[3] = alpha;
531104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTempColor;
532104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
533104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
534104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void enableBlending(boolean enableBlending) {
535104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (enableBlending) {
536104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glEnable(GLES20.GL_BLEND);
537104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
538104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
539104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glDisable(GLES20.GL_BLEND);
540104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
541104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
542104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
543104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
544104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void setPosition(ShaderParameter[] params, int offset) {
545104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
546104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
547104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
548104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
549104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
550104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
551104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
552104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
553104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
554104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
555104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            float height) {
556104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setMatrix(params, x, y, width, height);
557104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int positionHandle = params[INDEX_POSITION].handle;
558104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glEnableVertexAttribArray(positionHandle);
559104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
560104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glDrawArrays(type, 0, count);
561104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
562104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glDisableVertexAttribArray(positionHandle);
563104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
564104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
565104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
566104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
567104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
568104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
569104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
570104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
571104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
572104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
573104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
574104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
575104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void fillRect(float x, float y, float width, float height, int color) {
576104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
577104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                color, 0f);
578104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountFillRect++;
579104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
580104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
581104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
582104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
583104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (width <= 0 || height <= 0) {
584104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
585104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
586104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        copyTextureCoordinates(texture, mTempSourceRect);
587104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTargetRect.set(x, y, x + width, y + height);
588104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
589104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
590104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
591104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
592104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
593104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int left = 0;
594104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int top = 0;
595104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int right = texture.getWidth();
596104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int bottom = texture.getHeight();
597104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (texture.hasBorder()) {
598104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            left = 1;
599104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            top = 1;
600104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            right -= 1;
601104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            bottom -= 1;
602104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
603104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        outRect.set(left, top, right, bottom);
604104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
605104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
606104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
607104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawTexture(BasicTexture texture, RectF source, RectF target) {
608104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (target.width() <= 0 || target.height() <= 0) {
609104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
610104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
611104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempSourceRect.set(source);
612104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTargetRect.set(target);
613104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
614104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
615104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
616104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
617104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
618104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
619104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
620104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int h) {
621104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (w <= 0 || h <= 0) {
622104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
623104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
624104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTargetRect.set(x, y, x + w, y + h);
625104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        drawTextureRect(texture, textureTransform, mTempTargetRect);
626104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
627104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
628104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
629104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setTextureMatrix(source);
630104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        drawTextureRect(texture, mTempTextureMatrix, target);
631104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
632104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
633104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void setTextureMatrix(RectF source) {
634104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTextureMatrix[0] = source.width();
635104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTextureMatrix[5] = source.height();
636104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTextureMatrix[12] = source.left;
637104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTextureMatrix[13] = source.top;
638104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
639104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
640104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // This function changes the source coordinate to the texture coordinates.
641104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // It also clips the source and target coordinates if it is beyond the
642104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // bound of the texture.
643104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
644104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int width = texture.getWidth();
645104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int height = texture.getHeight();
646104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int texWidth = texture.getTextureWidth();
647104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int texHeight = texture.getTextureHeight();
648104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Convert to texture coordinates
649104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        source.left /= texWidth;
650104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        source.right /= texWidth;
651104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        source.top /= texHeight;
652104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        source.bottom /= texHeight;
653104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
654104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // Clip if the rendering range is beyond the bound of the texture.
655104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float xBound = (float) width / texWidth;
656104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (source.right > xBound) {
657104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            target.right = target.left + target.width() * (xBound - source.left) / source.width();
658104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            source.right = xBound;
659104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
660104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float yBound = (float) height / texHeight;
661104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (source.bottom > yBound) {
662104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
663104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            source.bottom = yBound;
664104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
665104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
666104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
667104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
668104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ShaderParameter[] params = prepareTexture(texture);
669104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setPosition(params, OFFSET_FILL_RECT);
670104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
671104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
672104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (texture.isFlippedVertically()) {
673104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            save(SAVE_FLAG_MATRIX);
674104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            translate(0, target.centerY());
675104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            scale(1, -1, 1);
676104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            translate(0, -target.centerY());
677104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
678104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
679104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                target.width(), target.height());
680104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (texture.isFlippedVertically()) {
681104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            restore();
682104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
683104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountTextureRect++;
684104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
685104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
686104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private ShaderParameter[] prepareTexture(BasicTexture texture) {
687104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ShaderParameter[] params;
688104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int program;
689104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
690104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            params = mTextureParameters;
691104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            program = mTextureProgram;
692104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
693104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            params = mOesTextureParameters;
694104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            program = mOesTextureProgram;
695104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
696104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        prepareTexture(texture, program, params);
697104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return params;
698104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
699104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
700104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
701104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glUseProgram(program);
702104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
703104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
704104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
705104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
706104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        texture.onBind(this);
707104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindTexture(texture.getTarget(), texture.getId());
708104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
709104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
710104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
711104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
712104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
713104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
714104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
715104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
716104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
717104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int indexBuffer, int indexCount) {
718104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        prepareTexture(texture, mMeshProgram, mMeshParameters);
719104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
720104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
721104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
722104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
723104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
724104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
725104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int positionHandle = mMeshParameters[INDEX_POSITION].handle;
726104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
727104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                VERTEX_STRIDE, 0);
728104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
729104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
730104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
731104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
732104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
733104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
734104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                false, VERTEX_STRIDE, 0);
735104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
736104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
737104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
738104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
739104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glEnableVertexAttribArray(positionHandle);
740104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
741104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glEnableVertexAttribArray(texCoordHandle);
742104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
743104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
744104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setMatrix(mMeshParameters, x, y, 1, 1);
745104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
746104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
747104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
748104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glDisableVertexAttribArray(positionHandle);
749104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
750104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glDisableVertexAttribArray(texCoordHandle);
751104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
752104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
753104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
754104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountDrawMesh++;
755104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
756104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
757104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
758104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
759104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        copyTextureCoordinates(texture, mTempSourceRect);
760104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTempTargetRect.set(x, y, x + w, y + h);
761104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
762104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
763104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
764104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
765104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
766104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (target.width() <= 0 || target.height() <= 0) {
767104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return;
768104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
769104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        save(SAVE_FLAG_ALPHA);
770104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
771104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float currentAlpha = getAlpha();
772104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float cappedRatio = Math.min(1f, Math.max(0f, ratio));
773104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
774104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float textureAlpha = (1f - cappedRatio) * currentAlpha;
775104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setAlpha(textureAlpha);
776104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        drawTexture(texture, source, target);
777104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
778104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float colorAlpha = cappedRatio * currentAlpha;
779104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setAlpha(colorAlpha);
780104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        fillRect(target.left, target.top, target.width(), target.height(), toColor);
781104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
782104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        restore();
783104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
784104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
785104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
786104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public boolean unloadTexture(BasicTexture texture) {
787104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        boolean unload = texture.isLoaded();
788104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (unload) {
789104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            synchronized (mUnboundTextures) {
790104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mUnboundTextures.add(texture.getId());
791104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
792104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
793104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return unload;
794104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
795104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
796104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
797104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void deleteBuffer(int bufferId) {
798104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        synchronized (mUnboundTextures) {
799104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mDeleteBuffers.add(bufferId);
800104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
801104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
802104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
803104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
804104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void deleteRecycledResources() {
805104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        synchronized (mUnboundTextures) {
806104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            IntArray ids = mUnboundTextures;
807104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mUnboundTextures.size() > 0) {
808104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mGLId.glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
809104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ids.clear();
810104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
811104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
812104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            ids = mDeleteBuffers;
813104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (ids.size() > 0) {
814104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mGLId.glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
815104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ids.clear();
816104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
817104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
818104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
819104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
820104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
821104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void dumpStatisticsAndClear() {
822104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
823104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mCountTextureRect, mCountFillRect, mCountDrawLine);
824104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountDrawMesh = 0;
825104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountTextureRect = 0;
826104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountFillRect = 0;
827104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCountDrawLine = 0;
828104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Log.d(TAG, line);
829104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
830104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
831104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
832104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void endRenderTarget() {
833104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
834104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        RawTexture texture = getTargetTexture();
835104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setRenderTarget(oldTexture, texture);
836104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        restore(); // restore matrix and alpha
837104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
838104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
839104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
840104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void beginRenderTarget(RawTexture texture) {
841104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        save(); // save matrix and alpha and blending
842104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        RawTexture oldTexture = getTargetTexture();
843104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTargetTextures.add(texture);
844104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setRenderTarget(oldTexture, texture);
845104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
846104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
847104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private RawTexture getTargetTexture() {
848104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTargetTextures.get(mTargetTextures.size() - 1);
849104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
850104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
851104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
852104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (oldTexture == null && texture != null) {
853104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
854104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
855104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
856104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
857104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else if (oldTexture != null && texture == null) {
858104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
859104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
860104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
861104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
862104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
863104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
864104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (texture == null) {
865104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            setSize(mScreenWidth, mScreenHeight);
866104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
867104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            setSize(texture.getWidth(), texture.getHeight());
868104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
869104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (!texture.isLoaded()) {
870104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                texture.prepare(this);
871104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
872104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
873104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
874104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    texture.getTarget(), texture.getId(), 0);
875104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkError();
876104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
877104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkFramebufferStatus();
878104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
879104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
880104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
881104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static void checkFramebufferStatus() {
882104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
883104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
884104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            String msg = "";
885104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            switch (status) {
886104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
887104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
888104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    break;
889104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
890104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
891104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    break;
892104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
893104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
894104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    break;
895104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
896104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    msg = "GL_FRAMEBUFFER_UNSUPPORTED";
897104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    break;
898104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
899104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new RuntimeException(msg + ":" + Integer.toHexString(status));
900104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
901104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
902104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
903104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
904104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void setTextureParameters(BasicTexture texture) {
905104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int target = texture.getTarget();
906104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindTexture(target, texture.getId());
907104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
908104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
909104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
910104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
911104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
912104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
913104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
914104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
915104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void initializeTextureSize(BasicTexture texture, int format, int type) {
916104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int target = texture.getTarget();
917104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindTexture(target, texture.getId());
918104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
919104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int width = texture.getTextureWidth();
920104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int height = texture.getTextureHeight();
921104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
922104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
923104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
924104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
925104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
926104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int target = texture.getTarget();
927104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindTexture(target, texture.getId());
928104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
929104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLUtils.texImage2D(target, 0, bitmap, 0);
930104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
931104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
932104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
933104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
934104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int format, int type) {
935104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int target = texture.getTarget();
936104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindTexture(target, texture.getId());
937104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
938104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
939104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
940104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
941104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
942104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public int uploadBuffer(FloatBuffer buf) {
943104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return uploadBuffer(buf, FLOAT_SIZE);
944104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
945104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
946104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
947104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public int uploadBuffer(ByteBuffer buf) {
948104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return uploadBuffer(buf, 1);
949104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
950104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
951104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private int uploadBuffer(Buffer buffer, int elementSize) {
952104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mGLId.glGenBuffers(1, mTempIntArray, 0);
953104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
954104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int bufferId = mTempIntArray[0];
955104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
956104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
957104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
958104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                GLES20.GL_STATIC_DRAW);
959104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
960104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return bufferId;
961104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
962104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
963104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static void checkError() {
964104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int error = GLES20.glGetError();
965104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (error != 0) {
966104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Throwable t = new Throwable();
967104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, "GL error: " + error, t);
968104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
969104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
970104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
971104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @SuppressWarnings("unused")
972104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static void printMatrix(String message, float[] m, int offset) {
973104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        StringBuilder b = new StringBuilder(message);
974104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        for (int i = 0; i < MATRIX_SIZE; i++) {
975104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            b.append(' ');
976104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (i % 4 == 0) {
977104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                b.append('\n');
978104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
979104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            b.append(m[offset + i]);
980104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
981104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Log.v(TAG, b.toString());
982104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
983104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
984104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
985104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void recoverFromLightCycle() {
986104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glViewport(0, 0, mWidth, mHeight);
987104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glDisable(GLES20.GL_DEPTH_TEST);
988104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
989104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        checkError();
990104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
991104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
992104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
993104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void getBounds(Rect bounds, int x, int y, int width, int height) {
994104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
995104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
996104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE, mTempMatrix, 0, BOUNDS_COORDINATES, 0);
997104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE + 4, mTempMatrix, 0, BOUNDS_COORDINATES, 4);
998104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        bounds.left = Math.round(mTempMatrix[MATRIX_SIZE]);
999104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        bounds.right = Math.round(mTempMatrix[MATRIX_SIZE + 4]);
1000104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        bounds.top = Math.round(mTempMatrix[MATRIX_SIZE + 1]);
1001104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        bounds.bottom = Math.round(mTempMatrix[MATRIX_SIZE + 5]);
1002104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        bounds.sort();
1003104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
1004104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
1005104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
1006104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public GLId getGLId() {
1007104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mGLId;
1008104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
1009104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
1010