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