Fix the issue that mixed textures cannot be drawn correctly when
alpha is set.

Change-Id: Ibd90e9c9309bd5ef2ea86725b3d3f3ab689727ec
diff --git a/new3d/src/com/android/gallery3d/ui/GLCanvasImp.java b/new3d/src/com/android/gallery3d/ui/GLCanvasImp.java
index 820c1e7..ed43e84 100644
--- a/new3d/src/com/android/gallery3d/ui/GLCanvasImp.java
+++ b/new3d/src/com/android/gallery3d/ui/GLCanvasImp.java
@@ -4,7 +4,6 @@
 import android.graphics.RectF;
 import android.opengl.GLU;
 import android.opengl.Matrix;
-import android.util.Log;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -16,6 +15,7 @@
 import javax.microedition.khronos.opengles.GL11Ext;
 
 public class GLCanvasImp implements GLCanvas {
+    @SuppressWarnings("unused")
     private static final String TAG = "GLCanvasImp";
 
     // We need 16 vertices for a normal nine-patch image (the 4x4 vertices)
@@ -28,7 +28,6 @@
 
     private final GL11 mGL;
 
-    private final int mTextureId[] = new int[1];
     private final float mMatrixValues[] = new float[16];
 
     private final float mUvBuffer[] = new float[VERTEX_BUFFER_SIZE];
@@ -55,9 +54,9 @@
             new Stack<ConfigState>();
     private ConfigState mRecycledRestoreAction;
 
-    private RectF mDrawTextureSourceRect = new RectF();
-    private RectF mDrawTextureTargetRect = new RectF();
-    private float[] mTempMatrix = new float[32];
+    private final RectF mDrawTextureSourceRect = new RectF();
+    private final RectF mDrawTextureTargetRect = new RectF();
+    private final float[] mTempMatrix = new float[32];
     private final IntArray mUnboundIds = new IntArray();
 
     GLCanvasImp(GL11 gl) {
@@ -183,8 +182,7 @@
         System.arraycopy(temp, 16, mMatrixValues, 0, 16);
     }
 
-    private void textureRect(float x, float y, float width, float height, float alpha) {
-        mGLState.setTextureAlpha(alpha);
+    private void textureRect(float x, float y, float width, float height) {
         GL11 gl = mGL;
         gl.glLoadMatrixf(mMatrixValues, 0);
         putRectangle(x, y, width, height, mXyBuffer, mXyPointer);
@@ -416,7 +414,7 @@
     }
 
     private void drawBoundTexture(
-            BasicTexture texture, int x, int y, int width, int height, float alpha) {
+            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)) {
@@ -424,7 +422,7 @@
                     (texture.mWidth - 0.5f) / texture.mTextureWidth,
                     (texture.mHeight - 0.5f) / texture.mTextureHeight,
                     mUvBuffer, mUvPointer);
-            textureRect(x, y, width, height, alpha);
+            textureRect(x, y, width, height);
         } else {
             // draw the rect from bottom-left to top-right
             float points[] = mapPoints(
@@ -433,12 +431,10 @@
             y = (int) points[1];
             width = (int) points[2] - x;
             height = (int) points[3] - y;
-            mGLState.setTextureAlpha(alpha);
             if (width > 0 && height > 0) {
                 ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
             }
         }
-
     }
 
     public void drawTexture(
@@ -452,7 +448,8 @@
 
         mGLState.setBlendEnabled(!texture.isOpaque() || alpha < OPAQUE_ALPHA);
         bindTexture(texture);
-        drawBoundTexture(texture, x, y, width, height, alpha);
+        mGLState.setTextureAlpha(alpha);
+        drawBoundTexture(texture, x, y, width, height);
     }
 
     public void drawTexture(BasicTexture texture, RectF source, RectF target) {
@@ -468,8 +465,8 @@
         bindTexture(texture);
         convertCoordinate(source, target, texture);
         setTextureCoords(source);
-        textureRect(target.left, target.top, target.right - target.left,
-                target.bottom - target.top, mAlpha);
+        mGLState.setTextureAlpha(mAlpha);
+        textureRect(target.left, target.top, target.width(), target.height());
     }
 
     // This function changes the source coordinate to the texture coordinates.
