Bug 647687 - Fix filter SourceImage bounds. r=longsonr
authorRobert O'Callahan <roc@ocallahan.org>.
Sat, 17 Sep 2011 09:29:51 +0100
changeset 77114 87ce16809ea04684ea9a8302f7755c55af450fd3
parent 77113 09dc9b406bd636c528fc15e4b3c9f5eae0058586
child 77115 13d1f83e452a7fbc986f97af65857e26517fa1c4
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewerslongsonr
bugs647687
milestone9.0a1
Bug 647687 - Fix filter SourceImage bounds. r=longsonr
layout/reftests/svg/filter-bounds-01.svg
layout/reftests/svg/filter-bounds-02.svg
layout/reftests/svg/reftest.list
layout/svg/base/src/nsSVGFilterFrame.cpp
layout/svg/base/src/nsSVGFilterInstance.cpp
layout/svg/base/src/nsSVGFilterInstance.h
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filter-bounds-01.svg
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <title>Testcase for checking that filter bounds include stroke width</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=647687 -->
+
+  <defs>
+    <filter id="f1" filterUnits="userSpaceOnUse" x="150" y="150" width="200" height="200">
+      <feOffset in="SourceGraphic"/>
+    </filter>
+  </defs>
+
+  <rect height="100%" width="100%" fill="lime"/>
+
+  <rect x="150" y="150" height="200" width="200" fill="red"/>
+
+  <rect x="200" y="200" height="100" width="100" stroke-width="100"
+            fill="none" stroke="lime" filter="url(#f1)"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filter-bounds-02.svg
@@ -0,0 +1,25 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <title>Testcase for checking that filter bounds include stroke width</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=647687 -->
+
+  <defs>
+    <filter id="f1" filterUnits="objectBoundingBox">
+      <feFlood flood-color="red"/>
+    </filter>
+  </defs>
+
+  <rect height="100%" width="100%" fill="lime"/>
+
+  <polygon points="200,200 300,200 300,300 200,300" stroke-width="100"
+            fill="none" stroke="lime" filter="url(#f1)"/>
+
+  <rect x="150" y="150" height="200" width="200" fill="lime"/>
+
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -106,16 +106,18 @@ random == dynamic-use-nested-01.svg dyna
 == fallback-color-01a.svg pass.svg
 == fallback-color-01b.svg pass.svg
 == fallback-color-02a.svg fallback-color-02-ref.svg
 == fallback-color-02b.svg fallback-color-02-ref.svg
 == fallback-color-03.svg pass.svg
 == filter-basic-01.svg pass.svg
 == filter-basic-02.svg pass.svg
 == filter-basic-03.svg pass.svg
+== filter-bounds-01.svg pass.svg
+== filter-bounds-02.svg pass.svg
 == filter-foreignObject-01.svg pass.svg
 == filter-invalidation-01.svg pass.svg
 == filter-scaled-01.svg pass.svg
 == filter-translated-01.svg filter-translated-01-ref.svg
 == filters-and-group-opacity-01.svg filters-and-group-opacity-01-ref.svg
 == foreignObject-01.svg pass.svg
 == foreignObject-02.svg foreignObject-02-ref.svg
 == foreignObject-ancestor-style-change-01.svg foreignObject-ancestor-style-change-01-ref.svg
--- a/layout/svg/base/src/nsSVGFilterFrame.cpp
+++ b/layout/svg/base/src/nsSVGFilterFrame.cpp
@@ -201,21 +201,29 @@ nsAutoFilterInstance::nsAutoFilterInstan
   // filterToDeviceSpace is always invertible
   gfxMatrix deviceToFilterSpace = filterToDeviceSpace;
   deviceToFilterSpace.Invert();
 
   nsIntRect dirtyOutputRect =
     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aDirtyOutputRect);
   nsIntRect dirtyInputRect =
     MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aDirtyInputRect);
+  nsIntRect targetBoundsDeviceSpace;
+  nsISVGChildFrame* svgTarget = do_QueryFrame(aTarget);
+  if (svgTarget) {
+    targetBoundsDeviceSpace.UnionRect(targetBoundsDeviceSpace,
+      svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->PresContext()->AppUnitsPerDevPixel()));
+  }
+  nsIntRect targetBoundsFilterSpace =
+    MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, &targetBoundsDeviceSpace);
 
   // Setup instance data
   mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion,
                                       nsIntSize(filterRes.width, filterRes.height),
