Bug 948265 - Support SourceAlpha keyword in SVG filter chains. r=mstange
authorMax Vujovic <mvujovic@adobe.com>
Tue, 15 Jul 2014 11:37:00 +0200
changeset 195827 b80f4c9913c398a50957e4f212d7c86bd2e90e80
parent 195826 2ce5f7e7132b465decab64f40096ac985d0f2508
child 195828 55b20adc6177bf8d6c8c8a4efa319ec0e51c74a8
push id27197
push usercbook@mozilla.com
push dateThu, 24 Jul 2014 13:25:44 +0000
treeherdermozilla-central@340ff53a5467 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs948265
milestone34.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 - Support SourceAlpha keyword in SVG filter chains. r=mstange
gfx/src/FilterSupport.cpp
gfx/src/FilterSupport.h
layout/reftests/svg/filters/svg-filter-chains/reftest.list
layout/reftests/svg/filters/svg-filter-chains/second-filter-uses-SourceAlpha-ref.svg
layout/reftests/svg/filters/svg-filter-chains/second-filter-uses-SourceAlpha.svg
layout/svg/nsSVGFilterInstance.cpp
layout/svg/nsSVGFilterInstance.h
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -961,16 +961,21 @@ FilterNodeFromPrimitiveDescription(const
 
       RefPtr<FilterNode> transform = aDT->CreateFilter(FilterType::TRANSFORM);
       transform->SetInput(IN_TRANSFORM_IN, inputImage);
       transform->SetAttribute(ATT_TRANSFORM_MATRIX, TM);
       transform->SetAttribute(ATT_TRANSFORM_FILTER, atts.GetUint(eImageFilter));
       return transform;
     }
 
