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;
+ }
+
}