GLES20Canvas.java revision 6eb33768a15e2b4cc647bc55474568cf710876db
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.ui; 17 18import android.graphics.Bitmap; 19import android.graphics.RectF; 20import android.opengl.GLES20; 21import android.opengl.GLUtils; 22import android.opengl.Matrix; 23import android.util.Log; 24 25import com.android.gallery3d.common.Utils; 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 35import javax.microedition.khronos.opengles.GL11; 36import javax.microedition.khronos.opengles.GL11ExtensionPack; 37 38public class GLES20Canvas extends GLCanvas implements GLId { 39 // ************** Constants ********************** 40 private static final String TAG = GLES20Canvas.class.getSimpleName(); 41 private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE; 42 private static final float OPAQUE_ALPHA = 0.95f; 43 44 private static final int COORDS_PER_VERTEX = 2; 45 private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE; 46 47 private static final int COUNT_FILL_VERTEX = 4; 48 private static final int COUNT_LINE_VERTEX = 2; 49 private static final int COUNT_RECT_VERTEX = 4; 50 private static final int OFFSET_FILL_RECT = 0; 51 private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX; 52 private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX; 53 54 private static final float[] BOX_COORDINATES = { 55 0, 0, // Fill rectangle 56 1, 0, 57 0, 1, 58 1, 1, 59 0, 0, // Draw line 60 1, 1, 61 0, 0, // Draw rectangle outline 62 0, 1, 63 1, 1, 64 1, 0, 65 }; 66 67 private static final String POSITION_ATTRIBUTE = "aPosition"; 68 private static final String COLOR_UNIFORM = "uColor"; 69 private static final String MATRIX_UNIFORM = "uMatrix"; 70 private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix"; 71 private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler"; 72 private static final String ALPHA_UNIFORM = "uAlpha"; 73 private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate"; 74 75 private static final String DRAW_VERTEX_SHADER = "" 76 + "uniform mat4 " + MATRIX_UNIFORM + ";\n" 77 + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" 78 + "void main() {\n" 79 + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" 80 + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" 81 + "}\n"; 82 83 private static final String DRAW_FRAGMENT_SHADER = "" 84 + "precision mediump float;\n" 85 + "uniform vec4 " + COLOR_UNIFORM + ";\n" 86 + "void main() {\n" 87 + " gl_FragColor = " + COLOR_UNIFORM + ";\n" 88 + "}\n"; 89 90 private static final String TEXTURE_VERTEX_SHADER = "" 91 + "uniform mat4 " + MATRIX_UNIFORM + ";\n" 92 + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n" 93 + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" 94 + "varying vec2 vTextureCoord;\n" 95 + "void main() {\n" 96 + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" 97 + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" 98 + " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n" 99 + "}\n"; 100 101 private static final String MESH_VERTEX_SHADER = "" 102 + "uniform mat4 " + MATRIX_UNIFORM + ";\n" 103 + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" 104 + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n" 105 + "varying vec2 vTextureCoord;\n" 106 + "void main() {\n" 107 + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" 108 + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" 109 + " vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n" 110 + "}\n"; 111 112 private static final String TEXTURE_FRAGMENT_SHADER = "" 113 + "precision mediump float;\n" 114 + "varying vec2 vTextureCoord;\n" 115 + "uniform float " + ALPHA_UNIFORM + ";\n" 116 + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n" 117 + "void main() {\n" 118 + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" 119 + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n" 120 + "}\n"; 121 122 private static final String OES_TEXTURE_FRAGMENT_SHADER = "" 123 + "#extension GL_OES_EGL_image_external : require\n" 124 + "precision mediump float;\n" 125 + "varying vec2 vTextureCoord;\n" 126 + "uniform float " + ALPHA_UNIFORM + ";\n" 127 + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n" 128 + "void main() {\n" 129 + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" 130 + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n" 131 + "}\n"; 132 133 private static final int INITIAL_RESTORE_STATE_SIZE = 8; 134 private static final int MATRIX_SIZE = 16; 135 136 // Keep track of restore state 137 private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE]; 138 private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE]; 139 private IntArray mSaveFlags = new IntArray(); 140 private ArrayList<Blending> mBlendings = new ArrayList<Blending>(); 141 142 private int mCurrentAlphaIndex = 0; 143 private int mCurrentMatrixIndex = 0; 144 145 // Viewport size 146 private int mWidth; 147 private int mHeight; 148 149 // Projection matrix 150 private float[] mProjectionMatrix = new float[MATRIX_SIZE]; 151 152 // Screen size for when we aren't bound to a texture 153 private int mScreenWidth; 154 private int mScreenHeight; 155 156 // GL programs 157 private int mDrawProgram; 158 private int mTextureProgram; 159 private int mOesTextureProgram; 160 private int mMeshProgram; 161 162 // GL buffer containing BOX_COORDINATES 163 private int mBoxCoordinates; 164 165 // Handle indices -- common 166 private static final int INDEX_POSITION = 0; 167 private static final int INDEX_MATRIX = 1; 168 169 // Handle indices -- draw 170 private static final int INDEX_COLOR = 2; 171 172 // Handle indices -- texture 173 private static final int INDEX_TEXTURE_MATRIX = 2; 174 private static final int INDEX_TEXTURE_SAMPLER = 3; 175 private static final int INDEX_ALPHA = 4; 176 177 // Handle indices -- mesh 178 private static final int INDEX_TEXTURE_COORD = 2; 179 180 private abstract static class ShaderParameter { 181 public int handle; 182 protected final String mName; 183 184 public ShaderParameter(String name) { 185 mName = name; 186 } 187 188 public abstract void loadHandle(int program); 189 } 190 191 private static class UniformShaderParameter extends ShaderParameter { 192 public UniformShaderParameter(String name) { 193 super(name); 194 } 195 196 @Override 197 public void loadHandle(int program) { 198 handle = GLES20.glGetUniformLocation(program, mName); 199 checkError(); 200 } 201 } 202 203 private static class AttributeShaderParameter extends ShaderParameter { 204 public AttributeShaderParameter(String name) { 205 super(name); 206 } 207 208 @Override 209 public void loadHandle(int program) { 210 handle = GLES20.glGetAttribLocation(program, mName); 211 checkError(); 212 } 213 } 214 215 ShaderParameter[] mDrawParameters = { 216 new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION 217 new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX 218 new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR 219 }; 220 ShaderParameter[] mTextureParameters = { 221 new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION 222 new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX 223 new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX 224 new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER 225 new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA 226 }; 227 ShaderParameter[] mOesTextureParameters = { 228 new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION 229 new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX 230 new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX 231 new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER 232 new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA 233 }; 234 ShaderParameter[] mMeshParameters = { 235 new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION 236 new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX 237 new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD 238 new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER 239 new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA 240 }; 241 242 private final IntArray mUnboundTextures = new IntArray(); 243 private final IntArray mDeleteBuffers = new IntArray(); 244 245 // Keep track of statistics for debugging 246 private int mCountDrawMesh = 0; 247 private int mCountTextureRect = 0; 248 private int mCountFillRect = 0; 249 private int mCountDrawLine = 0; 250 251 private int mNextTextureId = 1; 252 253 // Buffer for framebuffer IDs -- we keep track so we can switch the attached 254 // texture. 255 private int[] mFrameBuffer = new int[1]; 256 257 // Bound textures. 258 private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>(); 259 260 // Temporary variables used within calculations 261 private final float[] mTempMatrix = new float[32]; 262 private final float[] mTempColor = new float[4]; 263 private final RectF mTempSourceRect = new RectF(); 264 private final RectF mTempTargetRect = new RectF(); 265 private final float[] mTempTextureMatrix = new float[MATRIX_SIZE]; 266 private final int[] mTempIntArray = new int[1]; 267 268 public GLES20Canvas() { 269 Matrix.setIdentityM(mTempTextureMatrix, 0); 270 Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); 271 mAlphas[mCurrentAlphaIndex] = 1f; 272 mTargetTextures.add(null); 273 } 274 275 @Override 276 public void initialize(GL11 gl) { 277 FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES); 278 mBoxCoordinates = uploadBuffer(boxBuffer); 279 280 int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER); 281 int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER); 282 int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER); 283 int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER); 284 int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER); 285 int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, 286 OES_TEXTURE_FRAGMENT_SHADER); 287 288 mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters); 289 mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader, 290 mTextureParameters); 291 mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader, 292 mOesTextureParameters); 293 mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters); 294 295 mBlendings.clear(); 296 mBlendings.add(null); 297 setBlending(Blending.Mix); 298 } 299 300 private static FloatBuffer createBuffer(float[] values) { 301 // First create an nio buffer, then create a VBO from it. 302 int size = values.length * FLOAT_SIZE; 303 FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()) 304 .asFloatBuffer(); 305 buffer.put(values, 0, values.length).position(0); 306 return buffer; 307 } 308 309 private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) { 310 int program = GLES20.glCreateProgram(); 311 checkError(); 312 if (program == 0) { 313 throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError()); 314 } 315 GLES20.glAttachShader(program, vertexShader); 316 checkError(); 317 GLES20.glAttachShader(program, fragmentShader); 318 checkError(); 319 GLES20.glLinkProgram(program); 320 checkError(); 321 int[] mLinkStatus = mTempIntArray; 322 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0); 323 if (mLinkStatus[0] != GLES20.GL_TRUE) { 324 Log.e(TAG, "Could not link program: "); 325 Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 326 GLES20.glDeleteProgram(program); 327 program = 0; 328 } 329 for (int i = 0; i < params.length; i++) { 330 params[i].loadHandle(program); 331 } 332 return program; 333 } 334 335 private static int loadShader(int type, String shaderCode) { 336 // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 337 // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 338 int shader = GLES20.glCreateShader(type); 339 340 // add the source code to the shader and compile it 341 GLES20.glShaderSource(shader, shaderCode); 342 checkError(); 343 GLES20.glCompileShader(shader); 344 checkError(); 345 346 return shader; 347 } 348 349 @Override 350 public void setSize(int width, int height) { 351 mWidth = width; 352 mHeight = height; 353 GLES20.glViewport(0, 0, mWidth, mHeight); 354 checkError(); 355 Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); 356 Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1); 357 if (getTargetTexture() == null) { 358 mScreenWidth = width; 359 mScreenHeight = height; 360 Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0); 361 Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1); 362 } 363 } 364 365 @Override 366 public void clearBuffer() { 367 GLES20.glClearColor(0f, 0f, 0f, 1f); 368 checkError(); 369 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 370 checkError(); 371 } 372 373 @Override 374 public void clearBuffer(float[] argb) { 375 GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]); 376 checkError(); 377 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 378 checkError(); 379 } 380 381 @Override 382 public float getAlpha() { 383 return mAlphas[mCurrentAlphaIndex]; 384 } 385 386 @Override 387 public void setAlpha(float alpha) { 388 mAlphas[mCurrentAlphaIndex] = alpha; 389 } 390 391 @Override 392 public void multiplyAlpha(float alpha) { 393 setAlpha(getAlpha() * alpha); 394 } 395 396 @Override 397 public void translate(float x, float y, float z) { 398 Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z); 399 } 400 401 // This is a faster version of translate(x, y, z) because 402 // (1) we knows z = 0, (2) we inline the Matrix.translateM call, 403 // (3) we unroll the loop 404 @Override 405 public void translate(float x, float y) { 406 int index = mCurrentMatrixIndex; 407 float[] m = mMatrices; 408 m[index + 12] += m[index + 0] * x + m[index + 4] * y; 409 m[index + 13] += m[index + 1] * x + m[index + 5] * y; 410 m[index + 14] += m[index + 2] * x + m[index + 6] * y; 411 m[index + 15] += m[index + 3] * x + m[index + 7] * y; 412 } 413 414 @Override 415 public void scale(float sx, float sy, float sz) { 416 Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz); 417 } 418 419 @Override 420 public void rotate(float angle, float x, float y, float z) { 421 if (angle == 0f) { 422 return; 423 } 424 float[] temp = mTempMatrix; 425 Matrix.setRotateM(temp, 0, angle, x, y, z); 426 float[] matrix = mMatrices; 427 int index = mCurrentMatrixIndex; 428 Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0); 429 System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE); 430 } 431 432 @Override 433 public void multiplyMatrix(float[] matrix, int offset) { 434 float[] temp = mTempMatrix; 435 float[] currentMatrix = mMatrices; 436 int index = mCurrentMatrixIndex; 437 Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset); 438 System.arraycopy(temp, 0, currentMatrix, index, 16); 439 } 440 441 @Override 442 public void save() { 443 save(SAVE_FLAG_ALL); 444 } 445 446 @Override 447 public void save(int saveFlags) { 448 boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; 449 if (saveAlpha) { 450 float currentAlpha = getAlpha(); 451 mCurrentAlphaIndex++; 452 if (mAlphas.length <= mCurrentAlphaIndex) { 453 mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2); 454 } 455 mAlphas[mCurrentAlphaIndex] = currentAlpha; 456 } 457 boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; 458 if (saveMatrix) { 459 int currentIndex = mCurrentMatrixIndex; 460 mCurrentMatrixIndex += MATRIX_SIZE; 461 if (mMatrices.length <= mCurrentMatrixIndex) { 462 mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2); 463 } 464 System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE); 465 } 466 boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; 467 if (saveBlending) { 468 mBlendings.add(mBlendings.get(mBlendings.size() - 1)); 469 } 470 mSaveFlags.add(saveFlags); 471 } 472 473 @Override 474 public void restore() { 475 int restoreFlags = mSaveFlags.removeLast(); 476 boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; 477 if (restoreAlpha) { 478 mCurrentAlphaIndex--; 479 } 480 boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; 481 if (restoreMatrix) { 482 mCurrentMatrixIndex -= MATRIX_SIZE; 483 } 484 boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; 485 if (restoreBlending) { 486 setBlending(mBlendings.get(mBlendings.size() - 2)); 487 mBlendings.remove(mBlendings.size() - 1); 488 } 489 } 490 491 @Override 492 public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { 493 draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1, 494 paint); 495 mCountDrawLine++; 496 } 497 498 @Override 499 public void drawRect(float x, float y, float width, float height, GLPaint paint) { 500 draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint); 501 mCountDrawLine++; 502 } 503 504 private void draw(int type, int offset, int count, float x, float y, float width, float height, 505 GLPaint paint) { 506 draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth()); 507 } 508 509 private void draw(int type, int offset, int count, float x, float y, float width, float height, 510 int color, float lineWidth) { 511 prepareDraw(offset, color, lineWidth); 512 draw(mDrawParameters, type, count, x, y, width, height); 513 } 514 515 private void prepareDraw(int offset, int color, float lineWidth) { 516 GLES20.glUseProgram(mDrawProgram); 517 checkError(); 518 if (lineWidth > 0) { 519 GLES20.glLineWidth(lineWidth); 520 checkError(); 521 } 522 float[] colorArray = getColor(color); 523 boolean blendingEnabled = (colorArray[3] < 1f); 524 enableBlending(blendingEnabled); 525 if (blendingEnabled) { 526 GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]); 527 checkError(); 528 } 529 530 GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0); 531 setPosition(mDrawParameters, offset); 532 checkError(); 533 } 534 535 private float[] getColor(int color) { 536 float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha(); 537 float red = ((color >>> 16) & 0xFF) / 255f * alpha; 538 float green = ((color >>> 8) & 0xFF) / 255f * alpha; 539 float blue = (color & 0xFF) / 255f * alpha; 540 mTempColor[0] = red; 541 mTempColor[1] = green; 542 mTempColor[2] = blue; 543 mTempColor[3] = alpha; 544 return mTempColor; 545 } 546 547 private void enableBlending(boolean enableBlending) { 548 if (enableBlending) { 549 GLES20.glEnable(GLES20.GL_BLEND); 550 checkError(); 551 } else { 552 GLES20.glDisable(GLES20.GL_BLEND); 553 checkError(); 554 } 555 } 556 557 private void setPosition(ShaderParameter[] params, int offset) { 558 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates); 559 checkError(); 560 GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX, 561 GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE); 562 checkError(); 563 } 564 565 private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width, 566 float height) { 567 setMatrix(params, x, y, width, height); 568 int positionHandle = params[INDEX_POSITION].handle; 569 GLES20.glEnableVertexAttribArray(positionHandle); 570 checkError(); 571 GLES20.glDrawArrays(type, 0, count); 572 checkError(); 573 GLES20.glDisableVertexAttribArray(positionHandle); 574 checkError(); 575 } 576 577 private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) { 578 Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f); 579 Matrix.scaleM(mTempMatrix, 0, width, height, 1f); 580 Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0); 581 GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE); 582 checkError(); 583 } 584 585 @Override 586 public void fillRect(float x, float y, float width, float height, int color) { 587 draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height, 588 color, 0f); 589 mCountFillRect++; 590 } 591 592 @Override 593 public void drawTexture(BasicTexture texture, int x, int y, int width, int height) { 594 if (width <= 0 || height <= 0) { 595 return; 596 } 597 copyTextureCoordinates(texture, mTempSourceRect); 598 mTempTargetRect.set(x, y, x + width, y + height); 599 convertCoordinate(mTempSourceRect, mTempTargetRect, texture); 600 drawTextureRect(texture, mTempSourceRect, mTempTargetRect); 601 } 602 603 private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) { 604 int left = 0; 605 int top = 0; 606 int right = texture.getWidth(); 607 int bottom = texture.getHeight(); 608 if (texture.hasBorder()) { 609 left = 1; 610 top = 1; 611 right -= 1; 612 bottom -= 1; 613 } 614 outRect.set(left, top, right, bottom); 615 } 616 617 @Override 618 public void drawTexture(BasicTexture texture, RectF source, RectF target) { 619 if (target.width() <= 0 || target.height() <= 0) { 620 return; 621 } 622 mTempSourceRect.set(source); 623 mTempTargetRect.set(target); 624 625 convertCoordinate(mTempSourceRect, mTempTargetRect, texture); 626 drawTextureRect(texture, mTempSourceRect, mTempTargetRect); 627 } 628 629 @Override 630 public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w, 631 int h) { 632 if (w <= 0 || h <= 0) { 633 return; 634 } 635 mTempTargetRect.set(x, y, x + w, y + h); 636 drawTextureRect(texture, textureTransform, mTempTargetRect); 637 } 638 639 private void drawTextureRect(BasicTexture texture, RectF source, RectF target) { 640 setTextureMatrix(source); 641 drawTextureRect(texture, mTempTextureMatrix, target); 642 } 643 644 private void setTextureMatrix(RectF source) { 645 mTempTextureMatrix[0] = source.width(); 646 mTempTextureMatrix[5] = source.height(); 647 mTempTextureMatrix[12] = source.left; 648 mTempTextureMatrix[13] = source.top; 649 } 650 651 // This function changes the source coordinate to the texture coordinates. 652 // It also clips the source and target coordinates if it is beyond the 653 // bound of the texture. 654 private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) { 655 int width = texture.getWidth(); 656 int height = texture.getHeight(); 657 int texWidth = texture.getTextureWidth(); 658 int texHeight = texture.getTextureHeight(); 659 // Convert to texture coordinates 660 source.left /= texWidth; 661 source.right /= texWidth; 662 source.top /= texHeight; 663 source.bottom /= texHeight; 664 665 // Clip if the rendering range is beyond the bound of the texture. 666 float xBound = (float) width / texWidth; 667 if (source.right > xBound) { 668 target.right = target.left + target.width() * (xBound - source.left) / source.width(); 669 source.right = xBound; 670 } 671 float yBound = (float) height / texHeight; 672 if (source.bottom > yBound) { 673 target.bottom = target.top + target.height() * (yBound - source.top) / source.height(); 674 source.bottom = yBound; 675 } 676 } 677 678 private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) { 679 ShaderParameter[] params = prepareTexture(texture); 680 setPosition(params, OFFSET_FILL_RECT); 681 GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0); 682 checkError(); 683 draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top, 684 target.width(), target.height()); 685 mCountTextureRect++; 686 } 687 688 private ShaderParameter[] prepareTexture(BasicTexture texture) { 689 ShaderParameter[] params; 690 int program; 691 if (texture.getTarget() == GLES20.GL_TEXTURE_2D) { 692 params = mTextureParameters; 693 program = mTextureProgram; 694 } else { 695 params = mOesTextureParameters; 696 program = mOesTextureProgram; 697 } 698 prepareTexture(texture, program, params); 699 return params; 700 } 701 702 private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { 703 GLES20.glUseProgram(program); 704 checkError(); 705 enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA); 706 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 707 checkError(); 708 texture.onBind(this); 709 GLES20.glBindTexture(texture.getTarget(), texture.getId()); 710 checkError(); 711 GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0); 712 checkError(); 713 GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha()); 714 checkError(); 715 } 716 717 @Override 718 public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer, 719 int indexBuffer, int indexCount) { 720 prepareTexture(texture, mMeshProgram, mMeshParameters); 721 722 GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 723 checkError(); 724 725 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer); 726 checkError(); 727 int positionHandle = mMeshParameters[INDEX_POSITION].handle; 728 GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 729 VERTEX_STRIDE, 0); 730 checkError(); 731 732 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer); 733 checkError(); 734 int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle; 735 GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, 736 false, VERTEX_STRIDE, 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 glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0); 809 ids.clear(); 810 } 811 812 ids = mDeleteBuffers; 813 if (ids.size() > 0) { 814 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 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 @Override 964 public void setBlending(Blending blending) { 965 Blending currentBlending = mBlendings.get(mBlendings.size() - 1); 966 if (currentBlending == blending) { 967 return; // nothing to change 968 } 969 mBlendings.set(mBlendings.size() - 1, blending); 970 int srcFunc = GLES20.GL_ONE; 971 int dstFunc; 972 switch (blending) { 973 case Additive: 974 dstFunc = GLES20.GL_ONE; 975 break; 976 case Mix: 977 dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; 978 break; 979 default: 980 Utils.fail("Unknown blend: " + blending); 981 dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; 982 break; 983 } 984 GLES20.glBlendFunc(srcFunc, dstFunc); 985 checkError(); 986 } 987 988 @Override 989 public int generateTexture() { 990 // Can use anything as a lock. No need to create a new object. 991 synchronized (mTempIntArray) { 992 return mNextTextureId++; 993 } 994 } 995 996 @Override 997 public void glGenBuffers(int n, int[] buffers, int offset) { 998 GLES20.glGenBuffers(n, buffers, offset); 999 checkError(); 1000 } 1001 1002 @Override 1003 public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) { 1004 GLES20.glDeleteTextures(n, textures, offset); 1005 checkError(); 1006 } 1007 1008 @Override 1009 public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) { 1010 GLES20.glDeleteBuffers(n, buffers, offset); 1011 checkError(); 1012 } 1013 1014 @Override 1015 public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) { 1016 GLES20.glDeleteFramebuffers(n, buffers, offset); 1017 checkError(); 1018 } 1019 1020 @Override 1021 public void enableStencil() { 1022 GLES20.glEnable(GLES20.GL_STENCIL_TEST); 1023 } 1024 1025 @Override 1026 public void disableStencil() { 1027 GLES20.glDisable(GLES20.GL_STENCIL_TEST); 1028 } 1029 1030 @Override 1031 public void clearStencilBuffer() { 1032 GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT); 1033 } 1034 1035 @Override 1036 public void updateStencil(boolean update) { 1037 int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP; 1038 GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp); 1039 } 1040 1041 @Override 1042 public void drawOnlyOutsideStencil(boolean onlyOutside) { 1043 int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS; 1044 GLES20.glStencilFunc(func, 1, 1); 1045 } 1046 1047 private static void checkError() { 1048 int error = GLES20.glGetError(); 1049 if (error != 0) { 1050 Throwable t = new Throwable(); 1051 Log.e(TAG, "GL error: " + error, t); 1052 } 1053 } 1054 1055 @SuppressWarnings("unused") 1056 private static void printMatrix(String message, float[] m, int offset) { 1057 StringBuilder b = new StringBuilder(message); 1058 for (int i = 0; i < MATRIX_SIZE; i++) { 1059 b.append(' '); 1060 if (i % 4 == 0) { 1061 b.append('\n'); 1062 } 1063 b.append(m[offset + i]); 1064 } 1065 Log.v(TAG, b.toString()); 1066 } 1067 1068} 1069