+    case PrimitiveType::ToAlpha:
+    {
+      return FilterWrappers::ToAlpha(aDT, aSources[0]);
+    }
+
     default:
       return nullptr;
   }
 }
 
 template<typename T>
 static const T&
 ElementForIndex(int32_t aIndex,
@@ -996,16 +1001,17 @@ ElementForIndex(int32_t aIndex,
 static AlphaModel
 InputAlphaModelForPrimitive(const FilterPrimitiveDescription& aDescr,
                             int32_t aInputIndex,
                             AlphaModel aOriginalAlphaModel)
 {
   switch (aDescr.Type()) {
     case PrimitiveType::Tile:
     case PrimitiveType::Offset:
+    case PrimitiveType::ToAlpha:
       return aOriginalAlphaModel;
 
     case PrimitiveType::ColorMatrix:
     case PrimitiveType::ComponentTransfer:
       return AlphaModel::Unpremultiplied;
 
     case PrimitiveType::DisplacementMap:
       return aInputIndex == 0 ?
@@ -1191,16 +1197,17 @@ ResultChangeRegionForPrimitive(const Fil
 
     case PrimitiveType::Blend:
     case PrimitiveType::Composite:
     case PrimitiveType::Merge:
       return UnionOfRegions(aInputChangeRegions);
 
     case PrimitiveType::ColorMatrix:
     case PrimitiveType::ComponentTransfer:
+    case PrimitiveType::ToAlpha:
       return aInputChangeRegions[0];
 
     case PrimitiveType::Morphology:
     {
       Size radii = atts.GetSize(eMorphologyRadii);
       int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
       int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
       return aInputChangeRegions[0].Inflated(nsIntMargin(ry, rx, ry, rx));
@@ -1420,16 +1427,17 @@ SourceNeededRegionForPrimitive(const Fil
     case PrimitiveType::Empty:
       return nsIntRegion();
 
     case PrimitiveType::Blend:
     case PrimitiveType::Composite:
     case PrimitiveType::Merge:
     case PrimitiveType::ColorMatrix:
     case PrimitiveType::ComponentTransfer:
+    case PrimitiveType::ToAlpha:
       return aResultNeededRegion;
 
     case PrimitiveType::Morphology:
     {
       Size radii = atts.GetSize(eMorphologyRadii);
       int32_t rx = clamped(int32_t(ceil(radii.width)), 0, kMorphologyMaxRadius);
       int32_t ry = clamped(int32_t(ceil(radii.height)), 0, kMorphologyMaxRadius);
       return aResultNeededRegion.Inflated(nsIntMargin(ry, rx, ry, rx));
--- a/gfx/src/FilterSupport.h
+++ b/gfx/src/FilterSupport.h
@@ -265,16 +265,17 @@ MOZ_BEGIN_ENUM_CLASS(PrimitiveType)
   Turbulence,
   Composite,
   Merge,
   Image,
   GaussianBlur,
   DropShadow,
   DiffuseLighting,
   SpecularLighting,
+  ToAlpha,
   Max
 MOZ_END_ENUM_CLASS(PrimitiveType)
 
 /**
  * A data structure to carry attributes for a given primitive that's part of a
  * filter. Will be serializable via IPDL, so it must not contain complex
  * functionality.
  * Used as part of a FilterDescription.
--- a/layout/reftests/svg/filters/svg-filter-chains/reftest.list
+++ b/layout/reftests/svg/filters/svg-filter-chains/reftest.list
@@ -4,10 +4,11 @@
 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-SourceAlpha.svg second-filter-uses-SourceAlpha.svg
 == second-filter-uses-SourceGraphic.svg simple-chain-ref.svg
 == simple-chain.svg simple-chain-ref.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/second-filter-uses-SourceAlpha-ref.svg
@@ -0,0 +1,29 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<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: Second Filter Uses SourceAlpha</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="blur">
+      <feGaussianBlur stdDeviation="3"/>
+    </filter>
+    <rect x="100" y="100" width="100" height="100" filter="url(#blur)" fill="#00ff00"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/svg-filter-chains/second-filter-uses-SourceAlpha.svg
@@ -0,0 +1,49 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<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: Second Filter Uses SourceAlpha</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="second-filter-uses-SourceAlpha.svg" />
+    <metadata class="flags">namespace svg</metadata>
+    <desc class="assert">
+      In an SVG filter chain, this test verifies that a filter receives the
+      correct SourceAlpha input from the previous filter in the chain. If the
+      test passes, you should see a blurred green square.
+    </desc>
+  </g>
+
+  <g id="test-body-content">
+    <filter id="blur">
+      <feGaussianBlur stdDeviation="3"/>
+    </filter>
+    <filter id="add-green">
+      <!--
+        This filter receives transparent black and the alpha channel of the
+        previous blur filter. Then, it adds to the green channel where the alpha
+        channel is set, resulting in a blurred green square.
+      -->
+      <feComponentTransfer in="SourceAlpha">
+        <feFuncR type="identity"/>
+        <feFuncG type="table" tableValues="1 1"/>
+        <feFuncB type="identity"/>
+        <feFuncA type="identity"/>
+      </feComponentTransfer>
+    </filter>
+    <rect x="100" y="100" width="100" height="100" filter="url(#blur) url(#add-green)" fill="red"/>
+  </g>
+</svg>
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -29,16 +29,17 @@ nsSVGFilterInstance::nsSVGFilterInstance
                                          const gfxRect& aTargetBBox,
                                          const gfxSize& aUserSpaceToFilterSpaceScale,
                                          const gfxSize& aFilterSpaceToUserSpaceScale) :
   mFilter(aFilter),
   mTargetFrame(aTargetFrame),
   mTargetBBox(aTargetBBox),
   mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale),
   mFilterSpaceToUserSpaceScale(aFilterSpaceToUserSpaceScale),
+  mSourceAlphaAvailable(false),
   mInitialized(false) {
 
   // Get the filter frame.
   mFilterFrame = GetFilterFrame();
   if (!mFilterFrame) {
     return;
   }
 
@@ -283,34 +284,71 @@ static int32_t
 GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
 {
   uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
   return !numPrimitiveDescrs ?
     FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
     numPrimitiveDescrs - 1;
 }
 
+int32_t
+nsSVGFilterInstance::GetOrCreateSourceAlphaIndex(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
+{
+  // If the SourceAlpha index has already been determined or created for this
+  // SVG filter, just return it.
+  if (mSourceAlphaAvailable)
+    return mSourceAlphaIndex;
+
+  // If this is the first filter in the chain, we can just use the
+  // kPrimitiveIndexSourceAlpha keyword to refer to the SourceAlpha of the
+  // original image.
+  if (mSourceGraphicIndex < 0) {
+    mSourceAlphaIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha;
+    mSourceAlphaAvailable = true;
+    return mSourceAlphaIndex;
+  }
+
+  // Otherwise, create a primitive description to turn the previous filter's
+  // output into a SourceAlpha input.
+  FilterPrimitiveDescription descr(PrimitiveType::ToAlpha);
+  descr.SetInputPrimitive(0, mSourceGraphicIndex);
+
+  const FilterPrimitiveDescription& sourcePrimitiveDescr =
+    aPrimitiveDescrs[mSourceGraphicIndex];
+  descr.SetPrimitiveSubregion(sourcePrimitiveDescr.PrimitiveSubregion());
+  descr.SetIsTainted(sourcePrimitiveDescr.IsTainted());
+
+  ColorSpace colorSpace = sourcePrimitiveDescr.OutputColorSpace();
+  descr.SetInputColorSpace(0, colorSpace);
+  descr.SetOutputColorSpace(colorSpace);
+
+  aPrimitiveDescrs.AppendElement(descr);
+  mSourceAlphaIndex = aPrimitiveDescrs.Length() - 1;
+  mSourceAlphaAvailable = true;
+  return mSourceAlphaIndex;
+}
+
 nsresult
 nsSVGFilterInstance::GetSourceIndices(nsSVGFE* aPrimitiveElement,
-                                      const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                                      nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                                       const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
                                       nsTArray<int32_t>& aSourceIndices)
 {
   nsAutoTArray<nsSVGStringInfo,2> sources;
   aPrimitiveElement->GetSourceImageNames(sources);
 
   for (uint32_t j = 0; j < sources.Length(); j++) {
     nsAutoString str;
     sources[j].mString->GetAnimValue(str, sources[j].mElement);
 
     int32_t sourceIndex = 0;
     if (str.EqualsLiteral("SourceGraphic")) {
       sourceIndex = mSourceGraphicIndex;
     } else if (str.EqualsLiteral("SourceAlpha")) {
-      sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha;
+      sourceIndex = GetOrCreateSourceAlphaIndex(aPrimitiveDescrs);
     } else if (str.EqualsLiteral("FillPaint")) {
       sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexFillPaint;
     } else if (str.EqualsLiteral("StrokePaint")) {
       sourceIndex = FilterPrimitiveDescription::kPrimitiveIndexStrokePaint;
     } else if (str.EqualsLiteral("BackgroundImage") ||
                str.EqualsLiteral("BackgroundAlpha")) {
       return NS_ERROR_NOT_IMPLEMENTED;
     } else if (str.EqualsLiteral("")) {
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -169,24 +169,34 @@ private:
   /**
    * Returns the transform from frame space to the coordinate space that
    * GetCanvasTM transforms to. "Frame space" is the origin of a frame, aka the
    * top-left corner of its border box, aka the top left corner of its mRect.
    */
   gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const;
 
   /**
+   * Appends a new FilterPrimitiveDescription to aPrimitiveDescrs that
+   * converts the FilterPrimitiveDescription at mSourceGraphicIndex into
+   * a SourceAlpha input for the next FilterPrimitiveDescription.
+   *
+   * The new FilterPrimitiveDescription zeros out the SourceGraphic's RGB
+   * channels and keeps the alpha channel intact.
+   */
+  int32_t GetOrCreateSourceAlphaIndex(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs);
+
+  /**
    * Finds the index in aPrimitiveDescrs of each input to aPrimitiveElement.
    * For example, if aPrimitiveElement is:
    *   <feGaussianBlur in="another-primitive" .../>
    * Then, the resulting aSourceIndices will contain the index of the
    * FilterPrimitiveDescription representing "another-primitive".
    */
   nsresult GetSourceIndices(nsSVGFE* aPrimitiveElement,
-                            const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
+                            nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                             const nsDataHashtable<nsStringHashKey, int32_t>& aImageTable,
                             nsTArray<int32_t>& aSourceIndices);
 
   /**
    * Compute the filter region in user space, filter space, and filter
    * space.
    */
   nsresult ComputeBounds();
@@ -235,12 +245,24 @@ private:
 
   /**
    * The index of the FilterPrimitiveDescription that this SVG filter should use
    * as its SourceGraphic, or the SourceGraphic keyword index if this is the
    * first filter in a chain.
    */
   int32_t mSourceGraphicIndex;
 
+  /**
+   * The index of the FilterPrimitiveDescription that this SVG filter should use
+   * as its SourceAlpha, or the SourceAlpha keyword index if this is the first
+   * filter in a chain.
+   */
+  int32_t mSourceAlphaIndex;
+
+  /**
+   * SourceAlpha is available if GetOrCreateSourceAlphaIndex has been called.
+   */
+  int32_t mSourceAlphaAvailable;
+
   bool                    mInitialized;
 };
 
 #endif