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