Home | History | Annotate | Download | only in glrenderer
      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 
     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
    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
    156     public void setAlpha(float alpha) {
    157         Assert.assertTrue(alpha >= 0 && alpha <= 1);
    158         mAlpha = alpha;
    159     }
    160 
    161     @Override
    162     public float getAlpha() {
    163         return mAlpha;
    164     }
    165 
    166     @Override
    167     public void multiplyAlpha(float alpha) {
    168         Assert.assertTrue(alpha >= 0 && alpha <= 1);
    169         mAlpha *= alpha;
    170     }
    171 
    172     private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
    173         return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
    174     }
    175 
    176     @Override
    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
    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
    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
    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
    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
    246     public void scale(float sx, float sy, float sz) {
    247         Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
    248     }
    249 
    250     @Override
    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
    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 
    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
    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.
    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 
    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
    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 
    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
    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
    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.
    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
    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 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    629         public void setLineWidth(float width) {
    630             if (mLineWidth == width) return;
    631             mLineWidth = width;
    632             mGL.glLineWidth(width);
    633         }
    634 
    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 
    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.
    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 
    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
    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
    699     public void clearBuffer() {
    700         clearBuffer(null);
    701     }
    702 
    703     private void setTextureCoords(RectF source) {
    704         setTextureCoords(source.left, source.top, source.right, source.bottom);
    705     }
    706 
    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 
    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
    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
    738     public void deleteBuffer(int bufferId) {
    739         synchronized (mUnboundTextures) {
    740             mDeleteBuffers.add(bufferId);
    741         }
    742     }
    743 
    744     @Override
    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
    762     public void save() {
    763         save(SAVE_FLAG_ALL);
    764     }
    765 
    766     @Override
    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
    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 
    793     private void freeRestoreConfig(ConfigState action) {
    794         action.mNextFree = mRecycledRestoreAction;
    795         mRecycledRestoreAction = action;
    796     }
    797 
    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 
    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
    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 
    834     private void saveTransform() {
    835         System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16);
    836     }
    837 
    838     private void restoreTransform() {
    839         System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16);
    840     }
    841 
    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
    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
    880     public void beginRenderTarget(RawTexture texture) {
    881         save(); // save matrix and alpha
    882         mTargetStack.add(mTargetTexture);
    883         setRenderTarget(texture);
    884     }
    885 
    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
    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
    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
    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
    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
    964     public int uploadBuffer(FloatBuffer buf) {
    965         return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
    966     }
    967 
    968     @Override
    969     public int uploadBuffer(ByteBuffer buf) {
    970         return uploadBuffer(buf, 1);
    971     }
    972 
    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
    984     public void recoverFromLightCycle() {
    985         // This is only required for GLES20
    986     }
    987 
    988     @Override
    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
    994     public GLId getGLId() {
    995         return mGLId;
    996     }
    997 }
    998