PageRenderTime 77ms CodeModel.GetById 10ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLES20Canvas.java

https://github.com/aizuzi/platform_frameworks_base
Java | 1009 lines | 839 code | 127 blank | 43 comment | 64 complexity | 49fc9ff3be3c7d276098d9c2a4eb8b5f MD5 | raw file
   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 */
  16package com.android.gallery3d.glrenderer;
  17
  18import android.graphics.Bitmap;
  19import android.graphics.Rect;
  20import android.graphics.RectF;
  21import android.opengl.GLES20;
  22import android.opengl.GLUtils;
  23import android.opengl.Matrix;
  24import android.util.Log;
  25
  26import com.android.gallery3d.util.IntArray;
  27
  28import java.nio.Buffer;
  29import java.nio.ByteBuffer;
  30import java.nio.ByteOrder;
  31import java.nio.FloatBuffer;
  32import java.util.ArrayList;
  33import java.util.Arrays;
  34
  35public 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}