1 /* 2 * Copyright (C) 2010 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 17 package com.android.gallery3d.glrenderer; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Rect; 21 import android.graphics.RectF; 22 import android.opengl.GLU; 23 import android.opengl.GLUtils; 24 import android.opengl.Matrix; 25 import android.util.Log; 26 27 import com.android.gallery3d.common.Utils; 28 import com.android.gallery3d.util.IntArray; 29 30 import java.nio.Buffer; 31 import java.nio.ByteBuffer; 32 import java.nio.ByteOrder; 33 import java.nio.FloatBuffer; 34 import java.util.ArrayList; 35 36 import javax.microedition.khronos.opengles.GL10; 37 import javax.microedition.khronos.opengles.GL11; 38 import javax.microedition.khronos.opengles.GL11Ext; 39 import javax.microedition.khronos.opengles.GL11ExtensionPack; 40 41 public class GLES11Canvas implements GLCanvas { 42 @SuppressWarnings("unused") 43 private static final String TAG = "GLCanvasImp"; 44 45 private static final float OPAQUE_ALPHA = 0.95f; 46 47 private static final int OFFSET_FILL_RECT = 0; 48 private static final int OFFSET_DRAW_LINE = 4; 49 private static final int OFFSET_DRAW_RECT = 6; 50 private static final float[] BOX_COORDINATES = { 51 0, 0, 1, 0, 0, 1, 1, 1, // used for filling a rectangle 52 0, 0, 1, 1, // used for drawing a line 53 0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle 54 55 private GL11 mGL; 56 57 private final float mMatrixValues[] = new float[16]; 58 private final float mTextureMatrixValues[] = new float[16]; 59 60 // The results of mapPoints are stored in this buffer, and the order is 61 // x1, y1, x2, y2. 62 private final float mMapPointsBuffer[] = new float[4]; 63 64 private final float mTextureColor[] = new float[4]; 65 66 private int mBoxCoords; 67 68 private GLState mGLState; 69 private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>(); 70 71 private float mAlpha; 72 private final ArrayList<ConfigState> mRestoreStack = new ArrayList<ConfigState>(); 73 private ConfigState mRecycledRestoreAction; 74 75 private final RectF mDrawTextureSourceRect = new RectF(); 76 private final RectF mDrawTextureTargetRect = new RectF(); 77 private final float[] mTempMatrix = new float[32]; 78 private final IntArray mUnboundTextures = new IntArray(); 79 private final IntArray mDeleteBuffers = new IntArray(); 80 private int mScreenWidth; 81 private int mScreenHeight; 82 private boolean mBlendEnabled = true; 83 private int mFrameBuffer[] = new int[1]; 84 private static float[] sCropRect = new float[4]; 85 86 private RawTexture mTargetTexture; 87 88 // Drawing statistics 89 int mCountDrawLine; 90 int mCountFillRect; 91 int mCountDrawMesh; 92 int mCountTextureRect; 93 int mCountTextureOES; 94 95 private static GLId mGLId = new GLES11IdImpl(); 96 97 public GLES11Canvas(GL11 gl) { 98 mGL = gl; 99 mGLState = new GLState(gl); 100 // First create an nio buffer, then create a VBO from it. 101 int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE; 102 FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); 103 xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0); 104 105 int[] name = new int[1]; 106 mGLId.glGenBuffers(1, name, 0); 107 mBoxCoords = name[0]; 108 109 gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); 110 gl.glBufferData(GL11.GL_ARRAY_BUFFER, xyBuffer.capacity() * (Float.SIZE / Byte.SIZE), 111 xyBuffer, GL11.GL_STATIC_DRAW); 112 113 gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); 114 gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 115 116 // Enable the texture coordinate array for Texture 1 117 gl.glClientActiveTexture(GL11.GL_TEXTURE1); 118 gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 119 gl.glClientActiveTexture(GL11.GL_TEXTURE0); 120 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 121 122 // mMatrixValues and mAlpha will be initialized in setSize() 123 } 124 125 @Override 126 public void setSize(int width, int height) { 127 Utils.assertTrue(width >= 0 && height >= 0); 128 129 if (mTargetTexture == null) { 130 mScreenWidth = width; 131 mScreenHeight = height; 132 } 133 mAlpha = 1.0f; 134 135 GL11 gl = mGL; 136 gl.glViewport(0, 0, width, height); 137 gl.glMatrixMode(GL11.GL_PROJECTION); 138 gl.glLoadIdentity(); 139 GLU.gluOrtho2D(gl, 0, width, 0, height); 140 141 gl.glMatrixMode(GL11.GL_MODELVIEW); 142 gl.glLoadIdentity(); 143 144 float matrix[] = mMatrixValues; 145 Matrix.setIdentityM(matrix, 0); 146 // to match the graphic coordinate system in android, we flip it vertically. 147 if (mTargetTexture == null) { 148 Matrix.translateM(matrix, 0, 0, height, 0); 149 Matrix.scaleM(matrix, 0, 1, -1, 1); 150 } 151 } 152 153 @Override 154 public void setAlpha(float alpha) { 155 Utils.assertTrue(alpha >= 0 && alpha <= 1); 156 mAlpha = alpha; 157 } 158 159 @Override 160 public float getAlpha() { 161 return mAlpha; 162 } 163 164 @Override 165 public void multiplyAlpha(float alpha) { 166 Utils.assertTrue(alpha >= 0 && alpha <= 1); 167 mAlpha *= alpha; 168 } 169 170 private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { 171 return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); 172 } 173 174 @Override 175 public void drawRect(float x, float y, float width, float height, GLPaint paint) { 176 GL11 gl = mGL; 177 178 mGLState.setColorMode(paint.getColor(), mAlpha); 179 mGLState.setLineWidth(paint.getLineWidth()); 180 181 saveTransform(); 182 translate(x, y); 183 scale(width, height, 1); 184 185 gl.glLoadMatrixf(mMatrixValues, 0); 186 gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4); 187 188 restoreTransform(); 189 mCountDrawLine++; 190 } 191 192 @Override 193 public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { 194 GL11 gl = mGL; 195 196 mGLState.setColorMode(paint.getColor(), mAlpha); 197 mGLState.setLineWidth(paint.getLineWidth()); 198 199 saveTransform(); 200 translate(x1, y1); 201 scale(x2 - x1, y2 - y1, 1); 202 203 gl.glLoadMatrixf(mMatrixValues, 0); 204 gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2); 205 206 restoreTransform(); 207 mCountDrawLine++; 208 } 209 210 @Override 211 public void fillRect(float x, float y, float width, float height, int color) { 212 mGLState.setColorMode(color, mAlpha); 213 GL11 gl = mGL; 214 215 saveTransform(); 216 translate(x, y); 217 scale(width, height, 1); 218 219 gl.glLoadMatrixf(mMatrixValues, 0); 220 gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); 221 222 restoreTransform(); 223 mCountFillRect++; 224 } 225 226 @Override 227 public void translate(float x, float y, float z) { 228 Matrix.translateM(mMatrixValues, 0, x, y, z); 229 } 230 231 // This is a faster version of translate(x, y, z) because 232 // (1) we knows z = 0, (2) we inline the Matrix.translateM call, 233 // (3) we unroll the loop 234 @Override 235 public void translate(float x, float y) { 236 float[] m = mMatrixValues; 237 m[12] += m[0] * x + m[4] * y; 238 m[13] += m[1] * x + m[5] * y; 239 m[14] += m[2] * x + m[6] * y; 240 m[15] += m[3] * x + m[7] * y; 241 } 242 243 @Override 244 public void scale(float sx, float sy, float sz) { 245 Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); 246 } 247 248 @Override 249 public void rotate(float angle, float x, float y, float z) { 250 if (angle == 0) return; 251 float[] temp = mTempMatrix; 252 Matrix.setRotateM(temp, 0, angle, x, y, z); 253 Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0); 254 System.arraycopy(temp, 16, mMatrixValues, 0, 16); 255 } 256 257 @Override 258 public void multiplyMatrix(float matrix[], int offset) { 259 float[] temp = mTempMatrix; 260 Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset); 261 System.arraycopy(temp, 0, mMatrixValues, 0, 16); 262 } 263 264 private void textureRect(float x, float y, float width, float height) { 265 GL11 gl = mGL; 266 267 saveTransform(); 268 translate(x, y); 269 scale(width, height, 1); 270 271 gl.glLoadMatrixf(mMatrixValues, 0); 272 gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); 273 274 restoreTransform(); 275 mCountTextureRect++; 276 } 277 278 @Override 279 public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, 280 int uvBuffer, int indexBuffer, int indexCount) { 281 float alpha = mAlpha; 282 if (!bindTexture(tex)) return; 283 284 mGLState.setBlendEnabled(mBlendEnabled 285 && (!tex.isOpaque() || alpha < OPAQUE_ALPHA)); 286 mGLState.setTextureAlpha(alpha); 287 288 // Reset the texture matrix. We will set our own texture coordinates 289 // below. 290 setTextureCoords(0, 0, 1, 1); 291 292 saveTransform(); 293 translate(x, y); 294 295 mGL.glLoadMatrixf(mMatrixValues, 0); 296 297 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer); 298 mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); 299 300 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer); 301 mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 302 303 mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 304 mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP, 305 indexCount, GL11.GL_UNSIGNED_BYTE, 0); 306 307 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); 308 mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); 309 mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 310 311 restoreTransform(); 312 mCountDrawMesh++; 313 } 314 315 // Transforms two points by the given matrix m. The result 316 // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned. 317 private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) { 318 float[] r = mMapPointsBuffer; 319 320 // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused. 321 float x3 = m[0] * x1 + m[4] * y1 + m[12]; 322 float y3 = m[1] * x1 + m[5] * y1 + m[13]; 323 float w3 = m[3] * x1 + m[7] * y1 + m[15]; 324 r[0] = x3 / w3; 325 r[1] = y3 / w3; 326 327 // Same for x2 y2. 328 float x4 = m[0] * x2 + m[4] * y2 + m[12]; 329 float y4 = m[1] * x2 + m[5] * y2 + m[13]; 330 float w4 = m[3] * x2 + m[7] * y2 + m[15]; 331 r[2] = x4 / w4; 332 r[3] = y4 / w4; 333 334 return r; 335 } 336 337 private void drawBoundTexture( 338 BasicTexture texture, int x, int y, int width, int height) { 339 // Test whether it has been rotated or flipped, if so, glDrawTexiOES 340 // won't work 341 if (isMatrixRotatedOrFlipped(mMatrixValues)) { 342 if (texture.hasBorder()) { 343 setTextureCoords( 344 1.0f / texture.getTextureWidth(), 345 1.0f / texture.getTextureHeight(), 346 (texture.getWidth() - 1.0f) / texture.getTextureWidth(), 347 (texture.getHeight() - 1.0f) / texture.getTextureHeight()); 348 } else { 349 setTextureCoords(0, 0, 350 (float) texture.getWidth() / texture.getTextureWidth(), 351 (float) texture.getHeight() / texture.getTextureHeight()); 352 } 353 textureRect(x, y, width, height); 354 } else { 355 // draw the rect from bottom-left to top-right 356 float points[] = mapPoints( 357 mMatrixValues, x, y + height, x + width, y); 358 x = (int) (points[0] + 0.5f); 359 y = (int) (points[1] + 0.5f); 360 width = (int) (points[2] + 0.5f) - x; 361 height = (int) (points[3] + 0.5f) - y; 362 if (width > 0 && height > 0) { 363 ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height); 364 mCountTextureOES++; 365 } 366 } 367 } 368 369 @Override 370 public void drawTexture( 371 BasicTexture texture, int x, int y, int width, int height) { 372 drawTexture(texture, x, y, width, height, mAlpha); 373 } 374 375 private void drawTexture(BasicTexture texture, 376 int x, int y, int width, int height, float alpha) { 377 if (width <= 0 || height <= 0) return; 378 379 mGLState.setBlendEnabled(mBlendEnabled 380 && (!texture.isOpaque() || alpha < OPAQUE_ALPHA)); 381 if (!bindTexture(texture)) return; 382 mGLState.setTextureAlpha(alpha); 383 drawBoundTexture(texture, x, y, width, height); 384 } 385 386 @Override 387 public void drawTexture(BasicTexture texture, RectF source, RectF target) { 388 if (target.width() <= 0 || target.height() <= 0) return; 389 390 // Copy the input to avoid changing it. 391 mDrawTextureSourceRect.set(source); 392 mDrawTextureTargetRect.set(target); 393 source = mDrawTextureSourceRect; 394 target = mDrawTextureTargetRect; 395 396 mGLState.setBlendEnabled(mBlendEnabled 397 && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); 398 if (!bindTexture(texture)) return; 399 convertCoordinate(source, target, texture); 400 setTextureCoords(source); 401 mGLState.setTextureAlpha(mAlpha); 402 textureRect(target.left, target.top, target.width(), target.height()); 403 } 404 405 @Override 406 public void drawTexture(BasicTexture texture, float[] mTextureTransform, 407 int x, int y, int w, int h) { 408 mGLState.setBlendEnabled(mBlendEnabled 409 && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); 410 if (!bindTexture(texture)) return; 411 setTextureCoords(mTextureTransform); 412 mGLState.setTextureAlpha(mAlpha); 413 textureRect(x, y, w, h); 414 } 415 416 // This function changes the source coordinate to the texture coordinates. 417 // It also clips the source and target coordinates if it is beyond the 418 // bound of the texture. 419 private static void convertCoordinate(RectF source, RectF target, 420 BasicTexture texture) { 421 422 int width = texture.getWidth(); 423 int height = texture.getHeight(); 424 int texWidth = texture.getTextureWidth(); 425 int texHeight = texture.getTextureHeight(); 426 // Convert to texture coordinates 427 source.left /= texWidth; 428 source.right /= texWidth; 429 source.top /= texHeight; 430 source.bottom /= texHeight; 431 432 // Clip if the rendering range is beyond the bound of the texture. 433 float xBound = (float) width / texWidth; 434 if (source.right > xBound) { 435 target.right = target.left + target.width() * 436 (xBound - source.left) / source.width(); 437 source.right = xBound; 438 } 439 float yBound = (float) height / texHeight; 440 if (source.bottom > yBound) { 441 target.bottom = target.top + target.height() * 442 (yBound - source.top) / source.height(); 443 source.bottom = yBound; 444 } 445 } 446 447 @Override 448 public void drawMixed(BasicTexture from, 449 int toColor, float ratio, int x, int y, int w, int h) { 450 drawMixed(from, toColor, ratio, x, y, w, h, mAlpha); 451 } 452 453 private boolean bindTexture(BasicTexture texture) { 454 if (!texture.onBind(this)) return false; 455 int target = texture.getTarget(); 456 mGLState.setTextureTarget(target); 457 mGL.glBindTexture(target, texture.getId()); 458 return true; 459 } 460 461 private void setTextureColor(float r, float g, float b, float alpha) { 462 float[] color = mTextureColor; 463 color[0] = r; 464 color[1] = g; 465 color[2] = b; 466 color[3] = alpha; 467 } 468 469 private void setMixedColor(int toColor, float ratio, float alpha) { 470 // 471 // The formula we want: 472 // alpha * ((1 - ratio) * from + ratio * to) 473 // 474 // The formula that GL supports is in the form of: 475 // combo * from + (1 - combo) * to * scale 476 // 477 // So, we have combo = alpha * (1 - ratio) 478 // and scale = alpha * ratio / (1 - combo) 479 // 480 float combo = alpha * (1 - ratio); 481 float scale = alpha * ratio / (1 - combo); 482 483 // Specify the interpolation factor via the alpha component of 484 // GL_TEXTURE_ENV_COLORs. 485 // RGB component are get from toColor and will used as SRC1 486 float colorScale = scale * (toColor >>> 24) / (0xff * 0xff); 487 setTextureColor(((toColor >>> 16) & 0xff) * colorScale, 488 ((toColor >>> 8) & 0xff) * colorScale, 489 (toColor & 0xff) * colorScale, combo); 490 GL11 gl = mGL; 491 gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0); 492 493 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE); 494 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE); 495 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT); 496 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR); 497 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT); 498 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA); 499 500 // Wire up the interpolation factor for RGB. 501 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT); 502 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA); 503 504 // Wire up the interpolation factor for alpha. 505 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT); 506 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA); 507 508 } 509 510 @Override 511 public void drawMixed(BasicTexture from, int toColor, float ratio, 512 RectF source, RectF target) { 513 if (target.width() <= 0 || target.height() <= 0) return; 514 515 if (ratio <= 0.01f) { 516 drawTexture(from, source, target); 517 return; 518 } else if (ratio >= 1) { 519 fillRect(target.left, target.top, target.width(), target.height(), toColor); 520 return; 521 } 522 523 float alpha = mAlpha; 524 525 // Copy the input to avoid changing it. 526 mDrawTextureSourceRect.set(source); 527 mDrawTextureTargetRect.set(target); 528 source = mDrawTextureSourceRect; 529 target = mDrawTextureTargetRect; 530 531 mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() 532 || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); 533 534 if (!bindTexture(from)) return; 535 536 // Interpolate the RGB and alpha values between both textures. 537 mGLState.setTexEnvMode(GL11.GL_COMBINE); 538 setMixedColor(toColor, ratio, alpha); 539 convertCoordinate(source, target, from); 540 setTextureCoords(source); 541 textureRect(target.left, target.top, target.width(), target.height()); 542 mGLState.setTexEnvMode(GL11.GL_REPLACE); 543 } 544 545 private void drawMixed(BasicTexture from, int toColor, 546 float ratio, int x, int y, int width, int height, float alpha) { 547 // change from 0 to 0.01f to prevent getting divided by zero below 548 if (ratio <= 0.01f) { 549 drawTexture(from, x, y, width, height, alpha); 550 return; 551 } else if (ratio >= 1) { 552 fillRect(x, y, width, height, toColor); 553 return; 554 } 555 556 mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() 557 || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); 558 559 final GL11 gl = mGL; 560 if (!bindTexture(from)) return; 561 562 // Interpolate the RGB and alpha values between both textures. 563 mGLState.setTexEnvMode(GL11.GL_COMBINE); 564 setMixedColor(toColor, ratio, alpha); 565 566 drawBoundTexture(from, x, y, width, height); 567 mGLState.setTexEnvMode(GL11.GL_REPLACE); 568 } 569 570 // TODO: the code only work for 2D should get fixed for 3D or removed 571 private static final int MSKEW_X = 4; 572 private static final int MSKEW_Y = 1; 573 private static final int MSCALE_X = 0; 574 private static final int MSCALE_Y = 5; 575 576 private static boolean isMatrixRotatedOrFlipped(float matrix[]) { 577 final float eps = 1e-5f; 578 return Math.abs(matrix[MSKEW_X]) > eps 579 || Math.abs(matrix[MSKEW_Y]) > eps 580 || matrix[MSCALE_X] < -eps 581 || matrix[MSCALE_Y] > eps; 582 } 583 584 private static class GLState { 585 586 private final GL11 mGL; 587 588 private int mTexEnvMode = GL11.GL_REPLACE; 589 private float mTextureAlpha = 1.0f; 590 private int mTextureTarget = GL11.GL_TEXTURE_2D; 591 private boolean mBlendEnabled = true; 592 private float mLineWidth = 1.0f; 593 private boolean mLineSmooth = false; 594 595 public GLState(GL11 gl) { 596 mGL = gl; 597 598 // Disable unused state 599 gl.glDisable(GL11.GL_LIGHTING); 600 601 // Enable used features 602 gl.glEnable(GL11.GL_DITHER); 603 604 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 605 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 606 gl.glEnable(GL11.GL_TEXTURE_2D); 607 608 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, 609 GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE); 610 611 // Set the background color 612 gl.glClearColor(0f, 0f, 0f, 0f); 613 614 gl.glEnable(GL11.GL_BLEND); 615 gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); 616 617 // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel. 618 gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); 619 } 620 621 public void setTexEnvMode(int mode) { 622 if (mTexEnvMode == mode) return; 623 mTexEnvMode = mode; 624 mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode); 625 } 626 627 public void setLineWidth(float width) { 628 if (mLineWidth == width) return; 629 mLineWidth = width; 630 mGL.glLineWidth(width); 631 } 632 633 public void setTextureAlpha(float alpha) { 634 if (mTextureAlpha == alpha) return; 635 mTextureAlpha = alpha; 636 if (alpha >= OPAQUE_ALPHA) { 637 // The alpha is need for those texture without alpha channel 638 mGL.glColor4f(1, 1, 1, 1); 639 setTexEnvMode(GL11.GL_REPLACE); 640 } else { 641 mGL.glColor4f(alpha, alpha, alpha, alpha); 642 setTexEnvMode(GL11.GL_MODULATE); 643 } 644 } 645 646 public void setColorMode(int color, float alpha) { 647 setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA); 648 649 // Set mTextureAlpha to an invalid value, so that it will reset 650 // again in setTextureAlpha(float) later. 651 mTextureAlpha = -1.0f; 652 653 setTextureTarget(0); 654 655 float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f; 656 mGL.glColor4x( 657 Math.round(((color >> 16) & 0xFF) * prealpha), 658 Math.round(((color >> 8) & 0xFF) * prealpha), 659 Math.round((color & 0xFF) * prealpha), 660 Math.round(255 * prealpha)); 661 } 662 663 // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled. 664 public void setTextureTarget(int target) { 665 if (mTextureTarget == target) return; 666 if (mTextureTarget != 0) { 667 mGL.glDisable(mTextureTarget); 668 } 669 mTextureTarget = target; 670 if (mTextureTarget != 0) { 671 mGL.glEnable(mTextureTarget); 672 } 673 } 674 675 public void setBlendEnabled(boolean enabled) { 676 if (mBlendEnabled == enabled) return; 677 mBlendEnabled = enabled; 678 if (enabled) { 679 mGL.glEnable(GL11.GL_BLEND); 680 } else { 681 mGL.glDisable(GL11.GL_BLEND); 682 } 683 } 684 } 685 686 @Override 687 public void clearBuffer(float[] argb) { 688 if(argb != null && argb.length == 4) { 689 mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]); 690 } else { 691 mGL.glClearColor(0, 0, 0, 1); 692 } 693 mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); 694 } 695 696 @Override 697 public void clearBuffer() { 698 clearBuffer(null); 699 } 700 701 private void setTextureCoords(RectF source) { 702 setTextureCoords(source.left, source.top, source.right, source.bottom); 703 } 704 705 private void setTextureCoords(float left, float top, 706 float right, float bottom) { 707 mGL.glMatrixMode(GL11.GL_TEXTURE); 708 mTextureMatrixValues[0] = right - left; 709 mTextureMatrixValues[5] = bottom - top; 710 mTextureMatrixValues[10] = 1; 711 mTextureMatrixValues[12] = left; 712 mTextureMatrixValues[13] = top; 713 mTextureMatrixValues[15] = 1; 714 mGL.glLoadMatrixf(mTextureMatrixValues, 0); 715 mGL.glMatrixMode(GL11.GL_MODELVIEW); 716 } 717 718 private void setTextureCoords(float[] mTextureTransform) { 719 mGL.glMatrixMode(GL11.GL_TEXTURE); 720 mGL.glLoadMatrixf(mTextureTransform, 0); 721 mGL.glMatrixMode(GL11.GL_MODELVIEW); 722 } 723 724 // unloadTexture and deleteBuffer can be called from the finalizer thread, 725 // so we synchronized on the mUnboundTextures object. 726 @Override 727 public boolean unloadTexture(BasicTexture t) { 728 synchronized (mUnboundTextures) { 729 if (!t.isLoaded()) return false; 730 mUnboundTextures.add(t.mId); 731 return true; 732 } 733 } 734 735 @Override 736 public void deleteBuffer(int bufferId) { 737 synchronized (mUnboundTextures) { 738 mDeleteBuffers.add(bufferId); 739 } 740 } 741 742 @Override 743 public void deleteRecycledResources() { 744 synchronized (mUnboundTextures) { 745 IntArray ids = mUnboundTextures; 746 if (ids.size() > 0) { 747 mGLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0); 748 ids.clear(); 749 } 750 751 ids = mDeleteBuffers; 752 if (ids.size() > 0) { 753 mGLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0); 754 ids.clear(); 755 } 756 } 757 } 758 759 @Override 760 public void save() { 761 save(SAVE_FLAG_ALL); 762 } 763 764 @Override 765 public void save(int saveFlags) { 766 ConfigState config = obtainRestoreConfig(); 767 768 if ((saveFlags & SAVE_FLAG_ALPHA) != 0) { 769 config.mAlpha = mAlpha; 770 } else { 771 config.mAlpha = -1; 772 } 773 774 if ((saveFlags & SAVE_FLAG_MATRIX) != 0) { 775 System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16); 776 } else { 777 config.mMatrix[0] = Float.NEGATIVE_INFINITY; 778 } 779 780 mRestoreStack.add(config); 781 } 782 783 @Override 784 public void restore() { 785 if (mRestoreStack.isEmpty()) throw new IllegalStateException(); 786 ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1); 787 config.restore(this); 788 freeRestoreConfig(config); 789 } 790 791 private void freeRestoreConfig(ConfigState action) { 792 action.mNextFree = mRecycledRestoreAction; 793 mRecycledRestoreAction = action; 794 } 795 796 private ConfigState obtainRestoreConfig() { 797 if (mRecycledRestoreAction != null) { 798 ConfigState result = mRecycledRestoreAction; 799 mRecycledRestoreAction = result.mNextFree; 800 return result; 801 } 802 return new ConfigState(); 803 } 804 805 private static class ConfigState { 806 float mAlpha; 807 float mMatrix[] = new float[16]; 808 ConfigState mNextFree; 809 810 public void restore(GLES11Canvas canvas) { 811 if (mAlpha >= 0) canvas.setAlpha(mAlpha); 812 if (mMatrix[0] != Float.NEGATIVE_INFINITY) { 813 System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16); 814 } 815 } 816 } 817 818 @Override 819 public void dumpStatisticsAndClear() { 820 String line = String.format( 821 "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", 822 mCountDrawMesh, mCountTextureRect, mCountTextureOES, 823 mCountFillRect, mCountDrawLine); 824 mCountDrawMesh = 0; 825 mCountTextureRect = 0; 826 mCountTextureOES = 0; 827 mCountFillRect = 0; 828 mCountDrawLine = 0; 829 Log.d(TAG, line); 830 } 831 832 private void saveTransform() { 833 System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); 834 } 835 836 private void restoreTransform() { 837 System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); 838 } 839 840 private void setRenderTarget(RawTexture texture) { 841 GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL; 842 843 if (mTargetTexture == null && texture != null) { 844 mGLId.glGenBuffers(1, mFrameBuffer, 0); 845 gl11ep.glBindFramebufferOES( 846 GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]); 847 } 848 if (mTargetTexture != null && texture == null) { 849 gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); 850 gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0); 851 } 852 853 mTargetTexture = texture; 854 if (texture == null) { 855 setSize(mScreenWidth, mScreenHeight); 856 } else { 857 setSize(texture.getWidth(), texture.getHeight()); 858 859 if (!texture.isLoaded()) texture.prepare(this); 860 861 gl11ep.glFramebufferTexture2DOES( 862 GL11ExtensionPack.GL_FRAMEBUFFER_OES, 863 GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, 864 GL11.GL_TEXTURE_2D, texture.getId(), 0); 865 866 checkFramebufferStatus(gl11ep); 867 } 868 } 869 870 @Override 871 public void endRenderTarget() { 872 RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1); 873 setRenderTarget(texture); 874 restore(); // restore matrix and alpha 875 } 876 877 @Override 878 public void beginRenderTarget(RawTexture texture) { 879 save(); // save matrix and alpha 880 mTargetStack.add(mTargetTexture); 881 setRenderTarget(texture); 882 } 883 884 private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) { 885 int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES); 886 if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) { 887 String msg = ""; 888 switch (status) { 889 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES: 890 msg = "FRAMEBUFFER_FORMATS"; 891 break; 892 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES: 893 msg = "FRAMEBUFFER_ATTACHMENT"; 894 break; 895 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES: 896 msg = "FRAMEBUFFER_MISSING_ATTACHMENT"; 897 break; 898 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES: 899 msg = "FRAMEBUFFER_DRAW_BUFFER"; 900 break; 901 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES: 902 msg = "FRAMEBUFFER_READ_BUFFER"; 903 break; 904 case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES: 905 msg = "FRAMEBUFFER_UNSUPPORTED"; 906 break; 907 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES: 908 msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; 909 break; 910 } 911 throw new RuntimeException(msg + ":" + Integer.toHexString(status)); 912 } 913 } 914 915 @Override 916 public void setTextureParameters(BasicTexture texture) { 917 int width = texture.getWidth(); 918 int height = texture.getHeight(); 919 // Define a vertically flipped crop rectangle for OES_draw_texture. 920 // The four values in sCropRect are: left, bottom, width, and 921 // height. Negative value of width or height means flip. 922 sCropRect[0] = 0; 923 sCropRect[1] = height; 924 sCropRect[2] = width; 925 sCropRect[3] = -height; 926 927 // Set texture parameters. 928 int target = texture.getTarget(); 929 mGL.glBindTexture(target, texture.getId()); 930 mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0); 931 mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE); 932 mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE); 933 mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); 934 mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); 935 } 936 937 @Override 938 public void initializeTextureSize(BasicTexture texture, int format, int type) { 939 int target = texture.getTarget(); 940 mGL.glBindTexture(target, texture.getId()); 941 int width = texture.getTextureWidth(); 942 int height = texture.getTextureHeight(); 943 mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null); 944 } 945 946 @Override 947 public void initializeTexture(BasicTexture texture, Bitmap bitmap) { 948 int target = texture.getTarget(); 949 mGL.glBindTexture(target, texture.getId()); 950 GLUtils.texImage2D(target, 0, bitmap, 0); 951 } 952 953 @Override 954 public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, 955 int format, int type) { 956 int target = texture.getTarget(); 957 mGL.glBindTexture(target, texture.getId()); 958 GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); 959 } 960 961 @Override 962 public int uploadBuffer(FloatBuffer buf) { 963 return uploadBuffer(buf, Float.SIZE / Byte.SIZE); 964 } 965 966 @Override 967 public int uploadBuffer(ByteBuffer buf) { 968 return uploadBuffer(buf, 1); 969 } 970 971 private int uploadBuffer(Buffer buf, int elementSize) { 972 int[] bufferIds = new int[1]; 973 mGLId.glGenBuffers(bufferIds.length, bufferIds, 0); 974 int bufferId = bufferIds[0]; 975 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId); 976 mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf, 977 GL11.GL_STATIC_DRAW); 978 return bufferId; 979 } 980 981 @Override 982 public void recoverFromLightCycle() { 983 // This is only required for GLES20 984 } 985 986 @Override 987 public void getBounds(Rect bounds, int x, int y, int width, int height) { 988 // This is only required for GLES20 989 } 990 991 @Override 992 public GLId getGLId() { 993 return mGLId; 994 } 995 } 996