@@ -527,14 +524,30 @@
 
     public void drawMixed(BasicTexture from, BasicTexture to,
             float ratio, int x, int y, int width, int height, float alpha) {
-        if (alpha < OPAQUE_ALPHA) {
-            throw new RuntimeException("Cannot support alpha value");
-        }
-        mGLState.setBlendEnabled(!from.isOpaque() || !to.isOpaque());
+        mGLState.setBlendEnabled(!from.isOpaque()
+                || !to.isOpaque() || alpha < OPAQUE_ALPHA);
 
         final GL11 gl = mGL;
         bindTexture(from);
 
+        //
+        // The formula we want:
+        //     alpha * ((1 - ratio) * from + ratio * to)
+        // The formula that GL supports is in the form of:
+        //     (1 - combo) * (modulate * from) + combo * to
+        //
+        // So, we have combo = alpha * ratio
+        //     and     modulate = alpha * (1f - ratio) / (1 - combo)
+        //
+        float comboRatio = alpha * ratio;
+
+        // handle the case that (1 - comboRatio) == 0
+        if (alpha < OPAQUE_ALPHA) {
+            mGLState.setTextureAlpha(alpha * (1f - ratio) / (1f - comboRatio));
+        } else {
+            mGLState.setTextureAlpha(1f);
+        }
+
         gl.glActiveTexture(GL11.GL_TEXTURE1);
         bindTexture(to);
         gl.glEnable(GL11.GL_TEXTURE_2D);
@@ -546,7 +559,8 @@
 
         // Specify the interpolation factor via the alpha component of
         // GL_TEXTURE_ENV_COLORs.
-        setTextureColor(ratio, ratio, ratio, ratio);
+        // 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.
@@ -558,7 +572,7 @@
         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
 
         // Draw the combined texture.
-        drawBoundTexture(to, x, y, width, height, alpha);
+        drawBoundTexture(to, x, y, width, height);
 
         // Disable TEXTURE1.
         gl.glDisable(GL11.GL_TEXTURE_2D);
@@ -645,9 +659,7 @@
 
             // Enable used features
             gl.glEnable(GL11.GL_DITHER);
-
             gl.glEnable(GL11.GL_SCISSOR_TEST);
-            gl.glEnable(GL11.GL_STENCIL_TEST);
 
             gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
             gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
diff --git a/new3d/src/com/android/gallery3d/ui/PopupWindow.java b/new3d/src/com/android/gallery3d/ui/PopupWindow.java
index c08c46d..784d046 100644
--- a/new3d/src/com/android/gallery3d/ui/PopupWindow.java
+++ b/new3d/src/com/android/gallery3d/ui/PopupWindow.java
@@ -123,6 +123,7 @@
         int aYoffset = height - aHeight;
         GL11 gl = canvas.getGLInstance();
         if (mAnchor != null) {
+            gl.glEnable(GL11.GL_STENCIL_TEST);
             gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
             gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1);
             mAnchor.draw(canvas, aXoffset, aYoffset);
@@ -134,6 +135,10 @@
             mBackground.draw(canvas, 0, 0,
                     width, height - aHeight + mAnchorOffset);
         }
+
+        if (mAnchor != null) {
+            gl.glDisable(GL11.GL_STENCIL_TEST);
+        }
     }
 
     private void renderBackgroundWithoutStencil(GLCanvas canvas) {
diff --git a/new3d/tests/src/com/android/gallery3d/ui/GLCanvasTest.java b/new3d/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
index 020f5e9..46fae89 100644
--- a/new3d/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
+++ b/new3d/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
@@ -5,7 +5,6 @@
 
 import junit.framework.TestCase;
 
-import java.nio.Buffer;
 import java.util.Arrays;
 
 import javax.microedition.khronos.opengles.GL10;
@@ -211,7 +210,7 @@
     // are used directly.
     private static class FillRectTest extends GLMock {
         private int mDrawArrayCalled = 0;
-        private int[] mResult = new int[8];
+        private final int[] mResult = new int[8];
 
         @Override
         public void glDrawArrays(int mode, int first, int count) {
@@ -279,8 +278,8 @@
     // The matrix here are all listed in column major order.
     //
     private static class TransformTest extends GLMock {
-        private float[] mModelViewMatrixUsed = new float[16];
-        private float[] mProjectionMatrixUsed = new float[16];
+        private final float[] mModelViewMatrixUsed = new float[16];
+        private final float[] mProjectionMatrixUsed = new float[16];
 
         @Override
         public void glDrawArrays(int mode, int first, int count) {
@@ -498,6 +497,7 @@
             mIsOpaque = isOpaque;
         }
 
+        @Override
         protected void onBind(GLCanvas canvas) {
             mBindCalled++;
         }
@@ -664,9 +664,7 @@
         @Override
         public void glTexEnvfv(int target, int pname, float[] params, int offset) {
             if (target == GL_TEXTURE_ENV && pname == GL_TEXTURE_ENV_COLOR) {
-                for (int i = 0; i < 4; i++) {
-                    assertEquals(0.5f, params[offset + i]);
-                }
+                assertEquals(0.5f, params[offset + 3]);
             }
         }
 
@@ -701,9 +699,9 @@
             assertFalse(mTexture2DEnabled1);
 
             // The test is currently broken, waiting for the fix
-            //canvas.setAlpha(0.3f);
-            //canvas.drawMixed(from, to, 0.5f, 100, 200, 300, 400, 1.0f);
-            //assertEquals(GL_COMBINE, getTexEnvi(GL_TEXTURE_ENV_MODE));
+            canvas.setAlpha(0.3f);
+            canvas.drawMixed(from, to, 0.5f, 100, 200, 300, 400, 1.0f);
+            assertEquals(GL_COMBINE, getTexEnvi(GL_TEXTURE_ENV_MODE));
         }
     }