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 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 
     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
    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
    154     public void setAlpha(float alpha) {
    155         Utils.assertTrue(alpha >= 0 && alpha <= 1);
    156         mAlpha = alpha;
    157     }
    158 
    159     @Override
    160     public float getAlpha() {
    161         return mAlpha;
    162     }
    163 
    164     @Override
    165     public void multiplyAlpha(float alpha) {
    166         Utils.assertTrue(alpha >= 0 && alpha <= 1);
    167         mAlpha *= alpha;
    168     }
    169 
    170     private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
    171         return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
    172     }
    173 
    174     @Override
    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
    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
    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
    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
    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
    244     public void scale(float sx, float sy, float sz) {
    245         Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
    246     }
    247 
    248     @Override
    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
    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 
    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
    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.
    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 
    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
    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 
    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
    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
    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.
    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
    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 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    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 
    627         public void setLineWidth(float width) {
    628             if (mLineWidth == width) return;
    629             mLineWidth = width;
    630             mGL.glLineWidth(width);
    631         }
    632 
    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 
    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.
    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 
    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
    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
    697     public void clearBuffer() {
    698         clearBuffer(null);
    699     }
    700 
    701     private void setTextureCoords(RectF source) {
    702         setTextureCoords(source.left, source.top, source.right, source.bottom);
    703     }
    704 
    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 
    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
    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
    736     public void deleteBuffer(int bufferId) {
    737         synchronized (mUnboundTextures) {
    738             mDeleteBuffers.add(bufferId);
    739         }
    740     }
    741 
    742     @Override
    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
    760     public void save() {
    761         save(SAVE_FLAG_ALL);
    762     }
    763 
    764     @Override
    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
    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 
    791     private void freeRestoreConfig(ConfigState action) {
    792         action.mNextFree = mRecycledRestoreAction;
    793         mRecycledRestoreAction = action;
    794     }
    795 
    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 
    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
    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 
    832     private void saveTransform() {
    833         System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16);
    834     }
    835 
    836     private void restoreTransform() {
    837         System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16);
    838     }
    839 
    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
    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
    878     public void beginRenderTarget(RawTexture texture) {
    879         save(); // save matrix and alpha
    880         mTargetStack.add(mTargetTexture);
    881         setRenderTarget(texture);
    882     }
    883 
    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
    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
    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
    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
    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
    962     public int uploadBuffer(FloatBuffer buf) {
    963         return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
    964     }
    965 
    966     @Override
    967     public int uploadBuffer(ByteBuffer buf) {
    968         return uploadBuffer(buf, 1);
    969     }
    970 
    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
    982     public void recoverFromLightCycle() {
    983         // This is only required for GLES20
    984     }
    985 
    986     @Override
    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
    992     public GLId getGLId() {
    993         return mGLId;
    994     }
    995 }
    996