LayoutLib: Fix gradient rendering.

- fully support canvas transform
- fully support shader local transform
- fix repeat/mirror issue in the negative values.

Change-Id: Ib2aa7ade1c2702da4364cbda9a5a3ae72c1d3174
diff --git a/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
index 042d557..bc4ccd2 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
@@ -119,20 +119,23 @@
                             pos = 0.f;
                             break;
                         case REPEAT:
-                            // remove the integer part to stay in the [0,1] range
-                            // careful: this is a negative value, so use ceil instead of floor
-                            pos = pos - (float)Math.ceil(pos);
+                            // remove the integer part to stay in the [0,1] range.
+                            // we also need to invert the value from [-1,0] to [0, 1]
+                            pos = pos - (float)Math.floor(pos);
                             break;
                         case MIRROR:
+                            // this is the same as the positive side, just make the value positive
+                            // first.
+                            pos = Math.abs(pos);
+
                             // get the integer and the decimal part
-                            // careful: this is a negative value, so use ceil instead of floor
-                            int intPart = (int)Math.ceil(pos);
+                            int intPart = (int)Math.floor(pos);
                             pos = pos - intPart;
-                            // 0  -> -1 : mirrored order
-                            // -1 -> -2: normal order
+                            // 0 -> 1 : normal order
+                            // 1 -> 2: mirrored
                             // etc..
-                            // this means if the intpart is even we invert
-                            if ((intPart % 2) == 0) {
+                            // this means if the intpart is odd we invert
+                            if ((intPart % 2) == 1) {
                                 pos = 1.f - pos;
                             }
                             break;
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index 7573dc1..862b4544 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -16,11 +16,14 @@
 
 package android.graphics;
 
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 
 import android.graphics.Shader.TileMode;
 
 import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
 
 /**
  * Delegate implementing the native methods of android.graphics.LinearGradient
@@ -115,7 +118,7 @@
      * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
      * modes.
      */
-    private static class LinearGradientPaint extends GradientPaint {
+    private class LinearGradientPaint extends GradientPaint {
 
         private final float mX0;
         private final float mY0;
@@ -126,11 +129,11 @@
         public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
                 float positions[], TileMode tile) {
             super(colors, positions, tile);
-                mX0 = x0;
-                mY0 = y0;
-                mDx = x1 - x0;
-                mDy = y1 - y0;
-                mDSize2 = mDx * mDx + mDy * mDy;
+            mX0 = x0;
+            mY0 = y0;
+            mDx = x1 - x0;
+            mDy = y1 - y0;
+            mDSize2 = mDx * mDx + mDy * mDy;
         }
 
         public java.awt.PaintContext createContext(
@@ -140,16 +143,37 @@
                 java.awt.geom.AffineTransform  xform,
                 java.awt.RenderingHints        hints) {
             precomputeGradientColors();
-            return new LinearGradientPaintContext(colorModel);
+
+            AffineTransform canvasMatrix;
+            try {
+                canvasMatrix = xform.createInverse();
+            } catch (NoninvertibleTransformException e) {
+                Bridge.getLog().error(null, "Unable to inverse matrix in LinearGradient", e);
+                canvasMatrix = new AffineTransform();
+            }
+
+            AffineTransform localMatrix = getLocalMatrix();
+            try {
+                localMatrix = localMatrix.createInverse();
+            } catch (NoninvertibleTransformException e) {
+                Bridge.getLog().error(null, "Unable to inverse matrix in LinearGradient", e);
+                localMatrix = new AffineTransform();
+            }
+
+            return new LinearGradientPaintContext(canvasMatrix, localMatrix, colorModel);
         }
 
         private class LinearGradientPaintContext implements java.awt.PaintContext {
 
+            private final AffineTransform mCanvasMatrix;
+            private final AffineTransform mLocalMatrix;
             private final java.awt.image.ColorModel mColorModel;
 
-            public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) {
+            public LinearGradientPaintContext(AffineTransform canvasMatrix,
+                    AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
+                mCanvasMatrix = canvasMatrix;
+                mLocalMatrix = localMatrix;
                 mColorModel = colorModel;
-                // FIXME: so far all this is always the same rect gotten in getRaster with an identity matrix?
             }
 
             public void dispose() {
@@ -165,31 +189,22 @@
 
                 int[] data = new int[w*h];
 
-                if (mDx == 0) { // vertical gradient
-                    // compute first column and copy to all other columns
-                    int index = 0;
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        int color = getColor(iy + y, mY0, mDy);
-                        for (int ix = 0 ; ix < w ; ix++) {
-                            data[index++] = color;
-                        }
-                    }
-                } else if (mDy == 0) { // horizontal
-                    // compute first line in a tmp array and copy to all lines
-                    int[] line = new int[w];
+                int index = 0;
+                float[] pt1 = new float[2];
+                float[] pt2 = new float[2];
+                for (int iy = 0 ; iy < h ; iy++) {
                     for (int ix = 0 ; ix < w ; ix++) {
-                        line[ix] = getColor(ix + x, mX0, mDx);
-                    }
+                        // handle the canvas transform
+                        pt1[0] = x + ix;
+                        pt1[1] = y + iy;
+                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
 
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        System.arraycopy(line, 0, data, iy*w, line.length);
-                    }
-                } else {
-                    int index = 0;
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        for (int ix = 0 ; ix < w ; ix++) {
-                            data[index++] = getColor(ix + x, iy + y);
-                        }
+                        // handle the local matrix.
+                        pt1[0] = pt2[0];
+                        pt1[1] = pt2[1];
+                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        data[index++] = getColor(pt2[0], pt2[1]);
                     }
                 }
 
@@ -199,13 +214,6 @@
             }
         }
 
