Bug 948265 - Clip filter primitives to their filter's filter region. r=mstange
authorMax Vujovic <mvujovic@adobe.com>
Wed, 25 Jun 2014 14:14:00 -0400
changeset 191234 a368ab7e93ab4f4d1e7b352316004c7bb9bf2e70
parent 191233 4b8680a3302d49e02c368cc2e30154c989d95a8b
child 191235 c252faf035c50a0ce73e4801574b2639bb8ad17c
push id27037
push userkwierso@gmail.com
push dateSat, 28 Jun 2014 00:41:04 +0000
treeherdermozilla-central@286b6cb59c3e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs948265
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 948265 - Clip filter primitives to their filter's filter region. r=mstange
layout/reftests/svg/filters/svg-filter-chains/clip-input-ref.svg
layout/reftests/svg/filters/svg-filter-chains/clip-input.svg
layout/reftests/svg/filters/svg-filter-chains/clip-output-ref.svg
layout/reftests/svg/filters/svg-filter-chains/clip-output.svg
layout/reftests/svg/filters/svg-filter-chains/default-subregion-ref.svg
layout/reftests/svg/filters/svg-filter-chains/default-subregion.svg
layout/reftests/svg/filters/svg-filter-chains/intersecting-filter-regions-ref.svg
layout/reftests/svg/filters/svg-filter-chains/intersecting-filter-regions.svg
layout/reftests/svg/filters/svg-filter-chains/reftest.list
layout/svg/nsSVGFilterInstance.cpp
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/clip-input-ref.svg
@@ -0,0 +1,22 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Clip Input Filter</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <metadata class="flags">namespace svg</metadata>
+  </g>
+
+  <g id="test-body-content">
+    <rect x="0" y="0" width="100" height="100" fill="green"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/clip-input.svg
@@ -0,0 +1,48 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Clip Input Filter</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <link rel="match"
+          href="clip-input-ref.svg" />
+    <metadata class="flags">namespace svg</metadata>
+    <desc class="assert">
+      In an SVG filter chain, this test verifies that a filter region clips a
+      SourceGraphic input filter. If the test passes, you should see a green
+      square.
+    </desc>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="f1" x="100" y="0" width="100" height="100" filterUnits="userSpaceOnUse">
+      <!-- Create a red square at x=100. -->
+      <feFlood flood-color="red"/>
+    </filter>
+    <filter id="f2" x="0" y="0" width="100" height="100" filterUnits="userSpaceOnUse">
+      <!-- Create a green square at x=0. -->
+      <feFlood result="green" flood-color="green"/>
+      <!--
+          Attempt to offset the red square left to cover up the green square.
+          However, this filter's filter region should clip away the red square,
+          and only transparent pixels should be offset left, leaving the green
+          square intact.
+      -->
+      <feOffset result="red" in="SourceGraphic" dx="-100" x="0" y="0" width="200" height="100"/>
+      <feMerge>
+          <feMergeNode in="green"/>
+          <feMergeNode in="red"/>
+      </feMerge>
+    </filter>
+    <rect x="0" y="0" width="100" height="100" filter="url(#f1) url(#f2)"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/clip-output-ref.svg
@@ -0,0 +1,26 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Clip Filter Output</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <metadata class="flags">namespace svg</metadata>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="hue-rotate">
+      <!-- Turn the red square green. -->
+      <feColorMatrix type="hueRotate" values="90" style="color-interpolation-filters:sRGB"/>
+    </filter>
+    <rect x="0" y="0" width="100" height="100" filter="url(#hue-rotate)" fill="red"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/clip-output.svg
@@ -0,0 +1,48 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Clip Filter Output</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <link rel="match"
+          href="clip-output-ref.svg" />
+    <metadata class="flags">namespace svg</metadata>
+    <desc class="assert">
+      In an SVG filter chain, this test verifies a filter region clips its
+      filter's output into the next filter. If the test passes, you should see a
+      green square.
+    </desc>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="flood" x="0" y="0" width="100" height="100" filterUnits="userSpaceOnUse">
+      <!--
+        Create a red square followed by a blue square. The blue square should be
+        clipped away due to this filter's filter region.
+      -->
+      <feFlood result="red" flood-color="red" x="0" y="0" width="100" height="100"/>
+      <feFlood result="blue" flood-color="blue" x="100" y="0" width="100" height="100"/>
+      <feMerge>
+          <feMergeNode in="red"/>
+          <feMergeNode in="blue"/>
+      </feMerge>
+    </filter>
+    <filter id="hue-rotate" x="0" y="0" width="200" height="100" filterUnits="userSpaceOnUse">
+      <!--
+        Turn the red square green. If the blue square wasn't clipped by the
+        previous filter's filter region, it will turn red.
+       -->
+      <feColorMatrix type="hueRotate" values="90" style="color-interpolation-filters:sRGB"/>
+    </filter>
+    <rect x="0" y="0" width="100" height="100" filter="url(#flood) url(#hue-rotate)"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/default-subregion-ref.svg
@@ -0,0 +1,25 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Default Filter Primitive Subregion</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <metadata class="flags">namespace svg</metadata>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="f1" x="50" y="50" width="200" height="200" filterUnits="userSpaceOnUse">
+      <feGaussianBlur stdDeviation="10"/>
+    </filter>
+    <rect x="100" y="100" width="100" height="100" filter="url(#f1)" fill="green"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/default-subregion.svg
@@ -0,0 +1,44 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Default Filter Primitive Subregion</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <link rel="match"
+          href="default-subregion-ref.svg" />
+    <metadata class="flags">namespace svg</metadata>
+    <desc class="assert">
+      In an SVG filter chain, this test verifies that the default filter
+      primitive subregion is equal to the filter region. If the test passes,
+      you should see a blurred green square.
+    </desc>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="f1" x="100" y="100" width="100" height="100" filterUnits="userSpaceOnUse">
+      <!-- Fill a 100x100 square with green. -->
+      <feFlood flood-color="green"/>
+    </filter>
+    <filter id="f2" x="50" y="50" width="200" height="200" filterUnits="userSpaceOnUse">
+      <!--
+        This feGaussianBlur primitive blurs the 100x100 green square from the
+        previous filter. It does not explicitly define a filter primitive
+        subregion, so its subregion should equal the filter region. The filter
+        region has plenty of space for the blur outsets, so the blur should not
+        appear clipped. If the blur incorrectly uses a primitive subregion or
+        filter region from a previous filter, the blur may appear clipped.
+      -->
+      <feGaussianBlur stdDeviation="10"/>
+    </filter>
+    <rect x="100" y="100" width="100" height="100" filter="url(#f1) url(#f2)" fill="red"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/intersecting-filter-regions-ref.svg
@@ -0,0 +1,25 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Intersecting Filter Regions</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <metadata class="flags">namespace svg</metadata>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="hue-rotate">
+      <feColorMatrix type="hueRotate" values="90" style="color-interpolation-filters:sRGB"/>
+    </filter>
+    <rect x="100" y="100" width="100" height="100" filter="url(#hue-rotate)" fill="red"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/intersecting-filter-regions.svg
@@ -0,0 +1,45 @@
+<svg id="svg-root"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
+
+  <g id="testmeta">
+    <title>SVG Filter Chains: Intersecting Filter Regions</title>
+    <link rel="copyright"
+          href="http://www.w3.org/Graphics/SVG/Test/Copyright"/>
+    <link rel="license"
+          href="http://www.w3.org/Consortium/Legal/2008/03-bsd-license.html"/>
+    <link rel="author"
+          title="Max Vujovic"
+          href="mailto:mvujovic@adobe.com"/>
+    <link rel="help"
+          href="http://dev.w3.org/fxtf/filters/#FilterPrimitiveSubRegion"/>
+    <link rel="match"
+          href="intersecting-filter-regions-ref.svg" />
+    <metadata class="flags">namespace svg</metadata>
+    <desc class="assert">
+      In an SVG filter chain, this test verifies that filters with intersecting
+      filter regions render properly. If the test passes, you should see a green
+      square.
+    </desc>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="flood" x="0" y="0" width="200" height="200" filterUnits="userSpaceOnUse">
+      <!--
+        This filter clips the SourceGraphic to its top left corner and fills it
+        with red.
+      -->
+      <feFlood flood-color="red"/>
+    </filter>
+    <filter id="hue-rotate" x="100" y="100" width="200" height="200" filterUnits="userSpaceOnUse">
+      <!--
+        This filter clips the output of the previous filter to the bottom right
+        corner, and it changes red into green. If the previous filter didn't run
+        or it didn't clip the SourceGraphic, this filter will change the
+        SourceGraphic's blue fill into red.
+      -->
+      <feColorMatrix type="hueRotate" values="90" style="color-interpolation-filters:sRGB"/>
+    </filter>
+    <rect x="0" y="0" width="300" height="300" filter="url(#flood) url(#hue-rotate)" fill="blue"/>
+  </g>
+</svg>
--- a/layout/reftests/svg/filters/svg-filter-chains/reftest.list
+++ b/layout/reftests/svg/filters/svg-filter-chains/reftest.list
@@ -1,9 +1,13 @@
 # These tests verify that SVG filter chains behave properly.
 # e.g. filter: url(#f1) url(#f2) url(#f3)
 
 default-preferences pref(layout.css.filters.enabled,true)
 
