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