-                                      filterToDeviceSpace,
+                                      filterToDeviceSpace, targetBoundsFilterSpace,
                                       dirtyOutputRect, dirtyInputRect,
                                       primitiveUnits);
 }
 
 nsAutoFilterInstance::~nsAutoFilterInstance()
 {
 }
 
--- a/layout/svg/base/src/nsSVGFilterInstance.cpp
+++ b/layout/svg/base/src/nsSVGFilterInstance.cpp
@@ -184,16 +184,17 @@ nsSVGFilterInstance::BuildSources()
   mSourceAlpha.mImage.mFilterPrimitiveSubregion = filterRegion;
 
   nsIntRect sourceBoundsInt;
   gfxRect sourceBounds = UserSpaceToFilterSpace(mTargetBBox);
   sourceBounds.RoundOut();
   // Detect possible float->int overflow
   if (!gfxUtils::GfxRectToIntRect(sourceBounds, &sourceBoundsInt))
     return NS_ERROR_FAILURE;
+  sourceBoundsInt.UnionRect(sourceBoundsInt, mTargetBounds);
 
   mSourceColorAlpha.mResultBoundingBox = sourceBoundsInt;
   mSourceAlpha.mResultBoundingBox = sourceBoundsInt;
   return NS_OK;
 }
 
 nsresult
 nsSVGFilterInstance::BuildPrimitives()
--- a/layout/svg/base/src/nsSVGFilterInstance.h
+++ b/layout/svg/base/src/nsSVGFilterInstance.h
@@ -65,26 +65,28 @@ class NS_STACK_CLASS nsSVGFilterInstance
 public:
   nsSVGFilterInstance(nsIFrame *aTargetFrame,
                       nsSVGFilterPaintCallback *aPaintCallback,
                       nsSVGFilterElement *aFilterElement,
                       const gfxRect &aTargetBBox,
                       const gfxRect& aFilterRect,
                       const nsIntSize& aFilterSpaceSize,
                       const gfxMatrix &aFilterSpaceToDeviceSpaceTransform,
+                      const nsIntRect& aTargetBounds,
                       const nsIntRect& aDirtyOutputRect,
                       const nsIntRect& aDirtyInputRect,
                       PRUint16 aPrimitiveUnits) :
     mTargetFrame(aTargetFrame),
     mPaintCallback(aPaintCallback),
     mFilterElement(aFilterElement),
     mTargetBBox(aTargetBBox),
     mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform),
     mFilterRect(aFilterRect),
     mFilterSpaceSize(aFilterSpaceSize),
+    mTargetBounds(aTargetBounds),
     mDirtyOutputRect(aDirtyOutputRect),
     mDirtyInputRect(aDirtyInputRect),
     mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize),
     mPrimitiveUnits(aPrimitiveUnits) {
   }
   
   // The area covered by temporary images, in filter space
   void SetSurfaceRect(const nsIntRect& aRect) { mSurfaceRect = aRect; }
@@ -204,16 +206,18 @@ private:
   nsIFrame*               mTargetFrame;
   nsSVGFilterPaintCallback* mPaintCallback;
   nsSVGFilterElement*     mFilterElement;
   // Bounding box of the target element, in user space
   gfxRect                 mTargetBBox;
   gfxMatrix               mFilterSpaceToDeviceSpaceTransform;
   gfxRect                 mFilterRect;
   nsIntSize               mFilterSpaceSize;
+  // Filter-space bounds of the target image (SourceAlpha/SourceGraphic)
+  nsIntRect               mTargetBounds;
   nsIntRect               mDirtyOutputRect;
   nsIntRect               mDirtyInputRect;
   nsIntRect               mSurfaceRect;
   PRUint16                mPrimitiveUnits;
 
   PrimitiveInfo           mSourceColorAlpha;
   PrimitiveInfo           mSourceAlpha;
   nsTArray<PrimitiveInfo> mPrimitives;