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