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 GLCanvasImpl(GL11 gl)89 GLCanvasImpl(GL11 gl) { 90 mGL = gl; 91 mGLState = new GLState(gl); 92 initialize(); 93 } 94 95 @Override setSize(int width, int height)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 setAlpha(float alpha)124 public void setAlpha(float alpha) { 125 Utils.assertTrue(alpha >= 0 && alpha <= 1); 126 mAlpha = alpha; 127 } 128 129 @Override getAlpha()130 public float getAlpha() { 131 return mAlpha; 132 } 133 134 @Override multiplyAlpha(float alpha)135 public void multiplyAlpha(float alpha) { 136 Utils.assertTrue(alpha >= 0 && alpha <= 1); 137 mAlpha *= alpha; 138 } 139 allocateDirectNativeOrderBuffer(int size)140 private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { 141 return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); 142 } 143 initialize()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 drawRect(float x, float y, float width, float height, GLPaint paint)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 drawLine(float x1, float y1, float x2, float y2, GLPaint paint)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 fillRect(float x, float y, float width, float height, int color)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 translate(float x, float y, float z)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 translate(float x, float y)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 scale(float sx, float sy, float sz)243 public void scale(float sx, float sy, float sz) { 244 Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); 245 } 246 247 @Override rotate(float angle, float x, float y, float z)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 multiplyMatrix(float matrix[], int offset)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 textureRect(float x, float y, float width, float height)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 drawMesh(BasicTexture tex, int x, int y, int xyBuffer, int uvBuffer, int indexBuffer, int indexCount)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. mapPoints(float m[], int x1, int y1, int x2, int y2)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 drawBoundTexture( BasicTexture texture, int x, int y, int width, int height)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 drawTexture( BasicTexture texture, int x, int y, int width, int height)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 drawTexture(BasicTexture texture, int x, int y, int width, int height, float alpha)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 drawTexture(BasicTexture texture, RectF source, RectF target)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 drawTexture(BasicTexture texture, float[] mTextureTransform, int x, int y, int w, int h)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. convertCoordinate(RectF source, RectF target, BasicTexture 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 drawMixed(BasicTexture from, int toColor, float ratio, int x, int y, int w, int h)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 bindTexture(BasicTexture texture)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 setTextureColor(float r, float g, float b, float alpha)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 setMixedColor(int toColor, float ratio, float alpha)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 drawMixed(BasicTexture from, int toColor, float ratio, RectF source, RectF target)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 drawMixed(BasicTexture from, int toColor, float ratio, int x, int y, int width, int height, float alpha)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 isMatrixRotatedOrFlipped(float matrix[])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 GLState(GL11 gl)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 setTexEnvMode(int mode)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 setLineWidth(float width)627 public void setLineWidth(float width) { 628 if (mLineWidth == width) return; 629 mLineWidth = width; 630 mGL.glLineWidth(width); 631 } 632 setTextureAlpha(float alpha)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 setColorMode(int color, float alpha)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. setTextureTarget(int target)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 setBlendEnabled(boolean enabled)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 getGLInstance()687 public GL11 getGLInstance() { 688 return mGL; 689 } 690 691 @Override clearBuffer(float[] argb)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 clearBuffer()702 public void clearBuffer() { 703 clearBuffer(null); 704 } 705 setTextureCoords(RectF source)706 private void setTextureCoords(RectF source) { 707 setTextureCoords(source.left, source.top, source.right, source.bottom); 708 } 709 setTextureCoords(float left, float top, float right, float bottom)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 setTextureCoords(float[] mTextureTransform)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 unloadTexture(BasicTexture t)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 deleteBuffer(int bufferId)741 public void deleteBuffer(int bufferId) { 742 synchronized (mUnboundTextures) { 743 mDeleteBuffers.add(bufferId); 744 } 745 } 746 747 @Override deleteRecycledResources()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 save()765 public void save() { 766 save(SAVE_FLAG_ALL); 767 } 768 769 @Override save(int saveFlags)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 restore()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 freeRestoreConfig(ConfigState action)797 private void freeRestoreConfig(ConfigState action) { 798 action.mNextFree = mRecycledRestoreAction; 799 mRecycledRestoreAction = action; 800 } 801 obtainRestoreConfig()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 restore(GLCanvasImpl canvas)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 dumpStatisticsAndClear()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 saveTransform()838 private void saveTransform() { 839 System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); 840 } 841 restoreTransform()842 private void restoreTransform() { 843 System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); 844 } 845 setRenderTarget(RawTexture texture)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 endRenderTarget()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 beginRenderTarget(RawTexture texture)884 public void beginRenderTarget(RawTexture texture) { 885 save(); // save matrix and alpha 886 mTargetStack.add(mTargetTexture); 887 setRenderTarget(texture); 888 } 889 checkFramebufferStatus(GL11ExtensionPack gl11ep)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