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