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