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