OSDN Git Service

Import translations. DO NOT MERGE
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / glrenderer / GLES11Canvas.java
1 /*
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
17 package com.android.gallery3d.glrenderer;
18
19 import android.graphics.Bitmap;
20 import android.graphics.Rect;
21 import android.graphics.RectF;
22 import android.opengl.GLU;
23 import android.opengl.GLUtils;
24 import android.opengl.Matrix;
25 import android.util.Log;
26
27 import com.android.gallery3d.common.Utils;
28 import com.android.gallery3d.util.IntArray;
29
30 import junit.framework.Assert;
31
32 import java.nio.Buffer;
33 import java.nio.ByteBuffer;
34 import java.nio.ByteOrder;
35 import java.nio.FloatBuffer;
36 import java.util.ArrayList;
37
38 import javax.microedition.khronos.opengles.GL10;
39 import javax.microedition.khronos.opengles.GL11;
40 import javax.microedition.khronos.opengles.GL11Ext;
41 import javax.microedition.khronos.opengles.GL11ExtensionPack;
42
43 public class GLES11Canvas implements GLCanvas {
44     @SuppressWarnings("unused")
45     private static final String TAG = "GLCanvasImp";
46
47     private static final float OPAQUE_ALPHA = 0.95f;
48
49     private static final int OFFSET_FILL_RECT = 0;
50     private static final int OFFSET_DRAW_LINE = 4;
51     private static final int OFFSET_DRAW_RECT = 6;
52     private static final float[] BOX_COORDINATES = {
53             0, 0, 1, 0, 0, 1, 1, 1,  // used for filling a rectangle
54             0, 0, 1, 1,              // used for drawing a line
55             0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
56
57     private GL11 mGL;
58
59     private final float mMatrixValues[] = new float[16];
60     private final float mTextureMatrixValues[] = new float[16];
61
62     // The results of mapPoints are stored in this buffer, and the order is
63     // x1, y1, x2, y2.
64     private final float mMapPointsBuffer[] = new float[4];
65
66     private final float mTextureColor[] = new float[4];
67
68     private int mBoxCoords;
69
70     private GLState mGLState;
71     private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();
72
73     private float mAlpha;
74     private final ArrayList<ConfigState> mRestoreStack = new ArrayList<ConfigState>();
75     private ConfigState mRecycledRestoreAction;
76
77     private final RectF mDrawTextureSourceRect = new RectF();
78     private final RectF mDrawTextureTargetRect = new RectF();
79     private final float[] mTempMatrix = new float[32];
80     private final IntArray mUnboundTextures = new IntArray();
81     private final IntArray mDeleteBuffers = new IntArray();
82     private int mScreenWidth;
83     private int mScreenHeight;
84     private boolean mBlendEnabled = true;
85     private int mFrameBuffer[] = new int[1];
86     private static float[] sCropRect = new float[4];
87
88     private RawTexture mTargetTexture;
89
90     // Drawing statistics
91     int mCountDrawLine;
92     int mCountFillRect;
93     int mCountDrawMesh;
94     int mCountTextureRect;
95     int mCountTextureOES;
96
97     private static GLId mGLId = new GLES11IdImpl();
98
99     public GLES11Canvas(GL11 gl) {
100         mGL = gl;
101         mGLState = new GLState(gl);
102         // First create an nio buffer, then create a VBO from it.
103         int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
104         FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
105         xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0);
106
107         int[] name = new int[1];
108         mGLId.glGenBuffers(1, name, 0);
109         mBoxCoords = name[0];
110
111         gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
112         gl.glBufferData(GL11.GL_ARRAY_BUFFER, xyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
113                 xyBuffer, GL11.GL_STATIC_DRAW);
114
115         gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
116         gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
117
118         // Enable the texture coordinate array for Texture 1
119         gl.glClientActiveTexture(GL11.GL_TEXTURE1);
120         gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
121         gl.glClientActiveTexture(GL11.GL_TEXTURE0);
122         gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
123
124         // mMatrixValues and mAlpha will be initialized in setSize()
125     }
126
127     @Override
128     public void setSize(int width, int height) {
129         Assert.assertTrue(width >= 0 && height >= 0);
130
131         if (mTargetTexture == null) {
132             mScreenWidth = width;
133             mScreenHeight = height;
134         }
135         mAlpha = 1.0f;
136
137         GL11 gl = mGL;
138         gl.glViewport(0, 0, width, height);
139         gl.glMatrixMode(GL11.GL_PROJECTION);
140         gl.glLoadIdentity();
141         GLU.gluOrtho2D(gl, 0, width, 0, height);
142
143         gl.glMatrixMode(GL11.GL_MODELVIEW);
144         gl.glLoadIdentity();
145
146         float matrix[] = mMatrixValues;
147         Matrix.setIdentityM(matrix, 0);
148         // to match the graphic coordinate system in android, we flip it vertically.
149         if (mTargetTexture == null) {
150             Matrix.translateM(matrix, 0, 0, height, 0);
151             Matrix.scaleM(matrix, 0, 1, -1, 1);
152         }
153     }
154
155     @Override
156     public void setAlpha(float alpha) {
157         Assert.assertTrue(alpha >= 0 && alpha <= 1);
158         mAlpha = alpha;
159     }
160
161     @Override
162     public float getAlpha() {
163         return mAlpha;
164     }
165
166     @Override
167     public void multiplyAlpha(float alpha) {
168         Assert.assertTrue(alpha >= 0 && alpha <= 1);
169         mAlpha *= alpha;
170     }
171
172     private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
173         return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
174     }
175
176     @Override
177     public void drawRect(float x, float y, float width, float height, GLPaint paint) {
178         GL11 gl = mGL;
179
180         mGLState.setColorMode(paint.getColor(), mAlpha);
181         mGLState.setLineWidth(paint.getLineWidth());
182
183         saveTransform();
184         translate(x, y);
185         scale(width, height, 1);
186
187         gl.glLoadMatrixf(mMatrixValues, 0);
188         gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4);
189
190         restoreTransform();
191         mCountDrawLine++;
192     }
193
194     @Override
195     public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
196         GL11 gl = mGL;
197
198         mGLState.setColorMode(paint.getColor(), mAlpha);
199         mGLState.setLineWidth(paint.getLineWidth());
200
201         saveTransform();
202         translate(x1, y1);
203         scale(x2 - x1, y2 - y1, 1);
204
205         gl.glLoadMatrixf(mMatrixValues, 0);
206         gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2);
207
208         restoreTransform();
209         mCountDrawLine++;
210     }
211
212     @Override
213     public void fillRect(float x, float y, float width, float height, int color) {
214         mGLState.setColorMode(color, mAlpha);
215         GL11 gl = mGL;
216
217         saveTransform();
218         translate(x, y);
219         scale(width, height, 1);
220
221         gl.glLoadMatrixf(mMatrixValues, 0);
222         gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
223
224         restoreTransform();
225         mCountFillRect++;
226     }
227
228     @Override
229     public void translate(float x, float y, float z) {
230         Matrix.translateM(mMatrixValues, 0, x, y, z);
231     }
232
233     // This is a faster version of translate(x, y, z) because
234     // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
235     // (3) we unroll the loop
236     @Override
237     public void translate(float x, float y) {
238         float[] m = mMatrixValues;
239         m[12] += m[0] * x + m[4] * y;
240         m[13] += m[1] * x + m[5] * y;
241         m[14] += m[2] * x + m[6] * y;
242         m[15] += m[3] * x + m[7] * y;
243     }
244
245     @Override
246     public void scale(float sx, float sy, float sz) {
247         Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
248     }
249
250     @Override
251     public void rotate(float angle, float x, float y, float z) {
252         if (angle == 0) return;
253         float[] temp = mTempMatrix;
254         Matrix.setRotateM(temp, 0, angle, x, y, z);
255         Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0);
256         System.arraycopy(temp, 16, mMatrixValues, 0, 16);
257     }
258
259     @Override
260     public void multiplyMatrix(float matrix[], int offset) {
261         float[] temp = mTempMatrix;
262         Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset);
263         System.arraycopy(temp, 0, mMatrixValues, 0, 16);
264     }
265
266     private void textureRect(float x, float y, float width, float height) {
267         GL11 gl = mGL;
268
269         saveTransform();
270         translate(x, y);
271         scale(width, height, 1);
272
273         gl.glLoadMatrixf(mMatrixValues, 0);
274         gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
275
276         restoreTransform();
277         mCountTextureRect++;
278     }
279
280     @Override
281     public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
282             int uvBuffer, int indexBuffer, int indexCount) {
283         float alpha = mAlpha;
284         if (!bindTexture(tex)) return;
285
286         mGLState.setBlendEnabled(mBlendEnabled
287                 && (!tex.isOpaque() || alpha < OPAQUE_ALPHA));
288         mGLState.setTextureAlpha(alpha);
289
290         // Reset the texture matrix. We will set our own texture coordinates
291         // below.
292         setTextureCoords(0, 0, 1, 1);
293
294         saveTransform();
295         translate(x, y);
296
297         mGL.glLoadMatrixf(mMatrixValues, 0);
298
299         mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer);
300         mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
301
302         mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer);
303         mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
304
305         mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
306         mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP,
307                 indexCount, GL11.GL_UNSIGNED_BYTE, 0);
308
309         mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
310         mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
311         mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
312
313         restoreTransform();
314         mCountDrawMesh++;
315     }
316
317     // Transforms two points by the given matrix m. The result
318     // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned.
319     private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) {
320         float[] r = mMapPointsBuffer;
321
322         // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused.
323         float x3 = m[0] * x1 + m[4] * y1 + m[12];
324         float y3 = m[1] * x1 + m[5] * y1 + m[13];
325         float w3 = m[3] * x1 + m[7] * y1 + m[15];
326         r[0] = x3 / w3;
327         r[1] = y3 / w3;
328
329         // Same for x2 y2.
330         float x4 = m[0] * x2 + m[4] * y2 + m[12];
331         float y4 = m[1] * x2 + m[5] * y2 + m[13];
332         float w4 = m[3] * x2 + m[7] * y2 + m[15];
333         r[2] = x4 / w4;
334         r[3] = y4 / w4;
335
336         return r;
337     }
338
339     private void drawBoundTexture(
340             BasicTexture texture, int x, int y, int width, int height) {
341         // Test whether it has been rotated or flipped, if so, glDrawTexiOES
342         // won't work
343         if (isMatrixRotatedOrFlipped(mMatrixValues)) {
344             if (texture.hasBorder()) {
345                 setTextureCoords(
346                         1.0f / texture.getTextureWidth(),
347                         1.0f / texture.getTextureHeight(),
348                         (texture.getWidth() - 1.0f) / texture.getTextureWidth(),
349                         (texture.getHeight() - 1.0f) / texture.getTextureHeight());
350             } else {
351                 setTextureCoords(0, 0,
352                         (float) texture.getWidth() / texture.getTextureWidth(),
353                         (float) texture.getHeight() / texture.getTextureHeight());
354             }
355             textureRect(x, y, width, height);
356         } else {
357             // draw the rect from bottom-left to top-right
358             float points[] = mapPoints(
359                     mMatrixValues, x, y + height, x + width, y);
360             x = (int) (points[0] + 0.5f);
361             y = (int) (points[1] + 0.5f);
362             width = (int) (points[2] + 0.5f) - x;
363             height = (int) (points[3] + 0.5f) - y;
364             if (width > 0 && height > 0) {
365                 ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
366                 mCountTextureOES++;
367             }
368         }
369     }
370
371     @Override
372     public void drawTexture(
373             BasicTexture texture, int x, int y, int width, int height) {
374         drawTexture(texture, x, y, width, height, mAlpha);
375     }
376
377     private void drawTexture(BasicTexture texture,
378             int x, int y, int width, int height, float alpha) {
379         if (width <= 0 || height <= 0) return;
380
381         mGLState.setBlendEnabled(mBlendEnabled
382                 && (!texture.isOpaque() || alpha < OPAQUE_ALPHA));
383         if (!bindTexture(texture)) return;
384         mGLState.setTextureAlpha(alpha);
385         drawBoundTexture(texture, x, y, width, height);
386     }
387
388     @Override
389     public void drawTexture(BasicTexture texture, RectF source, RectF target) {
390         if (target.width() <= 0 || target.height() <= 0) return;
391
392         // Copy the input to avoid changing it.
393         mDrawTextureSourceRect.set(source);
394         mDrawTextureTargetRect.set(target);
395         source = mDrawTextureSourceRect;
396         target = mDrawTextureTargetRect;
397
398         mGLState.setBlendEnabled(mBlendEnabled
399                 && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
400         if (!bindTexture(texture)) return;
401         convertCoordinate(source, target, texture);
402         setTextureCoords(source);
403         mGLState.setTextureAlpha(mAlpha);
404         textureRect(target.left, target.top, target.width(), target.height());
405     }
406
407     @Override
408     public void drawTexture(BasicTexture texture, float[] mTextureTransform,
409             int x, int y, int w, int h) {
410         mGLState.setBlendEnabled(mBlendEnabled
411                 && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
412         if (!bindTexture(texture)) return;
413         setTextureCoords(mTextureTransform);
414         mGLState.setTextureAlpha(mAlpha);
415         textureRect(x, y, w, h);
416     }
417
418     // This function changes the source coordinate to the texture coordinates.
419     // It also clips the source and target coordinates if it is beyond the
420     // bound of the texture.
421     private static void convertCoordinate(RectF source, RectF target,
422             BasicTexture texture) {
423
424         int width = texture.getWidth();
425         int height = texture.getHeight();
426         int texWidth = texture.getTextureWidth();
427         int texHeight = texture.getTextureHeight();
428         // Convert to texture coordinates
429         source.left /= texWidth;
430         source.right /= texWidth;
431         source.top /= texHeight;
432         source.bottom /= texHeight;
433
434         // Clip if the rendering range is beyond the bound of the texture.
435         float xBound = (float) width / texWidth;
436         if (source.right > xBound) {
437             target.right = target.left + target.width() *
438                     (xBound - source.left) / source.width();
439             source.right = xBound;
440         }
441         float yBound = (float) height / texHeight;
442         if (source.bottom > yBound) {
443             target.bottom = target.top + target.height() *
444                     (yBound - source.top) / source.height();
445             source.bottom = yBound;
446         }
447     }
448
449     @Override
450     public void drawMixed(BasicTexture from,
451             int toColor, float ratio, int x, int y, int w, int h) {
452         drawMixed(from, toColor, ratio, x, y, w, h, mAlpha);
453     }
454
455     private boolean bindTexture(BasicTexture texture) {
456         if (!texture.onBind(this)) return false;
457         int target = texture.getTarget();
458         mGLState.setTextureTarget(target);
459         mGL.glBindTexture(target, texture.getId());
460         return true;
461     }
462
463     private void setTextureColor(float r, float g, float b, float alpha) {
464         float[] color = mTextureColor;
465         color[0] = r;
466         color[1] = g;
467         color[2] = b;
468         color[3] = alpha;
469     }
470
471     private void setMixedColor(int toColor, float ratio, float alpha) {
472         //
473         // The formula we want:
474         //     alpha * ((1 - ratio) * from + ratio * to)
475         //
476         // The formula that GL supports is in the form of:
477         //     combo * from + (1 - combo) * to * scale
478         //
479         // So, we have combo = alpha * (1 - ratio)
480         //     and     scale = alpha * ratio / (1 - combo)
481         //
482         float combo = alpha * (1 - ratio);
483         float scale = alpha * ratio / (1 - combo);
484
485         // Specify the interpolation factor via the alpha component of
486         // GL_TEXTURE_ENV_COLORs.
487         // RGB component are get from toColor and will used as SRC1
488         float colorScale = scale * (toColor >>> 24) / (0xff * 0xff);
489         setTextureColor(((toColor >>> 16) & 0xff) * colorScale,
490                 ((toColor >>> 8) & 0xff) * colorScale,
491                 (toColor & 0xff) * colorScale, combo);
492         GL11 gl = mGL;
493         gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
494
495         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
496         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
497         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT);
498         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR);
499         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT);
500         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA);
501
502         // Wire up the interpolation factor for RGB.
503         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
504         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
505
506         // Wire up the interpolation factor for alpha.
507         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
508         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
509
510     }
511
512     @Override
513     public void drawMixed(BasicTexture from, int toColor, float ratio,
514             RectF source, RectF target) {
515         if (target.width() <= 0 || target.height() <= 0) return;
516
517         if (ratio <= 0.01f) {
518             drawTexture(from, source, target);
519             return;
520         } else if (ratio >= 1) {
521             fillRect(target.left, target.top, target.width(), target.height(), toColor);
522             return;
523         }
524
525         float alpha = mAlpha;
526
527         // Copy the input to avoid changing it.
528         mDrawTextureSourceRect.set(source);
529         mDrawTextureTargetRect.set(target);
530         source = mDrawTextureSourceRect;
531         target = mDrawTextureTargetRect;
532
533         mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
534                 || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
535
536         if (!bindTexture(from)) return;
537
538         // Interpolate the RGB and alpha values between both textures.
539         mGLState.setTexEnvMode(GL11.GL_COMBINE);
540         setMixedColor(toColor, ratio, alpha);
541         convertCoordinate(source, target, from);
542         setTextureCoords(source);
543         textureRect(target.left, target.top, target.width(), target.height());
544         mGLState.setTexEnvMode(GL11.GL_REPLACE);
545     }
546
547     private void drawMixed(BasicTexture from, int toColor,
548             float ratio, int x, int y, int width, int height, float alpha) {
549         // change from 0 to 0.01f to prevent getting divided by zero below
550         if (ratio <= 0.01f) {
551             drawTexture(from, x, y, width, height, alpha);
552             return;
553         } else if (ratio >= 1) {
554             fillRect(x, y, width, height, toColor);
555             return;
556         }
557
558         mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
559                 || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
560
561         final GL11 gl = mGL;
562         if (!bindTexture(from)) return;
563
564         // Interpolate the RGB and alpha values between both textures.
565         mGLState.setTexEnvMode(GL11.GL_COMBINE);
566         setMixedColor(toColor, ratio, alpha);
567
568         drawBoundTexture(from, x, y, width, height);
569         mGLState.setTexEnvMode(GL11.GL_REPLACE);
570     }
571
572     // TODO: the code only work for 2D should get fixed for 3D or removed
573     private static final int MSKEW_X = 4;
574     private static final int MSKEW_Y = 1;
575     private static final int MSCALE_X = 0;
576     private static final int MSCALE_Y = 5;
577
578     private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
579         final float eps = 1e-5f;
580         return Math.abs(matrix[MSKEW_X]) > eps
581                 || Math.abs(matrix[MSKEW_Y]) > eps
582                 || matrix[MSCALE_X] < -eps
583                 || matrix[MSCALE_Y] > eps;
584     }
585
586     private static class GLState {
587
588         private final GL11 mGL;
589
590         private int mTexEnvMode = GL11.GL_REPLACE;
591         private float mTextureAlpha = 1.0f;
592         private int mTextureTarget = GL11.GL_TEXTURE_2D;
593         private boolean mBlendEnabled = true;
594         private float mLineWidth = 1.0f;
595         private boolean mLineSmooth = false;
596
597         public GLState(GL11 gl) {
598             mGL = gl;
599
600             // Disable unused state
601             gl.glDisable(GL11.GL_LIGHTING);
602
603             // Enable used features
604             gl.glEnable(GL11.GL_DITHER);
605
606             gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
607             gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
608             gl.glEnable(GL11.GL_TEXTURE_2D);
609
610             gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
611                     GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
612
613             // Set the background color
614             gl.glClearColor(0f, 0f, 0f, 0f);
615
616             gl.glEnable(GL11.GL_BLEND);
617             gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
618
619             // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel.
620             gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
621         }
622
623         public void setTexEnvMode(int mode) {
624             if (mTexEnvMode == mode) return;
625             mTexEnvMode = mode;
626             mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode);
627         }
628
629         public void setLineWidth(float width) {
630             if (mLineWidth == width) return;
631             mLineWidth = width;
632             mGL.glLineWidth(width);
633         }
634
635         public void setTextureAlpha(float alpha) {
636             if (mTextureAlpha == alpha) return;
637             mTextureAlpha = alpha;
638             if (alpha >= OPAQUE_ALPHA) {
639                 // The alpha is need for those texture without alpha channel
640                 mGL.glColor4f(1, 1, 1, 1);
641                 setTexEnvMode(GL11.GL_REPLACE);
642             } else {
643                 mGL.glColor4f(alpha, alpha, alpha, alpha);
644                 setTexEnvMode(GL11.GL_MODULATE);
645             }
646         }
647
648         public void setColorMode(int color, float alpha) {
649             setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA);
650
651             // Set mTextureAlpha to an invalid value, so that it will reset
652             // again in setTextureAlpha(float) later.
653             mTextureAlpha = -1.0f;
654
655             setTextureTarget(0);
656
657             float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f;
658             mGL.glColor4x(
659                     Math.round(((color >> 16) & 0xFF) * prealpha),
660                     Math.round(((color >> 8) & 0xFF) * prealpha),
661                     Math.round((color & 0xFF) * prealpha),
662                     Math.round(255 * prealpha));
663         }
664
665         // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled.
666         public void setTextureTarget(int target) {
667             if (mTextureTarget == target) return;
668             if (mTextureTarget != 0) {
669                 mGL.glDisable(mTextureTarget);
670             }
671             mTextureTarget = target;
672             if (mTextureTarget != 0) {
673                 mGL.glEnable(mTextureTarget);
674             }
675         }
676
677         public void setBlendEnabled(boolean enabled) {
678             if (mBlendEnabled == enabled) return;
679             mBlendEnabled = enabled;
680             if (enabled) {
681                 mGL.glEnable(GL11.GL_BLEND);
682             } else {
683                 mGL.glDisable(GL11.GL_BLEND);
684             }
685         }
686     }
687
688     @Override
689     public void clearBuffer(float[] argb) {
690         if(argb != null && argb.length == 4) {
691             mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]);
692         } else {
693             mGL.glClearColor(0, 0, 0, 1);
694         }
695         mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
696     }
697
698     @Override
699     public void clearBuffer() {
700         clearBuffer(null);
701     }
702
703     private void setTextureCoords(RectF source) {
704         setTextureCoords(source.left, source.top, source.right, source.bottom);
705     }
706
707     private void setTextureCoords(float left, float top,
708             float right, float bottom) {
709         mGL.glMatrixMode(GL11.GL_TEXTURE);
710         mTextureMatrixValues[0] = right - left;
711         mTextureMatrixValues[5] = bottom - top;
712         mTextureMatrixValues[10] = 1;
713         mTextureMatrixValues[12] = left;
714         mTextureMatrixValues[13] = top;
715         mTextureMatrixValues[15] = 1;
716         mGL.glLoadMatrixf(mTextureMatrixValues, 0);
717         mGL.glMatrixMode(GL11.GL_MODELVIEW);
718     }
719
720     private void setTextureCoords(float[] mTextureTransform) {
721         mGL.glMatrixMode(GL11.GL_TEXTURE);
722         mGL.glLoadMatrixf(mTextureTransform, 0);
723         mGL.glMatrixMode(GL11.GL_MODELVIEW);
724     }
725
726     // unloadTexture and deleteBuffer can be called from the finalizer thread,
727     // so we synchronized on the mUnboundTextures object.
728     @Override
729     public boolean unloadTexture(BasicTexture t) {
730         synchronized (mUnboundTextures) {
731             if (!t.isLoaded()) return false;
732             mUnboundTextures.add(t.mId);
733             return true;
734         }
735     }
736
737     @Override
738     public void deleteBuffer(int bufferId) {
739         synchronized (mUnboundTextures) {
740             mDeleteBuffers.add(bufferId);
741         }
742     }
743
744     @Override
745     public void deleteRecycledResources() {
746         synchronized (mUnboundTextures) {
747             IntArray ids = mUnboundTextures;
748             if (ids.size() > 0) {
749                 mGLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0);
750                 ids.clear();
751             }
752
753             ids = mDeleteBuffers;
754             if (ids.size() > 0) {
755                 mGLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0);
756                 ids.clear();
757             }
758         }
759     }
760
761     @Override
762     public void save() {
763         save(SAVE_FLAG_ALL);
764     }
765
766     @Override
767     public void save(int saveFlags) {
768         ConfigState config = obtainRestoreConfig();
769
770         if ((saveFlags & SAVE_FLAG_ALPHA) != 0) {
771             config.mAlpha = mAlpha;
772         } else {
773             config.mAlpha = -1;
774         }
775
776         if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
777             System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
778         } else {
779             config.mMatrix[0] = Float.NEGATIVE_INFINITY;
780         }
781
782         mRestoreStack.add(config);
783     }
784
785     @Override
786     public void restore() {
787         if (mRestoreStack.isEmpty()) throw new IllegalStateException();
788         ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1);
789         config.restore(this);
790         freeRestoreConfig(config);
791     }
792
793     private void freeRestoreConfig(ConfigState action) {
794         action.mNextFree = mRecycledRestoreAction;
795         mRecycledRestoreAction = action;
796     }
797
798     private ConfigState obtainRestoreConfig() {
799         if (mRecycledRestoreAction != null) {
800             ConfigState result = mRecycledRestoreAction;
801             mRecycledRestoreAction = result.mNextFree;
802             return result;
803         }
804         return new ConfigState();
805     }
806
807     private static class ConfigState {
808         float mAlpha;
809         float mMatrix[] = new float[16];
810         ConfigState mNextFree;
811
812         public void restore(GLES11Canvas canvas) {
813             if (mAlpha >= 0) canvas.setAlpha(mAlpha);
814             if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
815                 System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
816             }
817         }
818     }
819
820     @Override
821     public void dumpStatisticsAndClear() {
822         String line = String.format(
823                 "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d",
824                 mCountDrawMesh, mCountTextureRect, mCountTextureOES,
825                 mCountFillRect, mCountDrawLine);
826         mCountDrawMesh = 0;
827         mCountTextureRect = 0;
828         mCountTextureOES = 0;
829         mCountFillRect = 0;
830         mCountDrawLine = 0;
831         Log.d(TAG, line);
832     }
833
834     private void saveTransform() {
835         System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16);
836     }
837
838     private void restoreTransform() {
839         System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16);
840     }
841
842     private void setRenderTarget(RawTexture texture) {
843         GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL;
844
845         if (mTargetTexture == null && texture != null) {
846             mGLId.glGenBuffers(1, mFrameBuffer, 0);
847             gl11ep.glBindFramebufferOES(
848                     GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]);
849         }
850         if (mTargetTexture != null && texture  == null) {
851             gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
852             gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0);
853         }
854
855         mTargetTexture = texture;
856         if (texture == null) {
857             setSize(mScreenWidth, mScreenHeight);
858         } else {
859             setSize(texture.getWidth(), texture.getHeight());
860
861             if (!texture.isLoaded()) texture.prepare(this);
862
863             gl11ep.glFramebufferTexture2DOES(
864                     GL11ExtensionPack.GL_FRAMEBUFFER_OES,
865                     GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES,
866                     GL11.GL_TEXTURE_2D, texture.getId(), 0);
867
868             checkFramebufferStatus(gl11ep);
869         }
870     }
871
872     @Override
873     public void endRenderTarget() {
874         RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1);
875         setRenderTarget(texture);
876         restore(); // restore matrix and alpha
877     }
878
879     @Override
880     public void beginRenderTarget(RawTexture texture) {
881         save(); // save matrix and alpha
882         mTargetStack.add(mTargetTexture);
883         setRenderTarget(texture);
884     }
885
886     private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) {
887         int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES);
888         if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) {
889             String msg = "";
890             switch (status) {
891                 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES:
892                     msg = "FRAMEBUFFER_FORMATS";
893                     break;
894                 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES:
895                     msg = "FRAMEBUFFER_ATTACHMENT";
896                     break;
897                 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES:
898                     msg = "FRAMEBUFFER_MISSING_ATTACHMENT";
899                     break;
900                 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES:
901                     msg = "FRAMEBUFFER_DRAW_BUFFER";
902                     break;
903                 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES:
904                     msg = "FRAMEBUFFER_READ_BUFFER";
905                     break;
906                 case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES:
907                     msg = "FRAMEBUFFER_UNSUPPORTED";
908                     break;
909                 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES:
910                     msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
911                     break;
912             }
913             throw new RuntimeException(msg + ":" + Integer.toHexString(status));
914         }
915     }
916
917     @Override
918     public void setTextureParameters(BasicTexture texture) {
919         int width = texture.getWidth();
920         int height = texture.getHeight();
921         // Define a vertically flipped crop rectangle for OES_draw_texture.
922         // The four values in sCropRect are: left, bottom, width, and
923         // height. Negative value of width or height means flip.
924         sCropRect[0] = 0;
925         sCropRect[1] = height;
926         sCropRect[2] = width;
927         sCropRect[3] = -height;
928
929         // Set texture parameters.
930         int target = texture.getTarget();
931         mGL.glBindTexture(target, texture.getId());
932         mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
933         mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
934         mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
935         mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
936         mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
937     }
938
939     @Override
940     public void initializeTextureSize(BasicTexture texture, int format, int type) {
941         int target = texture.getTarget();
942         mGL.glBindTexture(target, texture.getId());
943         int width = texture.getTextureWidth();
944         int height = texture.getTextureHeight();
945         mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
946     }
947
948     @Override
949     public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
950         int target = texture.getTarget();
951         mGL.glBindTexture(target, texture.getId());
952         GLUtils.texImage2D(target, 0, bitmap, 0);
953     }
954
955     @Override
956     public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
957             int format, int type) {
958         int target = texture.getTarget();
959         mGL.glBindTexture(target, texture.getId());
960         GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
961     }
962
963     @Override
964     public int uploadBuffer(FloatBuffer buf) {
965         return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
966     }
967
968     @Override
969     public int uploadBuffer(ByteBuffer buf) {
970         return uploadBuffer(buf, 1);
971     }
972
973     private int uploadBuffer(Buffer buf, int elementSize) {
974         int[] bufferIds = new int[1];
975         mGLId.glGenBuffers(bufferIds.length, bufferIds, 0);
976         int bufferId = bufferIds[0];
977         mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId);
978         mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf,
979                 GL11.GL_STATIC_DRAW);
980         return bufferId;
981     }
982
983     @Override
984     public void recoverFromLightCycle() {
985         // This is only required for GLES20
986     }
987
988     @Override
989     public void getBounds(Rect bounds, int x, int y, int width, int height) {
990         // This is only required for GLES20
991     }
992
993     @Override
994     public GLId getGLId() {
995         return mGLId;
996     }
997 }