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