Fix shadow painting in layoutlib

Shadows could be painted outside of the allowed drawing region
for the components for which they are the shadow.

Bug: http://b.android.com/215402
Change-Id: I2d2821b745147f3723e8f11d648094fcd684fe51
(cherry picked from commit 9702fffc768db43d0aba4fb1bea54af50af11361)
diff --git a/bridge/src/android/view/RectShadowPainter.java b/bridge/src/android/view/RectShadowPainter.java
index d15cb48..fad35d2 100644
--- a/bridge/src/android/view/RectShadowPainter.java
+++ b/bridge/src/android/view/RectShadowPainter.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import com.android.layoutlib.bridge.impl.GcSnapshot;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 
 import android.graphics.Canvas;
@@ -32,6 +33,8 @@
 import android.graphics.Region.Op;
 import android.graphics.Shader.TileMode;
 
+import java.awt.Rectangle;
+
 /**
  * Paints shadow for rounded rectangles. Inspiration from CardView. Couldn't use that directly,
  * since it modifies the size of the content, that we can't do.
@@ -127,9 +130,16 @@
         int saved = canvas.save();
         // Usually canvas has been translated to the top left corner of the view when this is
         // called. So, setting a clip rect at 0,0 will clip the top left part of the shadow.
-        // Thus, we just expand in each direction by width and height of the canvas.
-        canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
-                canvas.getHeight(), Op.REPLACE);
+        // Thus, we just expand in each direction by width and height of the canvas, while staying
+        // inside the original drawing region.
+        GcSnapshot snapshot = Canvas_Delegate.getDelegate(canvas).getSnapshot();
+        Rectangle originalClip = snapshot.getOriginalClip();
+        if (originalClip != null) {
+            canvas.clipRect(originalClip.x, originalClip.y, originalClip.x + originalClip.width,
+              originalClip.y + originalClip.height, Op.REPLACE);
+            canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
+              canvas.getHeight(), Op.INTERSECT);
+        }
         canvas.translate(0, shadowSize / 2f);
         return saved;
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 769ee33..e80cbf2 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -40,6 +40,7 @@
 import java.awt.Shape;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Area;
+import java.awt.geom.NoninvertibleTransformException;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.util.ArrayList;
@@ -870,4 +871,33 @@
         dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
     }
 
+    /**
+     * Returns the clip of the oldest snapshot of the stack, appropriately translated to be
+     * expressed in the coordinate system of the latest snapshot.
+     */
+    public Rectangle getOriginalClip() {
+        GcSnapshot originalSnapshot = this;
+        while (originalSnapshot.mPrevious != null) {
+            originalSnapshot = originalSnapshot.mPrevious;
+        }
+        if (originalSnapshot.mLayers.isEmpty()) {
+            return null;
+        }
+        Graphics2D graphics2D = originalSnapshot.mLayers.get(0).getGraphics();
+        Rectangle bounds = graphics2D.getClipBounds();
+        if (bounds == null) {
+            return null;
+        }
+        try {
+            AffineTransform originalTransform =
+                    ((Graphics2D) graphics2D.create()).getTransform().createInverse();
+            AffineTransform latestTransform = getTransform().createInverse();
+            bounds.x += latestTransform.getTranslateX() - originalTransform.getTranslateX();
+            bounds.y += latestTransform.getTranslateY() - originalTransform.getTranslateY();
+        } catch (NoninvertibleTransformException e) {
+            Bridge.getLog().warning(null, "Non invertible transformation", null);
+        }
+        return bounds;
+    }
+
 }