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