-        /** Returns a color for the easy vertical/horizontal mode */
-        private int getColor(float absPos, float refPos, float refSize) {
-            float pos = (absPos - refPos) / refSize;
-
-            return getGradientColor(pos);
-        }
-
         /**
          * Returns a color for an arbitrary point.
          */
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index bef8c8c..6b43544 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -471,7 +471,7 @@
             return false;
         }
 
-        d.preTransform(getRotate(degrees, px, py));
+        d.postTransform(getRotate(degrees, px, py));
         return true;
     }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index c36ce53..eebf378 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -16,11 +16,14 @@
 
 package android.graphics;
 
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 
 import android.graphics.Shader.TileMode;
 
 import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
 
 /**
  * Delegate implementing the native methods of android.graphics.RadialGradient
@@ -105,18 +108,17 @@
     private RadialGradient_Delegate(float x, float y, float radius, int colors[], float positions[],
             TileMode tile) {
         super(colors, positions);
-
         mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
     }
 
-    private static class RadialGradientPaint extends GradientPaint {
+    private class RadialGradientPaint extends GradientPaint {
 
         private final float mX;
         private final float mY;
         private final float mRadius;
 
-        public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions,
-                TileMode mode) {
+        public RadialGradientPaint(float x, float y, float radius,
+                int[] colors, float[] positions, TileMode mode) {
             super(colors, positions, mode);
             mX = x;
             mY = y;
@@ -130,14 +132,36 @@
                 java.awt.geom.AffineTransform xform,
                 java.awt.RenderingHints       hints) {
             precomputeGradientColors();
-            return new RadialGradientPaintContext(colorModel);
+
+            AffineTransform canvasMatrix;
+            try {
+                canvasMatrix = xform.createInverse();
+            } catch (NoninvertibleTransformException e) {
+                Bridge.getLog().error(null, "Unable to inverse matrix in RadialGradient", e);
+                canvasMatrix = new AffineTransform();
+            }
+
+            AffineTransform localMatrix = getLocalMatrix();
+            try {
+                localMatrix = localMatrix.createInverse();
+            } catch (NoninvertibleTransformException e) {
+                Bridge.getLog().error(null, "Unable to inverse matrix in RadialGradient", e);
+                localMatrix = new AffineTransform();
+            }
+
+            return new RadialGradientPaintContext(canvasMatrix, localMatrix, colorModel);
         }
 
         private class RadialGradientPaintContext implements java.awt.PaintContext {
 
+            private final AffineTransform mCanvasMatrix;
+            private final AffineTransform mLocalMatrix;
             private final java.awt.image.ColorModel mColorModel;
 
-            public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) {
+            public RadialGradientPaintContext(AffineTransform canvasMatrix,
+                    AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
+                mCanvasMatrix = canvasMatrix;
+                mLocalMatrix = localMatrix;
                 mColorModel = colorModel;
             }
 
@@ -157,10 +181,22 @@
                 // compute distance from each point to the center, and figure out the distance from
                 // it.
                 int index = 0;
+                float[] pt1 = new float[2];
+                float[] pt2 = new float[2];
                 for (int iy = 0 ; iy < h ; iy++) {
                     for (int ix = 0 ; ix < w ; ix++) {
-                        float _x = x + ix - mX;
-                        float _y = y + iy - mY;
+                        // handle the canvas transform
+                        pt1[0] = x + ix;
+                        pt1[1] = y + iy;
+                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        // handle the local matrix
+                        pt1[0] = pt2[0] - mX;
+                        pt1[1] = pt2[1] - mY;
+                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        float _x = pt2[0];
+                        float _y = pt2[1];
                         float distance = (float) Math.sqrt(_x * _x + _y * _y);
 
                         data[index++] = getGradientColor(distance / mRadius);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
index 646ac80..7bf1443 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -18,6 +18,8 @@
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
 
+import java.awt.geom.AffineTransform;
+
 /**
  * Delegate implementing the native methods of android.graphics.Shader
  *
@@ -109,4 +111,19 @@
 
     // ---- Private delegate/helper methods ----
 
+    protected AffineTransform getLocalMatrix() {
+        Matrix_Delegate localMatrixDelegate = null;
+        if (mLocalMatrix > 0) {
+            localMatrixDelegate = Matrix_Delegate.getDelegate(mLocalMatrix);
+            if (localMatrixDelegate == null) {
+                assert false;
+                return new AffineTransform();
+            }
+
+            return localMatrixDelegate.getAffineTransform();
+        }
+
+        return new AffineTransform();
+    }
+
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index 358c3c7..97c3cfd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -16,9 +16,12 @@
 
 package android.graphics;
 
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 
 import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
 
 /**
  * Delegate implementing the native methods of android.graphics.SweepGradient
@@ -90,16 +93,16 @@
     private SweepGradient_Delegate(float cx, float cy,
                          int colors[], float positions[]) {
         super(colors, positions);
-
         mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
     }
 
-    private static class SweepGradientPaint extends GradientPaint {
+    private class SweepGradientPaint extends GradientPaint {
 
         private final float mCx;
         private final float mCy;
 
-        public SweepGradientPaint(float cx, float cy, int[] colors, float[] positions) {
+        public SweepGradientPaint(float cx, float cy, int[] colors,
+                float[] positions) {
             super(colors, positions, null /*tileMode*/);
             mCx = cx;
             mCy = cy;
