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