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