@@ -112,14 +115,36 @@
                 java.awt.geom.AffineTransform xform,
                 java.awt.RenderingHints       hints) {
             precomputeGradientColors();
-            return new SweepGradientPaintContext(colorModel);
+
+            AffineTransform canvasMatrix;
+            try {
+                canvasMatrix = xform.createInverse();
+            } catch (NoninvertibleTransformException e) {
+                Bridge.getLog().error(null, "Unable to inverse matrix in SweepGradient", e);
+                canvasMatrix = new AffineTransform();
+            }
+
+            AffineTransform localMatrix = getLocalMatrix();
+            try {
+                localMatrix = localMatrix.createInverse();
+            } catch (NoninvertibleTransformException e) {
+                Bridge.getLog().error(null, "Unable to inverse matrix in SweepGradient", e);
+                localMatrix = new AffineTransform();
+            }
+
+            return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
         }
 
         private class SweepGradientPaintContext implements java.awt.PaintContext {
 
+            private final AffineTransform mCanvasMatrix;
+            private final AffineTransform mLocalMatrix;
             private final java.awt.image.ColorModel mColorModel;
 
-            public SweepGradientPaintContext(java.awt.image.ColorModel colorModel) {
+            public SweepGradientPaintContext(AffineTransform canvasMatrix,
+                    AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
+                mCanvasMatrix = canvasMatrix;
+                mLocalMatrix = localMatrix;
                 mColorModel = colorModel;
             }
 
@@ -139,10 +164,23 @@
                 // compute angle from each point to the center, and figure out the distance from
                 // it.
                 int index = 0;
+                float[] pt1 = new float[2];
+                float[] pt2 = new float[2];
                 for (int iy = 0 ; iy < h ; iy++) {
                     for (int ix = 0 ; ix < w ; ix++) {
-                        float dx = x + ix - mCx;
-                        float dy = y + iy - mCy;
+                        // handle the canvas transform
+                        pt1[0] = x + ix;
+                        pt1[1] = y + iy;
+                        mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        // handle the local matrix
+                        pt1[0] = pt2[0] - mCx;
+                        pt1[1] = pt2[1] - mCy;
+                        mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
+
+                        float dx = pt2[0];
+                        float dy = pt2[1];
+
                         float angle;
                         if (dx == 0) {
                             angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);