blob: 68f5636f738bf6163a8aa8134c151572c34fef7d [file] [log] [blame]
Owen Lina2fba682011-08-17 22:07:43 +08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.ui;
18
Owen Lina2fba682011-08-17 22:07:43 +080019import android.graphics.Rect;
20import android.graphics.RectF;
21import android.opengl.GLU;
22import android.opengl.Matrix;
23
Owen Lin73a04ff2012-03-14 17:27:24 +080024import com.android.gallery3d.common.Utils;
25import com.android.gallery3d.util.IntArray;
26
Owen Lina2fba682011-08-17 22:07:43 +080027import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
29import java.nio.FloatBuffer;
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +080030import java.util.ArrayList;
Owen Lin73a04ff2012-03-14 17:27:24 +080031
Owen Lina2fba682011-08-17 22:07:43 +080032import javax.microedition.khronos.opengles.GL10;
33import javax.microedition.khronos.opengles.GL11;
34import javax.microedition.khronos.opengles.GL11Ext;
35
36public class GLCanvasImpl implements GLCanvas {
37 @SuppressWarnings("unused")
38 private static final String TAG = "GLCanvasImp";
39
40 private static final float OPAQUE_ALPHA = 0.95f;
41
42 private static final int OFFSET_FILL_RECT = 0;
43 private static final int OFFSET_DRAW_LINE = 4;
44 private static final int OFFSET_DRAW_RECT = 6;
45 private static final float[] BOX_COORDINATES = {
46 0, 0, 1, 0, 0, 1, 1, 1, // used for filling a rectangle
47 0, 0, 1, 1, // used for drawing a line
48 0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
49
50 private final GL11 mGL;
51
52 private final float mMatrixValues[] = new float[16];
53 private final float mTextureMatrixValues[] = new float[16];
54
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +080055 // The results of mapPoints are stored in this buffer, and the order is
56 // x1, y1, x2, y2.
57 private final float mMapPointsBuffer[] = new float[4];
Owen Lina2fba682011-08-17 22:07:43 +080058
59 private final float mTextureColor[] = new float[4];
60
61 private int mBoxCoords;
62
63 private final GLState mGLState;
64
Owen Lina2fba682011-08-17 22:07:43 +080065 private float mAlpha;
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +080066 private final ArrayList<ConfigState> mRestoreStack =
67 new ArrayList<ConfigState>();
Owen Lina2fba682011-08-17 22:07:43 +080068 private ConfigState mRecycledRestoreAction;
69
70 private final RectF mDrawTextureSourceRect = new RectF();
71 private final RectF mDrawTextureTargetRect = new RectF();
72 private final float[] mTempMatrix = new float[32];
73 private final IntArray mUnboundTextures = new IntArray();
74 private final IntArray mDeleteBuffers = new IntArray();
75 private int mHeight;
76 private boolean mBlendEnabled = true;
77
78 // Drawing statistics
79 int mCountDrawLine;
80 int mCountFillRect;
81 int mCountDrawMesh;
82 int mCountTextureRect;
83 int mCountTextureOES;
84
85 GLCanvasImpl(GL11 gl) {
86 mGL = gl;
87 mGLState = new GLState(gl);
88 initialize();
89 }
90
91 public void setSize(int width, int height) {
92 Utils.assertTrue(width >= 0 && height >= 0);
93 mHeight = height;
94
95 GL11 gl = mGL;
96 gl.glViewport(0, 0, width, height);
97 gl.glMatrixMode(GL11.GL_PROJECTION);
98 gl.glLoadIdentity();
99 GLU.gluOrtho2D(gl, 0, width, 0, height);
100
101 gl.glMatrixMode(GL11.GL_MODELVIEW);
102 gl.glLoadIdentity();
103 float matrix[] = mMatrixValues;
104
105 Matrix.setIdentityM(matrix, 0);
106 Matrix.translateM(matrix, 0, 0, mHeight, 0);
107 Matrix.scaleM(matrix, 0, 1, -1, 1);
108
Owen Lina2fba682011-08-17 22:07:43 +0800109 gl.glScissor(0, 0, width, height);
110 }
111
Owen Lina2fba682011-08-17 22:07:43 +0800112 public void setAlpha(float alpha) {
113 Utils.assertTrue(alpha >= 0 && alpha <= 1);
114 mAlpha = alpha;
115 }
116
Chih-Chung Chang980724b2012-02-22 08:00:31 +0800117 public float getAlpha() {
118 return mAlpha;
119 }
120
Owen Lina2fba682011-08-17 22:07:43 +0800121 public void multiplyAlpha(float alpha) {
122 Utils.assertTrue(alpha >= 0 && alpha <= 1);
123 mAlpha *= alpha;
124 }
125
Owen Lina2fba682011-08-17 22:07:43 +0800126 private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
127 return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
128 }
129
130 private void initialize() {
131 GL11 gl = mGL;
132
133 // First create an nio buffer, then create a VBO from it.
134 int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
135 FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
136 xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0);
137
138 int[] name = new int[1];
139 gl.glGenBuffers(1, name, 0);
140 mBoxCoords = name[0];
141
142 gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
143 gl.glBufferData(GL11.GL_ARRAY_BUFFER,
144 xyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
145 xyBuffer, GL11.GL_STATIC_DRAW);
146
147 gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
148 gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
149
150 // Enable the texture coordinate array for Texture 1
151 gl.glClientActiveTexture(GL11.GL_TEXTURE1);
152 gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
153 gl.glClientActiveTexture(GL11.GL_TEXTURE0);
154 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
155
156 // mMatrixValues will be initialized in setSize()
157 mAlpha = 1.0f;
158 }
159
160 public void drawRect(float x, float y, float width, float height, GLPaint paint) {
161 GL11 gl = mGL;
162
163 mGLState.setColorMode(paint.getColor(), mAlpha);
164 mGLState.setLineWidth(paint.getLineWidth());
Owen Lina2fba682011-08-17 22:07:43 +0800165
166 saveTransform();
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800167 translate(x, y);
Owen Lina2fba682011-08-17 22:07:43 +0800168 scale(width, height, 1);
169
170 gl.glLoadMatrixf(mMatrixValues, 0);
171 gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4);
172
173 restoreTransform();
174 mCountDrawLine++;
175 }
176
177 public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
178 GL11 gl = mGL;
179
180 mGLState.setColorMode(paint.getColor(), mAlpha);
181 mGLState.setLineWidth(paint.getLineWidth());
Owen Lina2fba682011-08-17 22:07:43 +0800182
183 saveTransform();
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800184 translate(x1, y1);
Owen Lina2fba682011-08-17 22:07:43 +0800185 scale(x2 - x1, y2 - y1, 1);
186
187 gl.glLoadMatrixf(mMatrixValues, 0);
188 gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2);
189
190 restoreTransform();
191 mCountDrawLine++;
192 }
193
194 public void fillRect(float x, float y, float width, float height, int color) {
195 mGLState.setColorMode(color, mAlpha);
196 GL11 gl = mGL;
197
198 saveTransform();
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800199 translate(x, y);
Owen Lina2fba682011-08-17 22:07:43 +0800200 scale(width, height, 1);
201
202 gl.glLoadMatrixf(mMatrixValues, 0);
203 gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
204
205 restoreTransform();
206 mCountFillRect++;
207 }
208
209 public void translate(float x, float y, float z) {
210 Matrix.translateM(mMatrixValues, 0, x, y, z);
211 }
212
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800213 // This is a faster version of translate(x, y, z) because
214 // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
215 // (3) we unroll the loop
216 public void translate(float x, float y) {
217 float[] m = mMatrixValues;
218 m[12] += m[0] * x + m[4] * y;
219 m[13] += m[1] * x + m[5] * y;
220 m[14] += m[2] * x + m[6] * y;
221 m[15] += m[3] * x + m[7] * y;
222 }
223
Owen Lina2fba682011-08-17 22:07:43 +0800224 public void scale(float sx, float sy, float sz) {
225 Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
226 }
227
228 public void rotate(float angle, float x, float y, float z) {
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +0800229 if (angle == 0) return;
Owen Lina2fba682011-08-17 22:07:43 +0800230 float[] temp = mTempMatrix;
231 Matrix.setRotateM(temp, 0, angle, x, y, z);
232 Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0);
233 System.arraycopy(temp, 16, mMatrixValues, 0, 16);
234 }
235
236 public void multiplyMatrix(float matrix[], int offset) {
237 float[] temp = mTempMatrix;
Chih-Chung Changbe074852011-10-12 17:10:33 +0800238 Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset);
Owen Lina2fba682011-08-17 22:07:43 +0800239 System.arraycopy(temp, 0, mMatrixValues, 0, 16);
240 }
241
242 private void textureRect(float x, float y, float width, float height) {
243 GL11 gl = mGL;
244
245 saveTransform();
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800246 translate(x, y);
Owen Lina2fba682011-08-17 22:07:43 +0800247 scale(width, height, 1);
248
249 gl.glLoadMatrixf(mMatrixValues, 0);
250 gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
251
252 restoreTransform();
253 mCountTextureRect++;
254 }
255
256 public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
257 int uvBuffer, int indexBuffer, int indexCount) {
258 float alpha = mAlpha;
259 if (!bindTexture(tex)) return;
260
261 mGLState.setBlendEnabled(mBlendEnabled
262 && (!tex.isOpaque() || alpha < OPAQUE_ALPHA));
263 mGLState.setTextureAlpha(alpha);
264
265 // Reset the texture matrix. We will set our own texture coordinates
266 // below.
267 setTextureCoords(0, 0, 1, 1);
268
269 saveTransform();
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800270 translate(x, y);
Owen Lina2fba682011-08-17 22:07:43 +0800271
272 mGL.glLoadMatrixf(mMatrixValues, 0);
273
274 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer);
275 mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
276
277 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer);
278 mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
279
280 mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
281 mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP,
282 indexCount, GL11.GL_UNSIGNED_BYTE, 0);
283
284 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
285 mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
286 mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
287
288 restoreTransform();
289 mCountDrawMesh++;
290 }
291
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800292 // Transforms two points by the given matrix m. The result
293 // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned.
294 private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) {
295 float[] r = mMapPointsBuffer;
Owen Lina2fba682011-08-17 22:07:43 +0800296
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800297 // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused.
298 float x3 = m[0] * x1 + m[4] * y1 + m[12];
299 float y3 = m[1] * x1 + m[5] * y1 + m[13];
300 float w3 = m[3] * x1 + m[7] * y1 + m[15];
301 r[0] = x3 / w3;
302 r[1] = y3 / w3;
Owen Lina2fba682011-08-17 22:07:43 +0800303
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800304 // Same for x2 y2.
305 float x4 = m[0] * x2 + m[4] * y2 + m[12];
306 float y4 = m[1] * x2 + m[5] * y2 + m[13];
307 float w4 = m[3] * x2 + m[7] * y2 + m[15];
308 r[2] = x4 / w4;
309 r[3] = y4 / w4;
Owen Lina2fba682011-08-17 22:07:43 +0800310
Chih-Chung Chang3f43ecb2012-02-16 07:27:03 +0800311 return r;
Owen Lina2fba682011-08-17 22:07:43 +0800312 }
313
Owen Lina2fba682011-08-17 22:07:43 +0800314 private void drawBoundTexture(
315 BasicTexture texture, int x, int y, int width, int height) {
316 // Test whether it has been rotated or flipped, if so, glDrawTexiOES
317 // won't work
318 if (isMatrixRotatedOrFlipped(mMatrixValues)) {
Chih-Chung Change3312ff2011-09-22 14:55:32 +0800319 if (texture.hasBorder()) {
320 setTextureCoords(
321 1.0f / texture.getTextureWidth(),
322 1.0f / texture.getTextureHeight(),
323 (texture.getWidth() - 1.0f) / texture.getTextureWidth(),
324 (texture.getHeight() - 1.0f) / texture.getTextureHeight());
325 } else {
326 setTextureCoords(0, 0,
327 (float) texture.getWidth() / texture.getTextureWidth(),
328 (float) texture.getHeight() / texture.getTextureHeight());
329 }
Owen Lina2fba682011-08-17 22:07:43 +0800330 textureRect(x, y, width, height);
331 } else {
332 // draw the rect from bottom-left to top-right
333 float points[] = mapPoints(
334 mMatrixValues, x, y + height, x + width, y);
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +0800335 x = (int) (points[0] + 0.5f);
336 y = (int) (points[1] + 0.5f);
337 width = (int) (points[2] + 0.5f) - x;
338 height = (int) (points[3] + 0.5f) - y;
Owen Lina2fba682011-08-17 22:07:43 +0800339 if (width > 0 && height > 0) {
340 ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
341 mCountTextureOES++;
342 }
343 }
344 }
345
346 public void drawTexture(
347 BasicTexture texture, int x, int y, int width, int height) {
348 drawTexture(texture, x, y, width, height, mAlpha);
349 }
350
Chih-Chung Chang980724b2012-02-22 08:00:31 +0800351 private void drawTexture(BasicTexture texture,
Owen Lina2fba682011-08-17 22:07:43 +0800352 int x, int y, int width, int height, float alpha) {
353 if (width <= 0 || height <= 0) return;
354
355 mGLState.setBlendEnabled(mBlendEnabled
356 && (!texture.isOpaque() || alpha < OPAQUE_ALPHA));
357 if (!bindTexture(texture)) return;
358 mGLState.setTextureAlpha(alpha);
359 drawBoundTexture(texture, x, y, width, height);
360 }
361
362 public void drawTexture(BasicTexture texture, RectF source, RectF target) {
363 if (target.width() <= 0 || target.height() <= 0) return;
364
365 // Copy the input to avoid changing it.
366 mDrawTextureSourceRect.set(source);
367 mDrawTextureTargetRect.set(target);
368 source = mDrawTextureSourceRect;
369 target = mDrawTextureTargetRect;
370
371 mGLState.setBlendEnabled(mBlendEnabled
372 && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
373 if (!bindTexture(texture)) return;
374 convertCoordinate(source, target, texture);
375 setTextureCoords(source);
376 mGLState.setTextureAlpha(mAlpha);
377 textureRect(target.left, target.top, target.width(), target.height());
378 }
379
380 // This function changes the source coordinate to the texture coordinates.
381 // It also clips the source and target coordinates if it is beyond the
382 // bound of the texture.
383 private void convertCoordinate(RectF source, RectF target,
384 BasicTexture texture) {
385
386 int width = texture.getWidth();
387 int height = texture.getHeight();
388 int texWidth = texture.getTextureWidth();
389 int texHeight = texture.getTextureHeight();
390 // Convert to texture coordinates
391 source.left /= texWidth;
392 source.right /= texWidth;
393 source.top /= texHeight;
394 source.bottom /= texHeight;
395
396 // Clip if the rendering range is beyond the bound of the texture.
397 float xBound = (float) width / texWidth;
398 if (source.right > xBound) {
399 target.right = target.left + target.width() *
400 (xBound - source.left) / source.width();
401 source.right = xBound;
402 }
403 float yBound = (float) height / texHeight;
404 if (source.bottom > yBound) {
405 target.bottom = target.top + target.height() *
406 (yBound - source.top) / source.height();
407 source.bottom = yBound;
408 }
409 }
410
411 public void drawMixed(BasicTexture from,
412 int toColor, float ratio, int x, int y, int w, int h) {
413 drawMixed(from, toColor, ratio, x, y, w, h, mAlpha);
414 }
415
Owen Lina2fba682011-08-17 22:07:43 +0800416 private boolean bindTexture(BasicTexture texture) {
417 if (!texture.onBind(this)) return false;
418 mGLState.setTexture2DEnabled(true);
419 mGL.glBindTexture(GL11.GL_TEXTURE_2D, texture.getId());
420 return true;
421 }
422
423 private void setTextureColor(float r, float g, float b, float alpha) {
424 float[] color = mTextureColor;
425 color[0] = r;
426 color[1] = g;
427 color[2] = b;
428 color[3] = alpha;
429 }
430
431 private void drawMixed(BasicTexture from, int toColor,
432 float ratio, int x, int y, int width, int height, float alpha) {
433
434 if (ratio <= 0) {
435 drawTexture(from, x, y, width, height, alpha);
436 return;
437 } else if (ratio >= 1) {
438 fillRect(x, y, width, height, toColor);
439 return;
440 }
441
442 mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
443 || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
444
445 final GL11 gl = mGL;
446 if (!bindTexture(from)) return;
447
448 //
449 // The formula we want:
450 // alpha * ((1 - ratio) * from + ratio * to)
451 // The formula that GL supports is in the form of:
452 // combo * (modulate * from) + (1 - combo) * to
453 //
454 // So, we have combo = 1 - alpha * ratio
455 // and modulate = alpha * (1f - ratio) / combo
456 //
457 float comboRatio = 1 - alpha * ratio;
458
459 // handle the case that (1 - comboRatio) == 0
460 if (alpha < OPAQUE_ALPHA) {
461 mGLState.setTextureAlpha(alpha * (1f - ratio) / comboRatio);
462 } else {
463 mGLState.setTextureAlpha(1f);
464 }
465
466 // Interpolate the RGB and alpha values between both textures.
467 mGLState.setTexEnvMode(GL11.GL_COMBINE);
468 // Specify the interpolation factor via the alpha component of
469 // GL_TEXTURE_ENV_COLORs.
470 // RGB component are get from toColor and will used as SRC1
471 float colorAlpha = (float) (toColor >>> 24) / (0xff * 0xff);
472 setTextureColor(((toColor >>> 16) & 0xff) * colorAlpha,
473 ((toColor >>> 8) & 0xff) * colorAlpha,
474 (toColor & 0xff) * colorAlpha, comboRatio);
475 gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
476
477 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
478 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
479 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT);
480 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR);
481 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT);
482 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA);
483
484 // Wire up the interpolation factor for RGB.
485 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
486 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
487
488 // Wire up the interpolation factor for alpha.
489 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
490 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
491
492 drawBoundTexture(from, x, y, width, height);
493 mGLState.setTexEnvMode(GL11.GL_REPLACE);
494 }
495
Owen Lina2fba682011-08-17 22:07:43 +0800496 // TODO: the code only work for 2D should get fixed for 3D or removed
497 private static final int MSKEW_X = 4;
498 private static final int MSKEW_Y = 1;
499 private static final int MSCALE_X = 0;
500 private static final int MSCALE_Y = 5;
501
502 private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
503 final float eps = 1e-5f;
504 return Math.abs(matrix[MSKEW_X]) > eps
505 || Math.abs(matrix[MSKEW_Y]) > eps
506 || matrix[MSCALE_X] < -eps
507 || matrix[MSCALE_Y] > eps;
508 }
509
Owen Lina2fba682011-08-17 22:07:43 +0800510 private static class GLState {
511
512 private final GL11 mGL;
513
514 private int mTexEnvMode = GL11.GL_REPLACE;
515 private float mTextureAlpha = 1.0f;
516 private boolean mTexture2DEnabled = true;
517 private boolean mBlendEnabled = true;
518 private float mLineWidth = 1.0f;
519 private boolean mLineSmooth = false;
520
521 public GLState(GL11 gl) {
522 mGL = gl;
523
524 // Disable unused state
525 gl.glDisable(GL11.GL_LIGHTING);
526
527 // Enable used features
528 gl.glEnable(GL11.GL_DITHER);
529 gl.glEnable(GL11.GL_SCISSOR_TEST);
530
531 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
532 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
533 gl.glEnable(GL11.GL_TEXTURE_2D);
534
535 gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
536 GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
537
538 // Set the background color
539 gl.glClearColor(0f, 0f, 0f, 0f);
540 gl.glClearStencil(0);
541
542 gl.glEnable(GL11.GL_BLEND);
543 gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
544
545 // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel.
546 gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
547 }
548
549 public void setTexEnvMode(int mode) {
550 if (mTexEnvMode == mode) return;
551 mTexEnvMode = mode;
552 mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode);
553 }
554
555 public void setLineWidth(float width) {
556 if (mLineWidth == width) return;
557 mLineWidth = width;
558 mGL.glLineWidth(width);
559 }
560
Owen Lina2fba682011-08-17 22:07:43 +0800561 public void setTextureAlpha(float alpha) {
562 if (mTextureAlpha == alpha) return;
563 mTextureAlpha = alpha;
564 if (alpha >= OPAQUE_ALPHA) {
565 // The alpha is need for those texture without alpha channel
566 mGL.glColor4f(1, 1, 1, 1);
567 setTexEnvMode(GL11.GL_REPLACE);
568 } else {
569 mGL.glColor4f(alpha, alpha, alpha, alpha);
570 setTexEnvMode(GL11.GL_MODULATE);
571 }
572 }
573
574 public void setColorMode(int color, float alpha) {
575 setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA);
576
577 // Set mTextureAlpha to an invalid value, so that it will reset
578 // again in setTextureAlpha(float) later.
579 mTextureAlpha = -1.0f;
580
581 setTexture2DEnabled(false);
582
583 float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f;
584 mGL.glColor4x(
585 Math.round(((color >> 16) & 0xFF) * prealpha),
586 Math.round(((color >> 8) & 0xFF) * prealpha),
587 Math.round((color & 0xFF) * prealpha),
588 Math.round(255 * prealpha));
589 }
590
591 public void setTexture2DEnabled(boolean enabled) {
592 if (mTexture2DEnabled == enabled) return;
593 mTexture2DEnabled = enabled;
594 if (enabled) {
595 mGL.glEnable(GL11.GL_TEXTURE_2D);
596 } else {
597 mGL.glDisable(GL11.GL_TEXTURE_2D);
598 }
599 }
600
601 public void setBlendEnabled(boolean enabled) {
602 if (mBlendEnabled == enabled) return;
603 mBlendEnabled = enabled;
604 if (enabled) {
605 mGL.glEnable(GL11.GL_BLEND);
606 } else {
607 mGL.glDisable(GL11.GL_BLEND);
608 }
609 }
610 }
611
612 public GL11 getGLInstance() {
613 return mGL;
614 }
615
Owen Lina2fba682011-08-17 22:07:43 +0800616 public void clearBuffer() {
617 mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
618 }
619
620 private void setTextureCoords(RectF source) {
621 setTextureCoords(source.left, source.top, source.right, source.bottom);
622 }
623
624 private void setTextureCoords(float left, float top,
625 float right, float bottom) {
626 mGL.glMatrixMode(GL11.GL_TEXTURE);
627 mTextureMatrixValues[0] = right - left;
628 mTextureMatrixValues[5] = bottom - top;
629 mTextureMatrixValues[10] = 1;
630 mTextureMatrixValues[12] = left;
631 mTextureMatrixValues[13] = top;
632 mTextureMatrixValues[15] = 1;
633 mGL.glLoadMatrixf(mTextureMatrixValues, 0);
634 mGL.glMatrixMode(GL11.GL_MODELVIEW);
635 }
636
637 // unloadTexture and deleteBuffer can be called from the finalizer thread,
638 // so we synchronized on the mUnboundTextures object.
639 public boolean unloadTexture(BasicTexture t) {
640 synchronized (mUnboundTextures) {
641 if (!t.isLoaded(this)) return false;
642 mUnboundTextures.add(t.mId);
643 return true;
644 }
645 }
646
647 public void deleteBuffer(int bufferId) {
648 synchronized (mUnboundTextures) {
649 mDeleteBuffers.add(bufferId);
650 }
651 }
652
653 public void deleteRecycledResources() {
654 synchronized (mUnboundTextures) {
655 IntArray ids = mUnboundTextures;
656 if (ids.size() > 0) {
657 mGL.glDeleteTextures(ids.size(), ids.getInternalArray(), 0);
658 ids.clear();
659 }
660
661 ids = mDeleteBuffers;
662 if (ids.size() > 0) {
663 mGL.glDeleteBuffers(ids.size(), ids.getInternalArray(), 0);
664 ids.clear();
665 }
666 }
667 }
668
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +0800669 public void save() {
670 save(SAVE_FLAG_ALL);
Owen Lina2fba682011-08-17 22:07:43 +0800671 }
672
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +0800673 public void save(int saveFlags) {
Owen Lina2fba682011-08-17 22:07:43 +0800674 ConfigState config = obtainRestoreConfig();
675
676 if ((saveFlags & SAVE_FLAG_ALPHA) != 0) {
677 config.mAlpha = mAlpha;
678 } else {
679 config.mAlpha = -1;
680 }
681
Owen Lina2fba682011-08-17 22:07:43 +0800682
683 if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
684 System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
685 } else {
686 config.mMatrix[0] = Float.NEGATIVE_INFINITY;
687 }
688
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +0800689 mRestoreStack.add(config);
Owen Lina2fba682011-08-17 22:07:43 +0800690 }
691
692 public void restore() {
693 if (mRestoreStack.isEmpty()) throw new IllegalStateException();
Chih-Chung Changbe55f1e2012-02-18 06:17:18 +0800694 ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1);
Owen Lina2fba682011-08-17 22:07:43 +0800695 config.restore(this);
696 freeRestoreConfig(config);
697 }
698
699 private void freeRestoreConfig(ConfigState action) {
700 action.mNextFree = mRecycledRestoreAction;
701 mRecycledRestoreAction = action;
702 }
703
704 private ConfigState obtainRestoreConfig() {
705 if (mRecycledRestoreAction != null) {
706 ConfigState result = mRecycledRestoreAction;
707 mRecycledRestoreAction = result.mNextFree;
708 return result;
709 }
710 return new ConfigState();
711 }
712
713 private static class ConfigState {
714 float mAlpha;
715 Rect mRect = new Rect();
716 float mMatrix[] = new float[16];
717 ConfigState mNextFree;
718
719 public void restore(GLCanvasImpl canvas) {
720 if (mAlpha >= 0) canvas.setAlpha(mAlpha);
Owen Lina2fba682011-08-17 22:07:43 +0800721 if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
722 System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
723 }
724 }
725 }
726
727 public void dumpStatisticsAndClear() {
728 String line = String.format(
729 "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d",
730 mCountDrawMesh, mCountTextureRect, mCountTextureOES,
731 mCountFillRect, mCountDrawLine);
732 mCountDrawMesh = 0;
733 mCountTextureRect = 0;
734 mCountTextureOES = 0;
735 mCountFillRect = 0;
736 mCountDrawLine = 0;
737 Log.d(TAG, line);
738 }
739
740 private void saveTransform() {
741 System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16);
742 }
743
744 private void restoreTransform() {
745 System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16);
746 }
747}