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