Home | History | Annotate | Download | only in glrenderer
      1 /*
      2  * Copyright (C) 2012 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 package com.android.gallery3d.glrenderer;
     17 
     18 import android.graphics.Bitmap;
     19 import android.graphics.Rect;
     20 import android.graphics.RectF;
     21 import android.opengl.GLES20;
     22 import android.opengl.GLUtils;
     23 import android.opengl.Matrix;
     24 import android.util.Log;
     25 
     26 import com.android.gallery3d.util.IntArray;
     27 
     28 import java.nio.Buffer;
     29 import java.nio.ByteBuffer;
     30 import java.nio.ByteOrder;
     31 import java.nio.FloatBuffer;
     32 import java.util.ArrayList;
     33 import java.util.Arrays;
     34 
     35 public class GLES20Canvas implements GLCanvas {
     36     // ************** Constants **********************
     37     private static final String TAG = GLES20Canvas.class.getSimpleName();
     38     private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
     39     private static final float OPAQUE_ALPHA = 0.95f;
     40 
     41     private static final int COORDS_PER_VERTEX = 2;
     42     private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
     43 
     44     private static final int COUNT_FILL_VERTEX = 4;
     45     private static final int COUNT_LINE_VERTEX = 2;
     46     private static final int COUNT_RECT_VERTEX = 4;
     47     private static final int OFFSET_FILL_RECT = 0;
     48     private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
     49     private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
     50 
     51     private static final float[] BOX_COORDINATES = {
     52             0, 0, // Fill rectangle
     53             1, 0,
     54             0, 1,
     55             1, 1,
     56             0, 0, // Draw line
     57             1, 1,
     58             0, 0, // Draw rectangle outline
     59             0, 1,
     60             1, 1,
     61             1, 0,
     62     };
     63 
     64     private static final float[] BOUNDS_COORDINATES = {
     65         0, 0, 0, 1,
     66         1, 1, 0, 1,
     67     };
     68 
     69     private static final String POSITION_ATTRIBUTE = "aPosition";
     70     private static final String COLOR_UNIFORM = "uColor";
     71     private static final String MATRIX_UNIFORM = "uMatrix";
     72     private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
     73     private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
     74     private static final String ALPHA_UNIFORM = "uAlpha";
     75     private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
     76 
     77     private static final String DRAW_VERTEX_SHADER = ""
     78             + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
     79             + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
     80             + "void main() {\n"
     81             + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
     82             + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
     83             + "}\n";
     84 
     85     private static final String DRAW_FRAGMENT_SHADER = ""
     86             + "precision mediump float;\n"
     87             + "uniform vec4 " + COLOR_UNIFORM + ";\n"
     88             + "void main() {\n"
     89             + "  gl_FragColor = " + COLOR_UNIFORM + ";\n"
     90             + "}\n";
     91 
     92     private static final String TEXTURE_VERTEX_SHADER = ""
     93             + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
     94             + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n"
     95             + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
     96             + "varying vec2 vTextureCoord;\n"
     97             + "void main() {\n"
     98             + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
     99             + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
    100             + "  vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
    101             + "}\n";
    102 
    103     private static final String MESH_VERTEX_SHADER = ""
    104             + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
    105             + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
    106             + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
    107             + "varying vec2 vTextureCoord;\n"
    108             + "void main() {\n"
    109             + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
    110             + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
    111             + "  vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
    112             + "}\n";
    113 
    114     private static final String TEXTURE_FRAGMENT_SHADER = ""
    115             + "precision mediump float;\n"
    116             + "varying vec2 vTextureCoord;\n"
    117             + "uniform float " + ALPHA_UNIFORM + ";\n"
    118             + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n"
    119             + "void main() {\n"
    120             + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
    121             + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
    122             + "}\n";
    123 
    124     private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
    125             + "#extension GL_OES_EGL_image_external : require\n"
    126             + "precision mediump float;\n"
    127             + "varying vec2 vTextureCoord;\n"
    128             + "uniform float " + ALPHA_UNIFORM + ";\n"
    129             + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
    130             + "void main() {\n"
    131             + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
    132             + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
    133             + "}\n";
    134 
    135     private static final int INITIAL_RESTORE_STATE_SIZE = 8;
    136     private static final int MATRIX_SIZE = 16;
    137 
    138     // Keep track of restore state
    139     private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
    140     private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
    141     private IntArray mSaveFlags = new IntArray();
    142 
    143     private int mCurrentAlphaIndex = 0;
    144     private int mCurrentMatrixIndex = 0;
    145 
    146     // Viewport size
    147     private int mWidth;
    148     private int mHeight;
    149 
    150     // Projection matrix
    151     private float[] mProjectionMatrix = new float[MATRIX_SIZE];
    152 
    153     // Screen size for when we aren't bound to a texture
    154     private int mScreenWidth;
    155     private int mScreenHeight;
    156 
    157     // GL programs
    158     private int mDrawProgram;
    159     private int mTextureProgram;
    160     private int mOesTextureProgram;
    161     private int mMeshProgram;
    162 
    163     // GL buffer containing BOX_COORDINATES
    164     private int mBoxCoordinates;
    165 
    166     // Handle indices -- common
    167     private static final int INDEX_POSITION = 0;
    168     private static final int INDEX_MATRIX = 1;
    169 
    170     // Handle indices -- draw
    171     private static final int INDEX_COLOR = 2;
    172 
    173     // Handle indices -- texture
    174     private static final int INDEX_TEXTURE_MATRIX = 2;
    175     private static final int INDEX_TEXTURE_SAMPLER = 3;
    176     private static final int INDEX_ALPHA = 4;
    177 
    178     // Handle indices -- mesh
    179     private static final int INDEX_TEXTURE_COORD = 2;
    180 
    181     private abstract static class ShaderParameter {
    182         public int handle;
    183         protected final String mName;
    184 
    185         public ShaderParameter(String name) {
    186             mName = name;
    187         }
    188 
    189         public abstract void loadHandle(int program);
    190     }
    191 
    192     private static class UniformShaderParameter extends ShaderParameter {
    193         public UniformShaderParameter(String name) {
    194             super(name);
    195         }
    196 
    197         @Override
    198         public void loadHandle(int program) {
    199             handle = GLES20.glGetUniformLocation(program, mName);
    200             checkError();
    201         }
    202     }
    203 
    204     private static class AttributeShaderParameter extends ShaderParameter {
    205         public AttributeShaderParameter(String name) {
    206             super(name);
    207         }
    208 
    209         @Override
    210         public void loadHandle(int program) {
    211             handle = GLES20.glGetAttribLocation(program, mName);
    212             checkError();
    213         }
    214     }
    215 
    216     ShaderParameter[] mDrawParameters = {
    217             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
    218             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
    219             new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
    220     };
    221     ShaderParameter[] mTextureParameters = {
    222             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
    223             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
    224             new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
    225             new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
    226             new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
    227     };
    228     ShaderParameter[] mOesTextureParameters = {
    229             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
    230             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
    231             new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
    232             new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
    233             new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
    234     };
    235     ShaderParameter[] mMeshParameters = {
    236             new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
    237             new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
    238             new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
    239             new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
    240             new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
    241     };
    242 
    243     private final IntArray mUnboundTextures = new IntArray();
    244     private final IntArray mDeleteBuffers = new IntArray();
    245 
    246     // Keep track of statistics for debugging
    247     private int mCountDrawMesh = 0;
    248     private int mCountTextureRect = 0;
    249     private int mCountFillRect = 0;
    250     private int mCountDrawLine = 0;
    251 
    252     // Buffer for framebuffer IDs -- we keep track so we can switch the attached
    253     // texture.
    254     private int[] mFrameBuffer = new int[1];
    255 
    256     // Bound textures.
    257     private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
    258 
    259     // Temporary variables used within calculations
    260     private final float[] mTempMatrix = new float[32];
    261     private final float[] mTempColor = new float[4];
    262     private final RectF mTempSourceRect = new RectF();
    263     private final RectF mTempTargetRect = new RectF();
    264     private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
    265     private final int[] mTempIntArray = new int[1];
    266 
    267     private static final GLId mGLId = new GLES20IdImpl();
    268 
    269     public GLES20Canvas() {
    270         Matrix.setIdentityM(mTempTextureMatrix, 0);
    271         Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
    272         mAlphas[mCurrentAlphaIndex] = 1f;
    273         mTargetTextures.add(null);
    274 
    275         FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
    276         mBoxCoordinates = uploadBuffer(boxBuffer);
    277 
    278         int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
    279         int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
    280         int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
    281         int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
    282         int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
    283         int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
    284                 OES_TEXTURE_FRAGMENT_SHADER);
    285 
    286         mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
    287         mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
    288                 mTextureParameters);
    289         mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
    290                 mOesTextureParameters);
    291         mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
    292         GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
    293         checkError();
    294     }
    295 
    296     private static FloatBuffer createBuffer(float[] values) {
    297         // First create an nio buffer, then create a VBO from it.
    298         int size = values.length * FLOAT_SIZE;
    299         FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
    300                 .asFloatBuffer();
    301         buffer.put(values, 0, values.length).position(0);
    302         return buffer;
    303     }
    304 
    305     private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
    306         int program = GLES20.glCreateProgram();
    307         checkError();
    308         if (program == 0) {
    309             throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
    310         }
    311         GLES20.glAttachShader(program, vertexShader);
    312         checkError();
    313         GLES20.glAttachShader(program, fragmentShader);
    314         checkError();
    315         GLES20.glLinkProgram(program);
    316         checkError();
    317         int[] mLinkStatus = mTempIntArray;
    318         GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
    319         if (mLinkStatus[0] != GLES20.GL_TRUE) {
    320             Log.e(TAG, "Could not link program: ");
    321             Log.e(TAG, GLES20.glGetProgramInfoLog(program));
    322             GLES20.glDeleteProgram(program);
    323             program = 0;
    324         }
    325         for (int i = 0; i < params.length; i++) {
    326             params[i].loadHandle(program);
    327         }
    328         return program;
    329     }
    330 
    331     private static int loadShader(int type, String shaderCode) {
    332         // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
    333         // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
    334         int shader = GLES20.glCreateShader(type);
    335 
    336         // add the source code to the shader and compile it
    337         GLES20.glShaderSource(shader, shaderCode);
    338         checkError();
    339         GLES20.glCompileShader(shader);
    340         checkError();
    341 
    342         return shader;
    343     }
    344 
    345     @Override
    346     public void setSize(int width, int height) {
    347         mWidth = width;
    348         mHeight = height;
    349         GLES20.glViewport(0, 0, mWidth, mHeight);
    350         checkError();
    351         Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
    352         Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
    353         if (getTargetTexture() == null) {
    354             mScreenWidth = width;
    355             mScreenHeight = height;
    356             Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
    357             Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
    358         }
    359     }
    360 
    361     @Override
    362     public void clearBuffer() {
    363         GLES20.glClearColor(0f, 0f, 0f, 1f);
    364         checkError();
    365         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    366         checkError();
    367     }
    368 
    369     @Override
    370     public void clearBuffer(float[] argb) {
    371         GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
    372         checkError();
    373         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    374         checkError();
    375     }
    376 
    377     @Override
    378     public float getAlpha() {
    379         return mAlphas[mCurrentAlphaIndex];
    380     }
    381 
    382     @Override
    383     public void setAlpha(float alpha) {
    384         mAlphas[mCurrentAlphaIndex] = alpha;
    385     }
    386 
    387     @Override
    388     public void multiplyAlpha(float alpha) {
    389         setAlpha(getAlpha() * alpha);
    390     }
    391 
    392     @Override
    393     public void translate(float x, float y, float z) {
    394         Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
    395     }
    396 
    397     // This is a faster version of translate(x, y, z) because
    398     // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
    399     // (3) we unroll the loop
    400     @Override
    401     public void translate(float x, float y) {
    402         int index = mCurrentMatrixIndex;
    403         float[] m = mMatrices;
    404         m[index + 12] += m[index + 0] * x + m[index + 4] * y;
    405         m[index + 13] += m[index + 1] * x + m[index + 5] * y;
    406         m[index + 14] += m[index + 2] * x + m[index + 6] * y;
    407         m[index + 15] += m[index + 3] * x + m[index + 7] * y;
    408     }
    409 
    410     @Override
    411     public void scale(float sx, float sy, float sz) {
    412         Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
    413     }
    414 
    415     @Override
    416     public void rotate(float angle, float x, float y, float z) {
    417         if (angle == 0f) {
    418             return;
    419         }
    420         float[] temp = mTempMatrix;
    421         Matrix.setRotateM(temp, 0, angle, x, y, z);
    422         float[] matrix = mMatrices;
    423         int index = mCurrentMatrixIndex;
    424         Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
    425         System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
    426     }
    427 
    428     @Override
    429     public void multiplyMatrix(float[] matrix, int offset) {
    430         float[] temp = mTempMatrix;
    431         float[] currentMatrix = mMatrices;
    432         int index = mCurrentMatrixIndex;
    433         Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
    434         System.arraycopy(temp, 0, currentMatrix, index, 16);
    435     }
    436 
    437     @Override
    438     public void save() {
    439         save(SAVE_FLAG_ALL);
    440     }
    441 
    442     @Override
    443     public void save(int saveFlags) {
    444         boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
    445         if (saveAlpha) {
    446             float currentAlpha = getAlpha();
    447             mCurrentAlphaIndex++;
    448             if (mAlphas.length <= mCurrentAlphaIndex) {
    449                 mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
    450             }
    451             mAlphas[mCurrentAlphaIndex] = currentAlpha;
    452         }
    453         boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
    454         if (saveMatrix) {
    455             int currentIndex = mCurrentMatrixIndex;
    456             mCurrentMatrixIndex += MATRIX_SIZE;
    457             if (mMatrices.length <= mCurrentMatrixIndex) {
    458                 mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
    459             }
    460             System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
    461         }
    462         mSaveFlags.add(saveFlags);
    463     }
    464 
    465     @Override
    466     public void restore() {
    467         int restoreFlags = mSaveFlags.removeLast();
    468         boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
    469         if (restoreAlpha) {
    470             mCurrentAlphaIndex--;
    471         }
    472         boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
    473         if (restoreMatrix) {
    474             mCurrentMatrixIndex -= MATRIX_SIZE;
    475         }
    476     }
    477 
    478     @Override
    479     public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
    480         draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
    481                 paint);
    482         mCountDrawLine++;
    483     }
    484 
    485     @Override
    486     public void drawRect(float x, float y, float width, float height, GLPaint paint) {
    487         draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
    488         mCountDrawLine++;
    489     }
    490 
    491     private void draw(int type, int offset, int count, float x, float y, float width, float height,
    492             GLPaint paint) {
    493         draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
    494     }
    495 
    496     private void draw(int type, int offset, int count, float x, float y, float width, float height,
    497             int color, float lineWidth) {
    498         prepareDraw(offset, color, lineWidth);
    499         draw(mDrawParameters, type, count, x, y, width, height);
    500     }
    501 
    502     private void prepareDraw(int offset, int color, float lineWidth) {
    503         GLES20.glUseProgram(mDrawProgram);
    504         checkError();
    505         if (lineWidth > 0) {
    506             GLES20.glLineWidth(lineWidth);
    507             checkError();
    508         }
    509         float[] colorArray = getColor(color);
    510         boolean blendingEnabled = (colorArray[3] < 1f);
    511         enableBlending(blendingEnabled);
    512         if (blendingEnabled) {
    513             GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
    514             checkError();
    515         }
    516 
    517         GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
    518         setPosition(mDrawParameters, offset);
    519         checkError();
    520     }
    521 
    522     private float[] getColor(int color) {
    523         float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
    524         float red = ((color >>> 16) & 0xFF) / 255f * alpha;
    525         float green = ((color >>> 8) & 0xFF) / 255f * alpha;
    526         float blue = (color & 0xFF) / 255f * alpha;
    527         mTempColor[0] = red;
    528         mTempColor[1] = green;
    529         mTempColor[2] = blue;
    530         mTempColor[3] = alpha;
    531         return mTempColor;
    532     }
    533 
    534     private void enableBlending(boolean enableBlending) {
    535         if (enableBlending) {
    536             GLES20.glEnable(GLES20.GL_BLEND);
    537             checkError();
    538         } else {
    539             GLES20.glDisable(GLES20.GL_BLEND);
    540             checkError();
    541         }
    542     }
    543 
    544     private void setPosition(ShaderParameter[] params, int offset) {
    545         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
    546         checkError();
    547         GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
    548                 GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
    549         checkError();
    550         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    551         checkError();
    552     }
    553 
    554     private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
    555             float height) {
    556         setMatrix(params, x, y, width, height);
    557         int positionHandle = params[INDEX_POSITION].handle;
    558         GLES20.glEnableVertexAttribArray(positionHandle);
    559         checkError();
    560         GLES20.glDrawArrays(type, 0, count);
    561         checkError();
    562         GLES20.glDisableVertexAttribArray(positionHandle);
    563         checkError();
    564     }
    565 
    566     private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
    567         Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
    568         Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
    569         Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
    570         GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
    571         checkError();
    572     }
    573 
    574     @Override
    575     public void fillRect(float x, float y, float width, float height, int color) {
    576         draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
    577                 color, 0f);
    578         mCountFillRect++;
    579     }
    580 
    581     @Override
    582     public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
    583         if (width <= 0 || height <= 0) {
    584             return;
    585         }
    586         copyTextureCoordinates(texture, mTempSourceRect);
    587         mTempTargetRect.set(x, y, x + width, y + height);
    588         convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
    589         drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
    590     }
    591 
    592     private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
    593         int left = 0;
    594         int top = 0;
    595         int right = texture.getWidth();
    596         int bottom = texture.getHeight();
    597         if (texture.hasBorder()) {
    598             left = 1;
    599             top = 1;
    600             right -= 1;
    601             bottom -= 1;
    602         }
    603         outRect.set(left, top, right, bottom);
    604     }
    605 
    606     @Override
    607     public void drawTexture(BasicTexture texture, RectF source, RectF target) {
    608         if (target.width() <= 0 || target.height() <= 0) {
    609             return;
    610         }
    611         mTempSourceRect.set(source);
    612         mTempTargetRect.set(target);
    613 
    614         convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
    615         drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
    616     }
    617 
    618     @Override
    619     public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
    620             int h) {
    621         if (w <= 0 || h <= 0) {
    622             return;
    623         }
    624         mTempTargetRect.set(x, y, x + w, y + h);
    625         drawTextureRect(texture, textureTransform, mTempTargetRect);
    626     }
    627 
    628     private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
    629         setTextureMatrix(source);
    630         drawTextureRect(texture, mTempTextureMatrix, target);
    631     }
    632 
    633     private void setTextureMatrix(RectF source) {
    634         mTempTextureMatrix[0] = source.width();
    635         mTempTextureMatrix[5] = source.height();
    636         mTempTextureMatrix[12] = source.left;
    637         mTempTextureMatrix[13] = source.top;
    638     }
    639 
    640     // This function changes the source coordinate to the texture coordinates.
    641     // It also clips the source and target coordinates if it is beyond the
    642     // bound of the texture.
    643     private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
    644         int width = texture.getWidth();
    645         int height = texture.getHeight();
    646         int texWidth = texture.getTextureWidth();
    647         int texHeight = texture.getTextureHeight();
    648         // Convert to texture coordinates
    649         source.left /= texWidth;
    650         source.right /= texWidth;
    651         source.top /= texHeight;
    652         source.bottom /= texHeight;
    653 
    654         // Clip if the rendering range is beyond the bound of the texture.
    655         float xBound = (float) width / texWidth;
    656         if (source.right > xBound) {
    657             target.right = target.left + target.width() * (xBound - source.left) / source.width();
    658             source.right = xBound;
    659         }
    660         float yBound = (float) height / texHeight;
    661         if (source.bottom > yBound) {
    662             target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
    663             source.bottom = yBound;
    664         }
    665     }
    666 
    667     private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
    668         ShaderParameter[] params = prepareTexture(texture);
    669         setPosition(params, OFFSET_FILL_RECT);
    670         GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
    671         checkError();
    672         if (texture.isFlippedVertically()) {
    673             save(SAVE_FLAG_MATRIX);
    674             translate(0, target.centerY());
    675             scale(1, -1, 1);
    676             translate(0, -target.centerY());
    677         }
    678         draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
    679                 target.width(), target.height());
    680         if (texture.isFlippedVertically()) {
    681             restore();
    682         }
    683         mCountTextureRect++;
    684     }
    685 
    686     private ShaderParameter[] prepareTexture(BasicTexture texture) {
    687         ShaderParameter[] params;
    688         int program;
    689         if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
    690             params = mTextureParameters;
    691             program = mTextureProgram;
    692         } else {
    693             params = mOesTextureParameters;
    694             program = mOesTextureProgram;
    695         }
    696         prepareTexture(texture, program, params);
    697         return params;
    698     }
    699 
    700     private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
    701         GLES20.glUseProgram(program);
    702         checkError();
    703         enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
    704         GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    705         checkError();
    706         texture.onBind(this);
    707         GLES20.glBindTexture(texture.getTarget(), texture.getId());
    708         checkError();
    709         GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
    710         checkError();
    711         GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
    712         checkError();
    713     }
    714 
    715     @Override
    716     public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
    717             int indexBuffer, int indexCount) {
    718         prepareTexture(texture, mMeshProgram, mMeshParameters);
    719 
    720         GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    721         checkError();
    722 
    723         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
    724         checkError();
    725         int positionHandle = mMeshParameters[INDEX_POSITION].handle;
    726         GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
    727                 VERTEX_STRIDE, 0);
    728         checkError();
    729 
    730         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
    731         checkError();
    732         int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
    733         GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
    734                 false, VERTEX_STRIDE, 0);
    735         checkError();
    736         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    737         checkError();
    738 
    739         GLES20.glEnableVertexAttribArray(positionHandle);
    740         checkError();
    741         GLES20.glEnableVertexAttribArray(texCoordHandle);
    742         checkError();
    743 
    744         setMatrix(mMeshParameters, x, y, 1, 1);
    745         GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
    746         checkError();
    747 
    748         GLES20.glDisableVertexAttribArray(positionHandle);
    749         checkError();
    750         GLES20.glDisableVertexAttribArray(texCoordHandle);
    751         checkError();
    752         GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
    753         checkError();
    754         mCountDrawMesh++;
    755     }
    756 
    757     @Override
    758     public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
    759         copyTextureCoordinates(texture, mTempSourceRect);
    760         mTempTargetRect.set(x, y, x + w, y + h);
    761         drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
    762     }
    763 
    764     @Override
    765     public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
    766         if (target.width() <= 0 || target.height() <= 0) {
    767             return;
    768         }
    769         save(SAVE_FLAG_ALPHA);
    770 
    771         float currentAlpha = getAlpha();
    772         float cappedRatio = Math.min(1f, Math.max(0f, ratio));
    773 
    774         float textureAlpha = (1f - cappedRatio) * currentAlpha;
    775         setAlpha(textureAlpha);
    776         drawTexture(texture, source, target);
    777 
    778         float colorAlpha = cappedRatio * currentAlpha;
    779         setAlpha(colorAlpha);
    780         fillRect(target.left, target.top, target.width(), target.height(), toColor);
    781 
    782         restore();
    783     }
    784 
    785     @Override
    786     public boolean unloadTexture(BasicTexture texture) {
    787         boolean unload = texture.isLoaded();
    788         if (unload) {
    789             synchronized (mUnboundTextures) {
    790                 mUnboundTextures.add(texture.getId());
    791             }
    792         }
    793         return unload;
    794     }
    795 
    796     @Override
    797     public void deleteBuffer(int bufferId) {
    798         synchronized (mUnboundTextures) {
    799             mDeleteBuffers.add(bufferId);
    800         }
    801     }
    802 
    803     @Override
    804     public void deleteRecycledResources() {
    805         synchronized (mUnboundTextures) {
    806             IntArray ids = mUnboundTextures;
    807             if (mUnboundTextures.size() > 0) {
    808                 mGLId.glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
    809                 ids.clear();
    810             }
    811 
    812             ids = mDeleteBuffers;
    813             if (ids.size() > 0) {
    814                 mGLId.glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
    815                 ids.clear();
    816             }
    817         }
    818     }
    819 
    820     @Override
    821     public void dumpStatisticsAndClear() {
    822         String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
    823                 mCountTextureRect, mCountFillRect, mCountDrawLine);
    824         mCountDrawMesh = 0;
    825         mCountTextureRect = 0;
    826         mCountFillRect = 0;
    827         mCountDrawLine = 0;
    828         Log.d(TAG, line);
    829     }
    830 
    831     @Override
    832     public void endRenderTarget() {
    833         RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
    834         RawTexture texture = getTargetTexture();
    835         setRenderTarget(oldTexture, texture);
    836         restore(); // restore matrix and alpha
    837     }
    838 
    839     @Override
    840     public void beginRenderTarget(RawTexture texture) {
    841         save(); // save matrix and alpha and blending
    842         RawTexture oldTexture = getTargetTexture();
    843         mTargetTextures.add(texture);
    844         setRenderTarget(oldTexture, texture);
    845     }
    846 
    847     private RawTexture getTargetTexture() {
    848         return mTargetTextures.get(mTargetTextures.size() - 1);
    849     }
    850 
    851     private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
    852         if (oldTexture == null && texture != null) {
    853             GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
    854             checkError();
    855             GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
    856             checkError();
    857         } else if (oldTexture != null && texture == null) {
    858             GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    859             checkError();
    860             GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
    861             checkError();
    862         }
    863 
    864         if (texture == null) {
    865             setSize(mScreenWidth, mScreenHeight);
    866         } else {
    867             setSize(texture.getWidth(), texture.getHeight());
    868 
    869             if (!texture.isLoaded()) {
    870                 texture.prepare(this);
    871             }
    872 
    873             GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
    874                     texture.getTarget(), texture.getId(), 0);
    875             checkError();
    876 
    877             checkFramebufferStatus();
    878         }
    879     }
    880 
    881     private static void checkFramebufferStatus() {
    882         int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
    883         if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
    884             String msg = "";
    885             switch (status) {
    886                 case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
    887                     msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
    888                     break;
    889                 case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
    890                     msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
    891                     break;
    892                 case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
    893                     msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
    894                     break;
    895                 case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
    896                     msg = "GL_FRAMEBUFFER_UNSUPPORTED";
    897                     break;
    898             }
    899             throw new RuntimeException(msg + ":" + Integer.toHexString(status));
    900         }
    901     }
    902 
    903     @Override
    904     public void setTextureParameters(BasicTexture texture) {
    905         int target = texture.getTarget();
    906         GLES20.glBindTexture(target, texture.getId());
    907         checkError();
    908         GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    909         GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    910         GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
    911         GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    912     }
    913 
    914     @Override
    915     public void initializeTextureSize(BasicTexture texture, int format, int type) {
    916         int target = texture.getTarget();
    917         GLES20.glBindTexture(target, texture.getId());
    918         checkError();
    919         int width = texture.getTextureWidth();
    920         int height = texture.getTextureHeight();
    921         GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
    922     }
    923 
    924     @Override
    925     public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
    926         int target = texture.getTarget();
    927         GLES20.glBindTexture(target, texture.getId());
    928         checkError();
    929         GLUtils.texImage2D(target, 0, bitmap, 0);
    930     }
    931 
    932     @Override
    933     public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
    934             int format, int type) {
    935         int target = texture.getTarget();
    936         GLES20.glBindTexture(target, texture.getId());
    937         checkError();
    938         GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
    939     }
    940 
    941     @Override
    942     public int uploadBuffer(FloatBuffer buf) {
    943         return uploadBuffer(buf, FLOAT_SIZE);
    944     }
    945 
    946     @Override
    947     public int uploadBuffer(ByteBuffer buf) {
    948         return uploadBuffer(buf, 1);
    949     }
    950 
    951     private int uploadBuffer(Buffer buffer, int elementSize) {
    952         mGLId.glGenBuffers(1, mTempIntArray, 0);
    953         checkError();
    954         int bufferId = mTempIntArray[0];
    955         GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
    956         checkError();
    957         GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
    958                 GLES20.GL_STATIC_DRAW);
    959         checkError();
    960         return bufferId;
    961     }
    962 
    963     public static void checkError() {
    964         int error = GLES20.glGetError();
    965         if (error != 0) {
    966             Throwable t = new Throwable();
    967             Log.e(TAG, "GL error: " + error, t);
    968         }
    969     }
    970 
    971     @SuppressWarnings("unused")
    972     private static void printMatrix(String message, float[] m, int offset) {
    973         StringBuilder b = new StringBuilder(message);
    974         for (int i = 0; i < MATRIX_SIZE; i++) {
    975             b.append(' ');
    976             if (i % 4 == 0) {
    977                 b.append('\n');
    978             }
    979             b.append(m[offset + i]);
    980         }
    981         Log.v(TAG, b.toString());
    982     }
    983 
    984     @Override
    985     public void recoverFromLightCycle() {
    986         GLES20.glViewport(0, 0, mWidth, mHeight);
    987         GLES20.glDisable(GLES20.GL_DEPTH_TEST);
    988         GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
    989         checkError();
    990     }
    991 
    992     @Override
    993     public void getBounds(Rect bounds, int x, int y, int width, int height) {
    994         Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
    995         Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
    996         Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE, mTempMatrix, 0, BOUNDS_COORDINATES, 0);
    997         Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE + 4, mTempMatrix, 0, BOUNDS_COORDINATES, 4);
    998         bounds.left = Math.round(mTempMatrix[MATRIX_SIZE]);
    999         bounds.right = Math.round(mTempMatrix[MATRIX_SIZE + 4]);
   1000         bounds.top = Math.round(mTempMatrix[MATRIX_SIZE + 1]);
   1001         bounds.bottom = Math.round(mTempMatrix[MATRIX_SIZE + 5]);
   1002         bounds.sort();
   1003     }
   1004 
   1005     @Override
   1006     public GLId getGLId() {
   1007         return mGLId;
   1008     }
   1009 }
   1010