summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/ui/GLCanvasImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/ui/GLCanvasImpl.java')
-rw-r--r--src/com/android/gallery3d/ui/GLCanvasImpl.java913
1 files changed, 913 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
new file mode 100644
index 000000000..387743f5d
--- /dev/null
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.ui;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.util.IntArray;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.opengl.GLU;
+import android.opengl.Matrix;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.Stack;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+public class GLCanvasImpl implements GLCanvas {
+ @SuppressWarnings("unused")
+ private static final String TAG = "GLCanvasImp";
+
+ private static final float OPAQUE_ALPHA = 0.95f;
+
+ private static final int OFFSET_FILL_RECT = 0;
+ private static final int OFFSET_DRAW_LINE = 4;
+ private static final int OFFSET_DRAW_RECT = 6;
+ private static final float[] BOX_COORDINATES = {
+ 0, 0, 1, 0, 0, 1, 1, 1, // used for filling a rectangle
+ 0, 0, 1, 1, // used for drawing a line
+ 0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
+
+ private final GL11 mGL;
+
+ private final float mMatrixValues[] = new float[16];
+ private final float mTextureMatrixValues[] = new float[16];
+
+ // mapPoints needs 10 input and output numbers.
+ private final float mMapPointsBuffer[] = new float[10];
+
+ private final float mTextureColor[] = new float[4];
+
+ private int mBoxCoords;
+
+ private final GLState mGLState;
+
+ private long mAnimationTime;
+
+ private float mAlpha;
+ private final Rect mClipRect = new Rect();
+ private final Stack<ConfigState> mRestoreStack =
+ new Stack<ConfigState>();
+ private ConfigState mRecycledRestoreAction;
+
+ private final RectF mDrawTextureSourceRect = new RectF();
+ private final RectF mDrawTextureTargetRect = new RectF();
+ private final float[] mTempMatrix = new float[32];
+ private final IntArray mUnboundTextures = new IntArray();
+ private final IntArray mDeleteBuffers = new IntArray();
+ private int mHeight;
+ private boolean mBlendEnabled = true;
+
+ // Drawing statistics
+ int mCountDrawLine;
+ int mCountFillRect;
+ int mCountDrawMesh;
+ int mCountTextureRect;
+ int mCountTextureOES;
+
+ GLCanvasImpl(GL11 gl) {
+ mGL = gl;
+ mGLState = new GLState(gl);
+ initialize();
+ }
+
+ public void setSize(int width, int height) {
+ Utils.assertTrue(width >= 0 && height >= 0);
+ mHeight = height;
+
+ GL11 gl = mGL;
+ gl.glViewport(0, 0, width, height);
+ gl.glMatrixMode(GL11.GL_PROJECTION);
+ gl.glLoadIdentity();
+ GLU.gluOrtho2D(gl, 0, width, 0, height);
+
+ gl.glMatrixMode(GL11.GL_MODELVIEW);
+ gl.glLoadIdentity();
+ float matrix[] = mMatrixValues;
+
+ Matrix.setIdentityM(matrix, 0);
+ Matrix.translateM(matrix, 0, 0, mHeight, 0);
+ Matrix.scaleM(matrix, 0, 1, -1, 1);
+
+ mClipRect.set(0, 0, width, height);
+ gl.glScissor(0, 0, width, height);
+ }
+
+ public long currentAnimationTimeMillis() {
+ return mAnimationTime;
+ }
+
+ public void setAlpha(float alpha) {
+ Utils.assertTrue(alpha >= 0 && alpha <= 1);
+ mAlpha = alpha;
+ }
+
+ public void multiplyAlpha(float alpha) {
+ Utils.assertTrue(alpha >= 0 && alpha <= 1);
+ mAlpha *= alpha;
+ }
+
+ public float getAlpha() {
+ return mAlpha;
+ }
+
+ private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
+ return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+ }
+
+ private void initialize() {
+ GL11 gl = mGL;
+
+ // First create an nio buffer, then create a VBO from it.
+ int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
+ FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
+ xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0);
+
+ int[] name = new int[1];
+ gl.glGenBuffers(1, name, 0);
+ mBoxCoords = name[0];
+
+ gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
+ gl.glBufferData(GL11.GL_ARRAY_BUFFER,
+ xyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
+ xyBuffer, GL11.GL_STATIC_DRAW);
+
+ gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
+ gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
+
+ // Enable the texture coordinate array for Texture 1
+ gl.glClientActiveTexture(GL11.GL_TEXTURE1);
+ gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
+ gl.glClientActiveTexture(GL11.GL_TEXTURE0);
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+
+ // mMatrixValues will be initialized in setSize()
+ mAlpha = 1.0f;
+ }
+
+ public void drawRect(float x, float y, float width, float height, GLPaint paint) {
+ GL11 gl = mGL;
+
+ mGLState.setColorMode(paint.getColor(), mAlpha);
+ mGLState.setLineWidth(paint.getLineWidth());
+ mGLState.setLineSmooth(paint.getAntiAlias());
+
+ saveTransform();
+ translate(x, y, 0);
+ scale(width, height, 1);
+
+ gl.glLoadMatrixf(mMatrixValues, 0);
+ gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4);
+
+ restoreTransform();
+ mCountDrawLine++;
+ }
+
+ public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
+ GL11 gl = mGL;
+
+ mGLState.setColorMode(paint.getColor(), mAlpha);
+ mGLState.setLineWidth(paint.getLineWidth());
+ mGLState.setLineSmooth(paint.getAntiAlias());
+
+ saveTransform();
+ translate(x1, y1, 0);
+ scale(x2 - x1, y2 - y1, 1);
+
+ gl.glLoadMatrixf(mMatrixValues, 0);
+ gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2);
+
+ restoreTransform();
+ mCountDrawLine++;
+ }
+
+ public void fillRect(float x, float y, float width, float height, int color) {
+ mGLState.setColorMode(color, mAlpha);
+ GL11 gl = mGL;
+
+ saveTransform();
+ translate(x, y, 0);
+ scale(width, height, 1);
+
+ gl.glLoadMatrixf(mMatrixValues, 0);
+ gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
+
+ restoreTransform();
+ mCountFillRect++;
+ }
+
+ public void translate(float x, float y, float z) {
+ Matrix.translateM(mMatrixValues, 0, x, y, z);
+ }
+
+ public void scale(float sx, float sy, float sz) {
+ Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
+ }
+
+ public void rotate(float angle, float x, float y, float z) {
+ float[] temp = mTempMatrix;
+ Matrix.setRotateM(temp, 0, angle, x, y, z);
+ Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0);
+ System.arraycopy(temp, 16, mMatrixValues, 0, 16);
+ }
+
+ public void multiplyMatrix(float matrix[], int offset) {
+ float[] temp = mTempMatrix;
+ Matrix.multiplyMM(temp, 0, mMatrixValues , 0, matrix, 0);
+ System.arraycopy(temp, 0, mMatrixValues, 0, 16);
+ }
+
+ private void textureRect(float x, float y, float width, float height) {
+ GL11 gl = mGL;
+
+ saveTransform();
+ translate(x, y, 0);
+ scale(width, height, 1);
+
+ gl.glLoadMatrixf(mMatrixValues, 0);
+ gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
+
+ restoreTransform();
+ mCountTextureRect++;
+ }
+
+ public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
+ int uvBuffer, int indexBuffer, int indexCount) {
+ float alpha = mAlpha;
+ if (!bindTexture(tex)) return;
+
+ mGLState.setBlendEnabled(mBlendEnabled
+ && (!tex.isOpaque() || alpha < OPAQUE_ALPHA));
+ mGLState.setTextureAlpha(alpha);
+
+ // Reset the texture matrix. We will set our own texture coordinates
+ // below.
+ setTextureCoords(0, 0, 1, 1);
+
+ saveTransform();
+ translate(x, y, 0);
+
+ mGL.glLoadMatrixf(mMatrixValues, 0);
+
+ mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer);
+ mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
+
+ mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer);
+ mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
+
+ mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
+ mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP,
+ indexCount, GL11.GL_UNSIGNED_BYTE, 0);
+
+ mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
+ mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
+ mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
+
+ restoreTransform();
+ mCountDrawMesh++;
+ }
+
+ private float[] mapPoints(float matrix[], int x1, int y1, int x2, int y2) {
+ float[] point = mMapPointsBuffer;
+ int srcOffset = 6;
+ point[srcOffset] = x1;
+ point[srcOffset + 1] = y1;
+ point[srcOffset + 2] = 0;
+ point[srcOffset + 3] = 1;
+
+ int resultOffset = 0;
+ Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
+ point[resultOffset] /= point[resultOffset + 3];
+ point[resultOffset + 1] /= point[resultOffset + 3];
+
+ // map the second point
+ point[srcOffset] = x2;
+ point[srcOffset + 1] = y2;
+ resultOffset = 2;
+ Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
+ point[resultOffset] /= point[resultOffset + 3];
+ point[resultOffset + 1] /= point[resultOffset + 3];
+
+ return point;
+ }
+
+ public boolean clipRect(int left, int top, int right, int bottom) {
+ float point[] = mapPoints(mMatrixValues, left, top, right, bottom);
+
+ // mMatrix could be a rotation matrix. In this case, we need to find
+ // the boundaries after rotation. (only handle 90 * n degrees)
+ if (point[0] > point[2]) {
+ left = (int) point[2];
+ right = (int) point[0];
+ } else {
+ left = (int) point[0];
+ right = (int) point[2];
+ }
+ if (point[1] > point[3]) {
+ top = (int) point[3];
+ bottom = (int) point[1];
+ } else {
+ top = (int) point[1];
+ bottom = (int) point[3];
+ }
+ Rect clip = mClipRect;
+
+ boolean intersect = clip.intersect(left, top, right, bottom);
+ if (!intersect) clip.set(0, 0, 0, 0);
+ mGL.glScissor(clip.left, clip.top, clip.width(), clip.height());
+ return intersect;
+ }
+
+ private void drawBoundTexture(
+ BasicTexture texture, int x, int y, int width, int height) {
+ // Test whether it has been rotated or flipped, if so, glDrawTexiOES
+ // won't work
+ if (isMatrixRotatedOrFlipped(mMatrixValues)) {
+ setTextureCoords(0, 0,
+ (float) texture.getWidth() / texture.getTextureWidth(),
+ (float) texture.getHeight() / texture.getTextureHeight());
+ textureRect(x, y, width, height);
+ } else {
+ // draw the rect from bottom-left to top-right
+ float points[] = mapPoints(
+ mMatrixValues, x, y + height, x + width, y);
+ x = Math.round(points[0]);
+ y = Math.round(points[1]);
+ width = Math.round(points[2]) - x;
+ height = Math.round(points[3]) - y;
+ if (width > 0 && height > 0) {
+ ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
+ mCountTextureOES++;
+ }
+ }
+ }
+
+ public void drawTexture(
+ BasicTexture texture, int x, int y, int width, int height) {
+ drawTexture(texture, x, y, width, height, mAlpha);
+ }
+
+ public void setBlendEnabled(boolean enabled) {
+ mBlendEnabled = enabled;
+ }
+
+ public void drawTexture(BasicTexture texture,
+ int x, int y, int width, int height, float alpha) {
+ if (width <= 0 || height <= 0) return;
+
+ mGLState.setBlendEnabled(mBlendEnabled
+ && (!texture.isOpaque() || alpha < OPAQUE_ALPHA));
+ if (!bindTexture(texture)) return;
+ mGLState.setTextureAlpha(alpha);
+ drawBoundTexture(texture, x, y, width, height);
+ }
+
+ public void drawTexture(BasicTexture texture, RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) return;
+
+ // Copy the input to avoid changing it.
+ mDrawTextureSourceRect.set(source);
+ mDrawTextureTargetRect.set(target);
+ source = mDrawTextureSourceRect;
+ target = mDrawTextureTargetRect;
+
+ mGLState.setBlendEnabled(mBlendEnabled
+ && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
+ if (!bindTexture(texture)) return;
+ convertCoordinate(source, target, texture);
+ setTextureCoords(source);
+ mGLState.setTextureAlpha(mAlpha);
+ textureRect(target.left, target.top, target.width(), target.height());
+ }
+
+ // This function changes the source coordinate to the texture coordinates.
+ // It also clips the source and target coordinates if it is beyond the
+ // bound of the texture.
+ private void convertCoordinate(RectF source, RectF target,
+ BasicTexture texture) {
+
+ int width = texture.getWidth();
+ int height = texture.getHeight();
+ int texWidth = texture.getTextureWidth();
+ int texHeight = texture.getTextureHeight();
+ // Convert to texture coordinates
+ source.left /= texWidth;
+ source.right /= texWidth;
+ source.top /= texHeight;
+ source.bottom /= texHeight;
+
+ // Clip if the rendering range is beyond the bound of the texture.
+ float xBound = (float) width / texWidth;
+ if (source.right > xBound) {
+ target.right = target.left + target.width() *
+ (xBound - source.left) / source.width();
+ source.right = xBound;
+ }
+ float yBound = (float) height / texHeight;
+ if (source.bottom > yBound) {
+ target.bottom = target.top + target.height() *
+ (yBound - source.top) / source.height();
+ source.bottom = yBound;
+ }
+ }
+
+ public void drawMixed(BasicTexture from,
+ int toColor, float ratio, int x, int y, int w, int h) {
+ drawMixed(from, toColor, ratio, x, y, w, h, mAlpha);
+ }
+
+ public void drawMixed(BasicTexture from, BasicTexture to,
+ float ratio, int x, int y, int w, int h) {
+ drawMixed(from, to, ratio, x, y, w, h, mAlpha);
+ }
+
+ private boolean bindTexture(BasicTexture texture) {
+ if (!texture.onBind(this)) return false;
+ mGLState.setTexture2DEnabled(true);
+ mGL.glBindTexture(GL11.GL_TEXTURE_2D, texture.getId());
+ return true;
+ }
+
+ private void setTextureColor(float r, float g, float b, float alpha) {
+ float[] color = mTextureColor;
+ color[0] = r;
+ color[1] = g;
+ color[2] = b;
+ color[3] = alpha;
+ }
+
+ private void drawMixed(BasicTexture from, int toColor,
+ float ratio, int x, int y, int width, int height, float alpha) {
+
+ if (ratio <= 0) {
+ drawTexture(from, x, y, width, height, alpha);
+ return;
+ } else if (ratio >= 1) {
+ fillRect(x, y, width, height, toColor);
+ return;
+ }
+
+ mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
+ || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
+
+ final GL11 gl = mGL;
+ if (!bindTexture(from)) return;
+
+ //
+ // The formula we want:
+ // alpha * ((1 - ratio) * from + ratio * to)
+ // The formula that GL supports is in the form of:
+ // combo * (modulate * from) + (1 - combo) * to
+ //
+ // So, we have combo = 1 - alpha * ratio
+ // and modulate = alpha * (1f - ratio) / combo
+ //
+ float comboRatio = 1 - alpha * ratio;
+
+ // handle the case that (1 - comboRatio) == 0
+ if (alpha < OPAQUE_ALPHA) {
+ mGLState.setTextureAlpha(alpha * (1f - ratio) / comboRatio);
+ } else {
+ mGLState.setTextureAlpha(1f);
+ }
+
+ // Interpolate the RGB and alpha values between both textures.
+ mGLState.setTexEnvMode(GL11.GL_COMBINE);
+ // Specify the interpolation factor via the alpha component of
+ // GL_TEXTURE_ENV_COLORs.
+ // RGB component are get from toColor and will used as SRC1
+ float colorAlpha = (float) (toColor >>> 24) / (0xff * 0xff);
+ setTextureColor(((toColor >>> 16) & 0xff) * colorAlpha,
+ ((toColor >>> 8) & 0xff) * colorAlpha,
+ (toColor & 0xff) * colorAlpha, comboRatio);
+ gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
+
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA);
+
+ // Wire up the interpolation factor for RGB.
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
+
+ // Wire up the interpolation factor for alpha.
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
+
+ drawBoundTexture(from, x, y, width, height);
+ mGLState.setTexEnvMode(GL11.GL_REPLACE);
+ }
+
+ private void drawMixed(BasicTexture from, BasicTexture to,
+ float ratio, int x, int y, int width, int height, float alpha) {
+
+ if (ratio <= 0) {
+ drawTexture(from, x, y, width, height, alpha);
+ return;
+ } else if (ratio >= 1) {
+ drawTexture(to, x, y, width, height, alpha);
+ return;
+ }
+
+ // In the current implementation the two textures must have the
+ // same size.
+ Utils.assertTrue(from.getWidth() == to.getWidth()
+ && from.getHeight() == to.getHeight());
+
+ mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
+ || !to.isOpaque() || alpha < OPAQUE_ALPHA));
+
+ final GL11 gl = mGL;
+ if (!bindTexture(from)) return;
+
+ //
+ // The formula we want:
+ // alpha * ((1 - ratio) * from + ratio * to)
+ // The formula that GL supports is in the form of:
+ // combo * (modulate * from) + (1 - combo) * to
+ //
+ // So, we have combo = 1 - alpha * ratio
+ // and modulate = alpha * (1f - ratio) / combo
+ //
+ float comboRatio = 1 - alpha * ratio;
+
+ // handle the case that (1 - comboRatio) == 0
+ if (alpha < OPAQUE_ALPHA) {
+ mGLState.setTextureAlpha(alpha * (1f - ratio) / comboRatio);
+ } else {
+ mGLState.setTextureAlpha(1f);
+ }
+
+ gl.glActiveTexture(GL11.GL_TEXTURE1);
+ if (!bindTexture(to)) {
+ // Disable TEXTURE1.
+ gl.glDisable(GL11.GL_TEXTURE_2D);
+ // Switch back to the default texture unit.
+ gl.glActiveTexture(GL11.GL_TEXTURE0);
+ return;
+ }
+ gl.glEnable(GL11.GL_TEXTURE_2D);
+
+ // Interpolate the RGB and alpha values between both textures.
+ mGLState.setTexEnvMode(GL11.GL_COMBINE);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
+
+ // Specify the interpolation factor via the alpha component of
+ // GL_TEXTURE_ENV_COLORs.
+ // We don't use the RGB color, so just give them 0s.
+ setTextureColor(0, 0, 0, comboRatio);
+ gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
+
+ // Wire up the interpolation factor for RGB.
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
+
+ // Wire up the interpolation factor for alpha.
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
+
+ // Draw the combined texture.
+ drawBoundTexture(to, x, y, width, height);
+
+ // Disable TEXTURE1.
+ gl.glDisable(GL11.GL_TEXTURE_2D);
+ // Switch back to the default texture unit.
+ gl.glActiveTexture(GL11.GL_TEXTURE0);
+ }
+
+ // TODO: the code only work for 2D should get fixed for 3D or removed
+ private static final int MSKEW_X = 4;
+ private static final int MSKEW_Y = 1;
+ private static final int MSCALE_X = 0;
+ private static final int MSCALE_Y = 5;
+
+ private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
+ final float eps = 1e-5f;
+ return Math.abs(matrix[MSKEW_X]) > eps
+ || Math.abs(matrix[MSKEW_Y]) > eps
+ || matrix[MSCALE_X] < -eps
+ || matrix[MSCALE_Y] > eps;
+ }
+
+ public BasicTexture copyTexture(int x, int y, int width, int height) {
+
+ if (isMatrixRotatedOrFlipped(mMatrixValues)) {
+ throw new IllegalArgumentException("cannot support rotated matrix");
+ }
+ float points[] = mapPoints(mMatrixValues, x, y + height, x + width, y);
+ x = (int) points[0];
+ y = (int) points[1];
+ width = (int) points[2] - x;
+ height = (int) points[3] - y;
+
+ GL11 gl = mGL;
+
+ RawTexture texture = RawTexture.newInstance(this);
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, texture.getId());
+ texture.setSize(width, height);
+
+ int[] cropRect = {0, 0, width, height};
+ gl.glTexParameteriv(GL11.GL_TEXTURE_2D,
+ GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ gl.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0,
+ GL11.GL_RGB, x, y, texture.getTextureWidth(),
+ texture.getTextureHeight(), 0);
+
+ return texture;
+ }
+
+ private static class GLState {
+
+ private final GL11 mGL;
+
+ private int mTexEnvMode = GL11.GL_REPLACE;
+ private float mTextureAlpha = 1.0f;
+ private boolean mTexture2DEnabled = true;
+ private boolean mBlendEnabled = true;
+ private float mLineWidth = 1.0f;
+ private boolean mLineSmooth = false;
+
+ public GLState(GL11 gl) {
+ mGL = gl;
+
+ // Disable unused state
+ gl.glDisable(GL11.GL_LIGHTING);
+
+ // Enable used features
+ gl.glEnable(GL11.GL_DITHER);
+ gl.glEnable(GL11.GL_SCISSOR_TEST);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+ gl.glEnable(GL11.GL_TEXTURE_2D);
+
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
+ GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
+
+ // Set the background color
+ gl.glClearColor(0f, 0f, 0f, 0f);
+ gl.glClearStencil(0);
+
+ gl.glEnable(GL11.GL_BLEND);
+ gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel.
+ gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
+ }
+
+ public void setTexEnvMode(int mode) {
+ if (mTexEnvMode == mode) return;
+ mTexEnvMode = mode;
+ mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode);
+ }
+
+ public void setLineWidth(float width) {
+ if (mLineWidth == width) return;
+ mLineWidth = width;
+ mGL.glLineWidth(width);
+ }
+
+ public void setLineSmooth(boolean enabled) {
+ if (mLineSmooth == enabled) return;
+ mLineSmooth = enabled;
+ if (enabled) {
+ mGL.glEnable(GL11.GL_LINE_SMOOTH);
+ } else {
+ mGL.glDisable(GL11.GL_LINE_SMOOTH);
+ }
+ }
+
+ public void setTextureAlpha(float alpha) {
+ if (mTextureAlpha == alpha) return;
+ mTextureAlpha = alpha;
+ if (alpha >= OPAQUE_ALPHA) {
+ // The alpha is need for those texture without alpha channel
+ mGL.glColor4f(1, 1, 1, 1);
+ setTexEnvMode(GL11.GL_REPLACE);
+ } else {
+ mGL.glColor4f(alpha, alpha, alpha, alpha);
+ setTexEnvMode(GL11.GL_MODULATE);
+ }
+ }
+
+ public void setColorMode(int color, float alpha) {
+ setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA);
+
+ // Set mTextureAlpha to an invalid value, so that it will reset
+ // again in setTextureAlpha(float) later.
+ mTextureAlpha = -1.0f;
+
+ setTexture2DEnabled(false);
+
+ float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f;
+ mGL.glColor4x(
+ Math.round(((color >> 16) & 0xFF) * prealpha),
+ Math.round(((color >> 8) & 0xFF) * prealpha),
+ Math.round((color & 0xFF) * prealpha),
+ Math.round(255 * prealpha));
+ }
+
+ public void setTexture2DEnabled(boolean enabled) {
+ if (mTexture2DEnabled == enabled) return;
+ mTexture2DEnabled = enabled;
+ if (enabled) {
+ mGL.glEnable(GL11.GL_TEXTURE_2D);
+ } else {
+ mGL.glDisable(GL11.GL_TEXTURE_2D);
+ }
+ }
+
+ public void setBlendEnabled(boolean enabled) {
+ if (mBlendEnabled == enabled) return;
+ mBlendEnabled = enabled;
+ if (enabled) {
+ mGL.glEnable(GL11.GL_BLEND);
+ } else {
+ mGL.glDisable(GL11.GL_BLEND);
+ }
+ }
+ }
+
+ public GL11 getGLInstance() {
+ return mGL;
+ }
+
+ public void setCurrentAnimationTimeMillis(long time) {
+ Utils.assertTrue(time >= 0);
+ mAnimationTime = time;
+ }
+
+ public void clearBuffer() {
+ mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
+ }
+
+ private void setTextureCoords(RectF source) {
+ setTextureCoords(source.left, source.top, source.right, source.bottom);
+ }
+
+ private void setTextureCoords(float left, float top,
+ float right, float bottom) {
+ mGL.glMatrixMode(GL11.GL_TEXTURE);
+ mTextureMatrixValues[0] = right - left;
+ mTextureMatrixValues[5] = bottom - top;
+ mTextureMatrixValues[10] = 1;
+ mTextureMatrixValues[12] = left;
+ mTextureMatrixValues[13] = top;
+ mTextureMatrixValues[15] = 1;
+ mGL.glLoadMatrixf(mTextureMatrixValues, 0);
+ mGL.glMatrixMode(GL11.GL_MODELVIEW);
+ }
+
+ // unloadTexture and deleteBuffer can be called from the finalizer thread,
+ // so we synchronized on the mUnboundTextures object.
+ public boolean unloadTexture(BasicTexture t) {
+ synchronized (mUnboundTextures) {
+ if (!t.isLoaded(this)) return false;
+ mUnboundTextures.add(t.mId);
+ return true;
+ }
+ }
+
+ public void deleteBuffer(int bufferId) {
+ synchronized (mUnboundTextures) {
+ mDeleteBuffers.add(bufferId);
+ }
+ }
+
+ public void deleteRecycledResources() {
+ synchronized (mUnboundTextures) {
+ IntArray ids = mUnboundTextures;
+ if (ids.size() > 0) {
+ mGL.glDeleteTextures(ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+
+ ids = mDeleteBuffers;
+ if (ids.size() > 0) {
+ mGL.glDeleteBuffers(ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+ }
+ }
+
+ public int save() {
+ return save(SAVE_FLAG_ALL);
+ }
+
+ public int save(int saveFlags) {
+ ConfigState config = obtainRestoreConfig();
+
+ if ((saveFlags & SAVE_FLAG_ALPHA) != 0) {
+ config.mAlpha = mAlpha;
+ } else {
+ config.mAlpha = -1;
+ }
+
+ if ((saveFlags & SAVE_FLAG_CLIP) != 0) {
+ config.mRect.set(mClipRect);
+ } else {
+ config.mRect.left = Integer.MAX_VALUE;
+ }
+
+ if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
+ System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
+ } else {
+ config.mMatrix[0] = Float.NEGATIVE_INFINITY;
+ }
+
+ mRestoreStack.push(config);
+ return mRestoreStack.size() - 1;
+ }
+
+ public void restore() {
+ if (mRestoreStack.isEmpty()) throw new IllegalStateException();
+ ConfigState config = mRestoreStack.pop();
+ config.restore(this);
+ freeRestoreConfig(config);
+ }
+
+ private void freeRestoreConfig(ConfigState action) {
+ action.mNextFree = mRecycledRestoreAction;
+ mRecycledRestoreAction = action;
+ }
+
+ private ConfigState obtainRestoreConfig() {
+ if (mRecycledRestoreAction != null) {
+ ConfigState result = mRecycledRestoreAction;
+ mRecycledRestoreAction = result.mNextFree;
+ return result;
+ }
+ return new ConfigState();
+ }
+
+ private static class ConfigState {
+ float mAlpha;
+ Rect mRect = new Rect();
+ float mMatrix[] = new float[16];
+ ConfigState mNextFree;
+
+ public void restore(GLCanvasImpl canvas) {
+ if (mAlpha >= 0) canvas.setAlpha(mAlpha);
+ if (mRect.left != Integer.MAX_VALUE) {
+ Rect rect = mRect;
+ canvas.mClipRect.set(rect);
+ canvas.mGL.glScissor(
+ rect.left, rect.top, rect.width(), rect.height());
+ }
+ if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
+ System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
+ }
+ }
+ }
+
+ public void dumpStatisticsAndClear() {
+ String line = String.format(
+ "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d",
+ mCountDrawMesh, mCountTextureRect, mCountTextureOES,
+ mCountFillRect, mCountDrawLine);
+ mCountDrawMesh = 0;
+ mCountTextureRect = 0;
+ mCountTextureOES = 0;
+ mCountFillRect = 0;
+ mCountDrawLine = 0;
+ Log.d(TAG, line);
+ }
+
+ private void saveTransform() {
+ System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16);
+ }
+
+ private void restoreTransform() {
+ System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16);
+ }
+}