+== clip-input.svg clip-input-ref.svg
+== clip-output.svg clip-output.svg
+== default-subregion.svg default-subregion-ref.svg
+== intersecting-filter-regions.svg intersecting-filter-regions-ref.svg
 == long-chain.svg simple-chain-ref.svg
 == multiple-primitives-per-filter.svg simple-chain-ref.svg
 == second-filter-uses-SourceGraphic.svg simple-chain-ref.svg
 == simple-chain.svg simple-chain-ref.svg
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -224,19 +224,20 @@ nsSVGFilterInstance::ComputeFilterPrimit
                                                      const nsTArray<int32_t>& aInputIndices)
 {
   nsSVGFE* fE = aFilterElement;
 
   IntRect defaultFilterSubregion(0,0,0,0);
   if (fE->SubregionIsUnionOfRegions()) {
     for (uint32_t i = 0; i < aInputIndices.Length(); ++i) {
       int32_t inputIndex = aInputIndices[i];
-      IntRect inputSubregion = inputIndex >= 0 ?
-        aPrimitiveDescrs[inputIndex].PrimitiveSubregion() :
-        ToIntRect(mFilterSpaceBounds);
+      bool isStandardInput = inputIndex < 0 || inputIndex == mSourceGraphicIndex;
+      IntRect inputSubregion = isStandardInput ?
+        ToIntRect(mFilterSpaceBounds) :
+        aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
 
       defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion);
     }
   } else {
     defaultFilterSubregion = ToIntRect(mFilterSpaceBounds);
   }
 
   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
@@ -251,18 +252,20 @@ nsSVGFilterInstance::ComputeFilterPrimit
     region.width = defaultFilterSubregion.Width();
   if (!fE->mLengthAttributes[nsSVGFE::ATTR_HEIGHT].IsExplicitlySet())
     region.height = defaultFilterSubregion.Height();
 
   // We currently require filter primitive subregions to be pixel-aligned.
   // Following the spec, any pixel partially in the region is included
   // in the region.
   region.RoundOut();
+  IntRect regionInt = RoundedToInt(region);
 
-  return RoundedToInt(region);
+  // Clip the primitive subregion to this filter's filter region.
+  return regionInt.Intersect(ToIntRect(mFilterSpaceBounds));
 }
 
 void
 nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                                          const nsTArray<int32_t>& aInputIndices,
                                          nsTArray<bool>& aOutInputsAreTainted)
 {
   for (uint32_t i = 0; i < aInputIndices.Length(); i++) {