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