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 setSize(int width, int height)95 public void setSize(int width, int height) { 96 Utils.assertTrue(width >= 0 && height >= 0); 97 98 if (mTargetTexture == null) { 99 mScreenWidth = width; 100 mScreenHeight = height; 101 } 102 mAlpha = 1.0f; 103 104 GL11 gl = mGL; 105 gl.glViewport(0, 0, width, height); 106 gl.glMatrixMode(GL11.GL_PROJECTION); 107 gl.glLoadIdentity(); 108 GLU.gluOrtho2D(gl, 0, width, 0, height); 109 110 gl.glMatrixMode(GL11.GL_MODELVIEW); 111 gl.glLoadIdentity(); 112 113 float matrix[] = mMatrixValues; 114 Matrix.setIdentityM(matrix, 0); 115 // to match the graphic coordinate system in android, we flip it vertically. 116 if (mTargetTexture == null) { 117 Matrix.translateM(matrix, 0, 0, height, 0); 118 Matrix.scaleM(matrix, 0, 1, -1, 1); 119 } 120 } 121 setAlpha(float alpha)122 public void setAlpha(float alpha) { 123 Utils.assertTrue(alpha >= 0 && alpha <= 1); 124 mAlpha = alpha; 125 } 126 getAlpha()127 public float getAlpha() { 128 return mAlpha; 129 } 130 multiplyAlpha(float alpha)131 public void multiplyAlpha(float alpha) { 132 Utils.assertTrue(alpha >= 0 && alpha <= 1); 133 mAlpha *= alpha; 134 } 135 allocateDirectNativeOrderBuffer(int size)136 private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { 137 return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); 138 } 139 initialize()140 private void initialize() { 141 GL11 gl = mGL; 142 143 // First create an nio buffer, then create a VBO from it. 144 int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE; 145 FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); 146 xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0); 147 148 int[] name = new int[1]; 149 GLId.glGenBuffers(1, name, 0); 150 mBoxCoords = name[0]; 151 152 gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); 153 gl.glBufferData(GL11.GL_ARRAY_BUFFER, 154 xyBuffer.capacity() * (Float.SIZE / Byte.SIZE), 155 xyBuffer, GL11.GL_STATIC_DRAW); 156 157 gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); 158 gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 159 160 // Enable the texture coordinate array for Texture 1 161 gl.glClientActiveTexture(GL11.GL_TEXTURE1); 162 gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 163 gl.glClientActiveTexture(GL11.GL_TEXTURE0); 164 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 165 166 // mMatrixValues and mAlpha will be initialized in setSize() 167 } 168 drawRect(float x, float y, float width, float height, GLPaint paint)169 public void drawRect(float x, float y, float width, float height, GLPaint paint) { 170 GL11 gl = mGL; 171 172 mGLState.setColorMode(paint.getColor(), mAlpha); 173 mGLState.setLineWidth(paint.getLineWidth()); 174 175 saveTransform(); 176 translate(x, y); 177 scale(width, height, 1); 178 179 gl.glLoadMatrixf(mMatrixValues, 0); 180 gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4); 181 182 restoreTransform(); 183 mCountDrawLine++; 184 } 185 drawLine(float x1, float y1, float x2, float y2, GLPaint paint)186 public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { 187 GL11 gl = mGL; 188 189 mGLState.setColorMode(paint.getColor(), mAlpha); 190 mGLState.setLineWidth(paint.getLineWidth()); 191 192 saveTransform(); 193 translate(x1, y1); 194 scale(x2 - x1, y2 - y1, 1); 195 196 gl.glLoadMatrixf(mMatrixValues, 0); 197 gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2); 198 199 restoreTransform(); 200 mCountDrawLine++; 201 } 202 fillRect(float x, float y, float width, float height, int color)203 public void fillRect(float x, float y, float width, float height, int color) { 204 mGLState.setColorMode(color, mAlpha); 205 GL11 gl = mGL; 206 207 saveTransform(); 208 translate(x, y); 209 scale(width, height, 1); 210 211 gl.glLoadMatrixf(mMatrixValues, 0); 212 gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); 213 214 restoreTransform(); 215 mCountFillRect++; 216 } 217 translate(float x, float y, float z)218 public void translate(float x, float y, float z) { 219 Matrix.translateM(mMatrixValues, 0, x, y, z); 220 } 221 222 // This is a faster version of translate(x, y, z) because 223 // (1) we knows z = 0, (2) we inline the Matrix.translateM call, 224 // (3) we unroll the loop translate(float x, float y)225 public void translate(float x, float y) { 226 float[] m = mMatrixValues; 227 m[12] += m[0] * x + m[4] * y; 228 m[13] += m[1] * x + m[5] * y; 229 m[14] += m[2] * x + m[6] * y; 230 m[15] += m[3] * x + m[7] * y; 231 } 232 scale(float sx, float sy, float sz)233 public void scale(float sx, float sy, float sz) { 234 Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); 235 } 236 rotate(float angle, float x, float y, float z)237 public void rotate(float angle, float x, float y, float z) { 238 if (angle == 0) return; 239 float[] temp = mTempMatrix; 240 Matrix.setRotateM(temp, 0, angle, x, y, z); 241 Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0); 242 System.arraycopy(temp, 16, mMatrixValues, 0, 16); 243 } 244 multiplyMatrix(float matrix[], int offset)245 public void multiplyMatrix(float matrix[], int offset) { 246 float[] temp = mTempMatrix; 247 Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset); 248 System.arraycopy(temp, 0, mMatrixValues, 0, 16); 249 } 250 textureRect(float x, float y, float width, float height)251 private void textureRect(float x, float y, float width, float height) { 252 GL11 gl = mGL; 253 254 saveTransform(); 255 translate(x, y); 256 scale(width, height, 1); 257 258 gl.glLoadMatrixf(mMatrixValues, 0); 259 gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); 260 261 restoreTransform(); 262 mCountTextureRect++; 263 } 264 drawMesh(BasicTexture tex, int x, int y, int xyBuffer, int uvBuffer, int indexBuffer, int indexCount)265 public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, 266 int uvBuffer, int indexBuffer, int indexCount) { 267 float alpha = mAlpha; 268 if (!bindTexture(tex)) return; 269 270 mGLState.setBlendEnabled(mBlendEnabled 271 && (!tex.isOpaque() || alpha < OPAQUE_ALPHA)); 272 mGLState.setTextureAlpha(alpha); 273 274 // Reset the texture matrix. We will set our own texture coordinates 275 // below. 276 setTextureCoords(0, 0, 1, 1); 277 278 saveTransform(); 279 translate(x, y); 280 281 mGL.glLoadMatrixf(mMatrixValues, 0); 282 283 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer); 284 mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); 285 286 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer); 287 mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 288 289 mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 290 mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP, 291 indexCount, GL11.GL_UNSIGNED_BYTE, 0); 292 293 mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); 294 mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); 295 mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); 296 297 restoreTransform(); 298 mCountDrawMesh++; 299 } 300 301 // Transforms two points by the given matrix m. The result 302 // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned. mapPoints(float m[], int x1, int y1, int x2, int y2)303 private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) { 304 float[] r = mMapPointsBuffer; 305 306 // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused. 307 float x3 = m[0] * x1 + m[4] * y1 + m[12]; 308 float y3 = m[1] * x1 + m[5] * y1 + m[13]; 309 float w3 = m[3] * x1 + m[7] * y1 + m[15]; 310 r[0] = x3 / w3; 311 r[1] = y3 / w3; 312 313 // Same for x2 y2. 314 float x4 = m[0] * x2 + m[4] * y2 + m[12]; 315 float y4 = m[1] * x2 + m[5] * y2 + m[13]; 316 float w4 = m[3] * x2 + m[7] * y2 + m[15]; 317 r[2] = x4 / w4; 318 r[3] = y4 / w4; 319 320 return r; 321 } 322 drawBoundTexture( BasicTexture texture, int x, int y, int width, int height)323 private void drawBoundTexture( 324 BasicTexture texture, int x, int y, int width, int height) { 325 // Test whether it has been rotated or flipped, if so, glDrawTexiOES 326 // won't work 327 if (isMatrixRotatedOrFlipped(mMatrixValues)) { 328 if (texture.hasBorder()) { 329 setTextureCoords( 330 1.0f / texture.getTextureWidth(), 331 1.0f / texture.getTextureHeight(), 332 (texture.getWidth() - 1.0f) / texture.getTextureWidth(), 333 (texture.getHeight() - 1.0f) / texture.getTextureHeight()); 334 } else { 335 setTextureCoords(0, 0, 336 (float) texture.getWidth() / texture.getTextureWidth(), 337 (float) texture.getHeight() / texture.getTextureHeight()); 338 } 339 textureRect(x, y, width, height); 340 } else { 341 // draw the rect from bottom-left to top-right 342 float points[] = mapPoints( 343 mMatrixValues, x, y + height, x + width, y); 344 x = (int) (points[0] + 0.5f); 345 y = (int) (points[1] + 0.5f); 346 width = (int) (points[2] + 0.5f) - x; 347 height = (int) (points[3] + 0.5f) - y; 348 if (width > 0 && height > 0) { 349 ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height); 350 mCountTextureOES++; 351 } 352 } 353 } 354 drawTexture( BasicTexture texture, int x, int y, int width, int height)355 public void drawTexture( 356 BasicTexture texture, int x, int y, int width, int height) { 357 drawTexture(texture, x, y, width, height, mAlpha); 358 } 359 drawTexture(BasicTexture texture, int x, int y, int width, int height, float alpha)360 private void drawTexture(BasicTexture texture, 361 int x, int y, int width, int height, float alpha) { 362 if (width <= 0 || height <= 0) return; 363 364 mGLState.setBlendEnabled(mBlendEnabled 365 && (!texture.isOpaque() || alpha < OPAQUE_ALPHA)); 366 if (!bindTexture(texture)) return; 367 mGLState.setTextureAlpha(alpha); 368 drawBoundTexture(texture, x, y, width, height); 369 } 370 drawTexture(BasicTexture texture, RectF source, RectF target)371 public void drawTexture(BasicTexture texture, RectF source, RectF target) { 372 if (target.width() <= 0 || target.height() <= 0) return; 373 374 // Copy the input to avoid changing it. 375 mDrawTextureSourceRect.set(source); 376 mDrawTextureTargetRect.set(target); 377 source = mDrawTextureSourceRect; 378 target = mDrawTextureTargetRect; 379 380 mGLState.setBlendEnabled(mBlendEnabled 381 && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); 382 if (!bindTexture(texture)) return; 383 convertCoordinate(source, target, texture); 384 setTextureCoords(source); 385 mGLState.setTextureAlpha(mAlpha); 386 textureRect(target.left, target.top, target.width(), target.height()); 387 } 388 drawTexture(BasicTexture texture, float[] mTextureTransform, int x, int y, int w, int h)389 public void drawTexture(BasicTexture texture, float[] mTextureTransform, 390 int x, int y, int w, int h) { 391 mGLState.setBlendEnabled(mBlendEnabled 392 && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); 393 if (!bindTexture(texture)) return; 394 setTextureCoords(mTextureTransform); 395 mGLState.setTextureAlpha(mAlpha); 396 textureRect(x, y, w, h); 397 } 398 399 // This function changes the source coordinate to the texture coordinates. 400 // It also clips the source and target coordinates if it is beyond the 401 // bound of the texture. convertCoordinate(RectF source, RectF target, BasicTexture texture)402 private void convertCoordinate(RectF source, RectF target, 403 BasicTexture texture) { 404 405 int width = texture.getWidth(); 406 int height = texture.getHeight(); 407 int texWidth = texture.getTextureWidth(); 408 int texHeight = texture.getTextureHeight(); 409 // Convert to texture coordinates 410 source.left /= texWidth; 411 source.right /= texWidth; 412 source.top /= texHeight; 413 source.bottom /= texHeight; 414 415 // Clip if the rendering range is beyond the bound of the texture. 416 float xBound = (float) width / texWidth; 417 if (source.right > xBound) { 418 target.right = target.left + target.width() * 419 (xBound - source.left) / source.width(); 420 source.right = xBound; 421 } 422 float yBound = (float) height / texHeight; 423 if (source.bottom > yBound) { 424 target.bottom = target.top + target.height() * 425 (yBound - source.top) / source.height(); 426 source.bottom = yBound; 427 } 428 } 429 drawMixed(BasicTexture from, int toColor, float ratio, int x, int y, int w, int h)430 public void drawMixed(BasicTexture from, 431 int toColor, float ratio, int x, int y, int w, int h) { 432 drawMixed(from, toColor, ratio, x, y, w, h, mAlpha); 433 } 434 bindTexture(BasicTexture texture)435 private boolean bindTexture(BasicTexture texture) { 436 if (!texture.onBind(this)) return false; 437 int target = texture.getTarget(); 438 mGLState.setTextureTarget(target); 439 mGL.glBindTexture(target, texture.getId()); 440 return true; 441 } 442 setTextureColor(float r, float g, float b, float alpha)443 private void setTextureColor(float r, float g, float b, float alpha) { 444 float[] color = mTextureColor; 445 color[0] = r; 446 color[1] = g; 447 color[2] = b; 448 color[3] = alpha; 449 } 450 drawMixed(BasicTexture from, int toColor, float ratio, int x, int y, int width, int height, float alpha)451 private void drawMixed(BasicTexture from, int toColor, 452 float ratio, int x, int y, int width, int height, float alpha) { 453 // change from 0 to 0.01f to prevent getting divided by zero below 454 if (ratio <= 0.01f) { 455 drawTexture(from, x, y, width, height, alpha); 456 return; 457 } else if (ratio >= 1) { 458 fillRect(x, y, width, height, toColor); 459 return; 460 } 461 462 mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() 463 || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); 464 465 final GL11 gl = mGL; 466 if (!bindTexture(from)) return; 467 468 // 469 // The formula we want: 470 // alpha * ((1 - ratio) * from + ratio * to) 471 // 472 // The formula that GL supports is in the form of: 473 // combo * from + (1 - combo) * to * scale 474 // 475 // So, we have combo = alpha * (1 - ratio) 476 // and scale = alpha * ratio / (1 - combo) 477 // 478 float combo = alpha * (1 - ratio); 479 float scale = alpha * ratio / (1 - combo); 480 481 // Interpolate the RGB and alpha values between both textures. 482 mGLState.setTexEnvMode(GL11.GL_COMBINE); 483 484 // Specify the interpolation factor via the alpha component of 485 // GL_TEXTURE_ENV_COLORs. 486 // RGB component are get from toColor and will used as SRC1 487 float colorScale = scale * (toColor >>> 24) / (0xff * 0xff); 488 setTextureColor(((toColor >>> 16) & 0xff) * colorScale, 489 ((toColor >>> 8) & 0xff) * colorScale, 490 (toColor & 0xff) * colorScale, combo); 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 drawBoundTexture(from, x, y, width, height); 509 mGLState.setTexEnvMode(GL11.GL_REPLACE); 510 } 511 512 // TODO: the code only work for 2D should get fixed for 3D or removed 513 private static final int MSKEW_X = 4; 514 private static final int MSKEW_Y = 1; 515 private static final int MSCALE_X = 0; 516 private static final int MSCALE_Y = 5; 517 isMatrixRotatedOrFlipped(float matrix[])518 private static boolean isMatrixRotatedOrFlipped(float matrix[]) { 519 final float eps = 1e-5f; 520 return Math.abs(matrix[MSKEW_X]) > eps 521 || Math.abs(matrix[MSKEW_Y]) > eps 522 || matrix[MSCALE_X] < -eps 523 || matrix[MSCALE_Y] > eps; 524 } 525 526 private static class GLState { 527 528 private final GL11 mGL; 529 530 private int mTexEnvMode = GL11.GL_REPLACE; 531 private float mTextureAlpha = 1.0f; 532 private int mTextureTarget = GL11.GL_TEXTURE_2D; 533 private boolean mBlendEnabled = true; 534 private float mLineWidth = 1.0f; 535 private boolean mLineSmooth = false; 536 GLState(GL11 gl)537 public GLState(GL11 gl) { 538 mGL = gl; 539 540 // Disable unused state 541 gl.glDisable(GL11.GL_LIGHTING); 542 543 // Enable used features 544 gl.glEnable(GL11.GL_DITHER); 545 546 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 547 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 548 gl.glEnable(GL11.GL_TEXTURE_2D); 549 550 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, 551 GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE); 552 553 // Set the background color 554 gl.glClearColor(0f, 0f, 0f, 0f); 555 gl.glClearStencil(0); 556 557 gl.glEnable(GL11.GL_BLEND); 558 gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); 559 560 // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel. 561 gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); 562 } 563 setTexEnvMode(int mode)564 public void setTexEnvMode(int mode) { 565 if (mTexEnvMode == mode) return; 566 mTexEnvMode = mode; 567 mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode); 568 } 569 setLineWidth(float width)570 public void setLineWidth(float width) { 571 if (mLineWidth == width) return; 572 mLineWidth = width; 573 mGL.glLineWidth(width); 574 } 575 setTextureAlpha(float alpha)576 public void setTextureAlpha(float alpha) { 577 if (mTextureAlpha == alpha) return; 578 mTextureAlpha = alpha; 579 if (alpha >= OPAQUE_ALPHA) { 580 // The alpha is need for those texture without alpha channel 581 mGL.glColor4f(1, 1, 1, 1); 582 setTexEnvMode(GL11.GL_REPLACE); 583 } else { 584 mGL.glColor4f(alpha, alpha, alpha, alpha); 585 setTexEnvMode(GL11.GL_MODULATE); 586 } 587 } 588 setColorMode(int color, float alpha)589 public void setColorMode(int color, float alpha) { 590 setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA); 591 592 // Set mTextureAlpha to an invalid value, so that it will reset 593 // again in setTextureAlpha(float) later. 594 mTextureAlpha = -1.0f; 595 596 setTextureTarget(0); 597 598 float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f; 599 mGL.glColor4x( 600 Math.round(((color >> 16) & 0xFF) * prealpha), 601 Math.round(((color >> 8) & 0xFF) * prealpha), 602 Math.round((color & 0xFF) * prealpha), 603 Math.round(255 * prealpha)); 604 } 605 606 // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled. setTextureTarget(int target)607 public void setTextureTarget(int target) { 608 if (mTextureTarget == target) return; 609 if (mTextureTarget != 0) { 610 mGL.glDisable(mTextureTarget); 611 } 612 mTextureTarget = target; 613 if (mTextureTarget != 0) { 614 mGL.glEnable(mTextureTarget); 615 } 616 } 617 setBlendEnabled(boolean enabled)618 public void setBlendEnabled(boolean enabled) { 619 if (mBlendEnabled == enabled) return; 620 mBlendEnabled = enabled; 621 if (enabled) { 622 mGL.glEnable(GL11.GL_BLEND); 623 } else { 624 mGL.glDisable(GL11.GL_BLEND); 625 } 626 } 627 } 628 getGLInstance()629 public GL11 getGLInstance() { 630 return mGL; 631 } 632 clearBuffer()633 public void clearBuffer() { 634 mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); 635 } 636 setTextureCoords(RectF source)637 private void setTextureCoords(RectF source) { 638 setTextureCoords(source.left, source.top, source.right, source.bottom); 639 } 640 setTextureCoords(float left, float top, float right, float bottom)641 private void setTextureCoords(float left, float top, 642 float right, float bottom) { 643 mGL.glMatrixMode(GL11.GL_TEXTURE); 644 mTextureMatrixValues[0] = right - left; 645 mTextureMatrixValues[5] = bottom - top; 646 mTextureMatrixValues[10] = 1; 647 mTextureMatrixValues[12] = left; 648 mTextureMatrixValues[13] = top; 649 mTextureMatrixValues[15] = 1; 650 mGL.glLoadMatrixf(mTextureMatrixValues, 0); 651 mGL.glMatrixMode(GL11.GL_MODELVIEW); 652 } 653 setTextureCoords(float[] mTextureTransform)654 private void setTextureCoords(float[] mTextureTransform) { 655 mGL.glMatrixMode(GL11.GL_TEXTURE); 656 mGL.glLoadMatrixf(mTextureTransform, 0); 657 mGL.glMatrixMode(GL11.GL_MODELVIEW); 658 } 659 660 // unloadTexture and deleteBuffer can be called from the finalizer thread, 661 // so we synchronized on the mUnboundTextures object. unloadTexture(BasicTexture t)662 public boolean unloadTexture(BasicTexture t) { 663 synchronized (mUnboundTextures) { 664 if (!t.isLoaded()) return false; 665 mUnboundTextures.add(t.mId); 666 return true; 667 } 668 } 669 deleteBuffer(int bufferId)670 public void deleteBuffer(int bufferId) { 671 synchronized (mUnboundTextures) { 672 mDeleteBuffers.add(bufferId); 673 } 674 } 675 deleteRecycledResources()676 public void deleteRecycledResources() { 677 synchronized (mUnboundTextures) { 678 IntArray ids = mUnboundTextures; 679 if (ids.size() > 0) { 680 GLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0); 681 ids.clear(); 682 } 683 684 ids = mDeleteBuffers; 685 if (ids.size() > 0) { 686 GLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0); 687 ids.clear(); 688 } 689 } 690 } 691 save()692 public void save() { 693 save(SAVE_FLAG_ALL); 694 } 695 save(int saveFlags)696 public void save(int saveFlags) { 697 ConfigState config = obtainRestoreConfig(); 698 699 if ((saveFlags & SAVE_FLAG_ALPHA) != 0) { 700 config.mAlpha = mAlpha; 701 } else { 702 config.mAlpha = -1; 703 } 704 705 706 if ((saveFlags & SAVE_FLAG_MATRIX) != 0) { 707 System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16); 708 } else { 709 config.mMatrix[0] = Float.NEGATIVE_INFINITY; 710 } 711 712 mRestoreStack.add(config); 713 } 714 restore()715 public void restore() { 716 if (mRestoreStack.isEmpty()) throw new IllegalStateException(); 717 ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1); 718 config.restore(this); 719 freeRestoreConfig(config); 720 } 721 freeRestoreConfig(ConfigState action)722 private void freeRestoreConfig(ConfigState action) { 723 action.mNextFree = mRecycledRestoreAction; 724 mRecycledRestoreAction = action; 725 } 726 obtainRestoreConfig()727 private ConfigState obtainRestoreConfig() { 728 if (mRecycledRestoreAction != null) { 729 ConfigState result = mRecycledRestoreAction; 730 mRecycledRestoreAction = result.mNextFree; 731 return result; 732 } 733 return new ConfigState(); 734 } 735 736 private static class ConfigState { 737 float mAlpha; 738 float mMatrix[] = new float[16]; 739 ConfigState mNextFree; 740 restore(GLCanvasImpl canvas)741 public void restore(GLCanvasImpl canvas) { 742 if (mAlpha >= 0) canvas.setAlpha(mAlpha); 743 if (mMatrix[0] != Float.NEGATIVE_INFINITY) { 744 System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16); 745 } 746 } 747 } 748 dumpStatisticsAndClear()749 public void dumpStatisticsAndClear() { 750 String line = String.format( 751 "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", 752 mCountDrawMesh, mCountTextureRect, mCountTextureOES, 753 mCountFillRect, mCountDrawLine); 754 mCountDrawMesh = 0; 755 mCountTextureRect = 0; 756 mCountTextureOES = 0; 757 mCountFillRect = 0; 758 mCountDrawLine = 0; 759 Log.d(TAG, line); 760 } 761 saveTransform()762 private void saveTransform() { 763 System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); 764 } 765 restoreTransform()766 private void restoreTransform() { 767 System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); 768 } 769 setRenderTarget(RawTexture texture)770 private void setRenderTarget(RawTexture texture) { 771 GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL; 772 773 if (mTargetTexture == null && texture != null) { 774 GLId.glGenBuffers(1, mFrameBuffer, 0); 775 gl11ep.glBindFramebufferOES( 776 GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]); 777 } 778 if (mTargetTexture != null && texture == null) { 779 gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); 780 gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0); 781 } 782 783 mTargetTexture = texture; 784 if (texture == null) { 785 setSize(mScreenWidth, mScreenHeight); 786 } else { 787 setSize(texture.getWidth(), texture.getHeight()); 788 789 if (!texture.isLoaded()) texture.prepare(this); 790 791 gl11ep.glFramebufferTexture2DOES( 792 GL11ExtensionPack.GL_FRAMEBUFFER_OES, 793 GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, 794 GL11.GL_TEXTURE_2D, texture.getId(), 0); 795 796 checkFramebufferStatus(gl11ep); 797 } 798 } 799 800 @Override endRenderTarget()801 public void endRenderTarget() { 802 RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1); 803 setRenderTarget(texture); 804 restore(); // restore matrix and alpha 805 } 806 807 @Override beginRenderTarget(RawTexture texture)808 public void beginRenderTarget(RawTexture texture) { 809 save(); // save matrix and alpha 810 mTargetStack.add(mTargetTexture); 811 setRenderTarget(texture); 812 } 813 checkFramebufferStatus(GL11ExtensionPack gl11ep)814 private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) { 815 int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES); 816 if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) { 817 String msg = ""; 818 switch (status) { 819 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES: 820 msg = "FRAMEBUFFER_FORMATS"; 821 break; 822 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES: 823 msg = "FRAMEBUFFER_ATTACHMENT"; 824 break; 825 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES: 826 msg = "FRAMEBUFFER_MISSING_ATTACHMENT"; 827 break; 828 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES: 829 msg = "FRAMEBUFFER_DRAW_BUFFER"; 830 break; 831 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES: 832 msg = "FRAMEBUFFER_READ_BUFFER"; 833 break; 834 case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES: 835 msg = "FRAMEBUFFER_UNSUPPORTED"; 836 break; 837 case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES: 838 msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; 839 break; 840 } 841 throw new RuntimeException(msg + ":" + Integer.toHexString(status)); 842 } 843 } 844 } 845