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 GLES11Canvas(GL11 gl)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 setSize(int width, int height)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 setAlpha(float alpha)156 public void setAlpha(float alpha) { 157 Assert.assertTrue(alpha >= 0 && alpha <= 1); 158 mAlpha = alpha; 159 } 160 161 @Override getAlpha()162 public float getAlpha() { 163 return mAlpha; 164 } 165 166 @Override multiplyAlpha(float alpha)167 public void multiplyAlpha(float alpha) { 168 Assert.assertTrue(alpha >= 0 && alpha <= 1); 169 mAlpha *= alpha; 170 } 171 allocateDirectNativeOrderBuffer(int size)172 private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { 173 return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); 174 } 175 176 @Override drawRect(float x, float y, float width, float height, GLPaint paint)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 drawLine(float x1, float y1, float x2, float y2, GLPaint paint)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 fillRect(float x, float y, float width, float height, int color)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 translate(float x, float y, float z)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 translate(float x, float y)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 scale(float sx, float sy, float sz)246 public void scale(float sx, float sy, float sz) { 247 Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); 248 } 249 250 @Override rotate(float angle, float x, float y, float z)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 multiplyMatrix(float matrix[], int offset)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 textureRect(float x, float y, float width, float height)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 drawMesh(BasicTexture tex, int x, int y, int xyBuffer, int uvBuffer, int indexBuffer, int indexCount)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. mapPoints(float m[], int x1, int y1, int x2, int y2)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 drawBoundTexture( BasicTexture texture, int x, int y, int width, int height)339 private void drawBoundTexture( 340 BasicTexture texture, int x, int y, int width, int height) { 341 // Test whether it has been rotated or flipped, if so, glDrawTexiOES 342 // won't work 343 if (isMatrixRotatedOrFlipped(mMatrixValues)) { 344 if (texture.hasBorder()) { 345 setTextureCoords( 346 1.0f / texture.getTextureWidth(), 347 1.0f / texture.getTextureHeight(), 348 (texture.getWidth() - 1.0f) / texture.getTextureWidth(), 349 (texture.getHeight() - 1.0f) / texture.getTextureHeight()); 350 } else { 351 setTextureCoords(0, 0, 352 (float) texture.getWidth() / texture.getTextureWidth(), 353 (float) texture.getHeight() / texture.getTextureHeight()); 354 } 355 textureRect(x, y, width, height); 356 } else { 357 // draw the rect from bottom-left to top-right 358 float points[] = mapPoints( 359 mMatrixValues, x, y + height, x + width, y); 360 x = (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 drawTexture( BasicTexture texture, int x, int y, int width, int height)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 drawTexture(BasicTexture texture, int x, int y, int width, int height, float alpha)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 drawTexture(BasicTexture texture, RectF source, RectF target)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 drawTexture(BasicTexture texture, float[] mTextureTransform, int x, int y, int w, int h)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. convertCoordinate(RectF source, RectF target, BasicTexture 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 drawMixed(BasicTexture from, int toColor, float ratio, int x, int y, int w, int h)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 bindTexture(BasicTexture texture)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 setTextureColor(float r, float g, float b, float alpha)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 setMixedColor(int toColor, float ratio, float alpha)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 drawMixed(BasicTexture from, int toColor, float ratio, RectF source, RectF target)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 drawMixed(BasicTexture from, int toColor, float ratio, int x, int y, int width, int height, float alpha)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 isMatrixRotatedOrFlipped(float matrix[])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 GLState(GL11 gl)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 setTexEnvMode(int mode)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 setLineWidth(float width)629 public void setLineWidth(float width) { 630 if (mLineWidth == width) return; 631 mLineWidth = width; 632 mGL.glLineWidth(width); 633 } 634 setTextureAlpha(float alpha)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 setColorMode(int color, float alpha)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. setTextureTarget(int target)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 setBlendEnabled(boolean enabled)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 clearBuffer(float[] argb)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 clearBuffer()699 public void clearBuffer() { 700 clearBuffer(null); 701 } 702 setTextureCoords(RectF source)703 private void setTextureCoords(RectF source) { 704 setTextureCoords(source.left, source.top, source.right, source.bottom); 705 } 706 setTextureCoords(float left, float top, float right, float bottom)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 setTextureCoords(float[] mTextureTransform)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 unloadTexture(BasicTexture t)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 deleteBuffer(int bufferId)738 public void deleteBuffer(int bufferId) { 739 synchronized (mUnboundTextures) { 740 mDeleteBuffers.add(bufferId); 741 } 742 } 743 744 @Override deleteRecycledResources()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 save()762 public void save() { 763 save(SAVE_FLAG_ALL); 764 } 765 766 @Override save(int saveFlags)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 restore()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 freeRestoreConfig(ConfigState action)793 private void freeRestoreConfig(ConfigState action) { 794 action.mNextFree = mRecycledRestoreAction; 795 mRecycledRestoreAction = action; 796 } 797 obtainRestoreConfig()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 restore(GLES11Canvas canvas)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 dumpStatisticsAndClear()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 saveTransform()834 private void saveTransform() { 835 System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); 836 } 837 restoreTransform()838 private void restoreTransform() { 839 System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); 840 } 841 setRenderTarget(RawTexture texture)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 endRenderTarget()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 beginRenderTarget(RawTexture texture)880 public void beginRenderTarget(RawTexture texture) { 881 save(); // save matrix and alpha 882 mTargetStack.add(mTargetTexture); 883 setRenderTarget(texture); 884 } 885 checkFramebufferStatus(GL11ExtensionPack gl11ep)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 setTextureParameters(BasicTexture texture)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 initializeTextureSize(BasicTexture texture, int format, int type)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 initializeTexture(BasicTexture texture, Bitmap bitmap)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 texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, int format, int type)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 uploadBuffer(FloatBuffer buf)964 public int uploadBuffer(FloatBuffer buf) { 965 return uploadBuffer(buf, Float.SIZE / Byte.SIZE); 966 } 967 968 @Override uploadBuffer(ByteBuffer buf)969 public int uploadBuffer(ByteBuffer buf) { 970 return uploadBuffer(buf, 1); 971 } 972 uploadBuffer(Buffer buf, int elementSize)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 recoverFromLightCycle()984 public void recoverFromLightCycle() { 985 // This is only required for GLES20 986 } 987 988 @Override getBounds(Rect bounds, int x, int y, int width, int height)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 getGLId()994 public GLId getGLId() { 995 return mGLId; 996 } 997 } 998