Bug 1459890 - Do not clip mask for clip path to bbox of clipped content. r=mstange
authorBotond Ballo <botond@mozilla.com>
Fri, 11 May 2018 18:55:30 -0400
changeset 796256 ed9f0ddabe14ac91b60da3588a57619ff550113e
parent 796255 8cc35753ba0d96dff834a27664cbcbee468641bd
child 796257 235653928b6570fd2212a93cd1a5c0d90f8c3def
push id110198
push userbmo:rcaliman@mozilla.com
push dateThu, 17 May 2018 12:04:52 +0000
reviewersmstange
bugs1459890
milestone62.0a1
Bug 1459890 - Do not clip mask for clip path to bbox of clipped content. r=mstange MozReview-Commit-ID: 9yZ1ziiDAKa
layout/generic/nsFrame.cpp
layout/reftests/svg/blur-inside-clipPath-ref.svg
layout/reftests/svg/blur-inside-clipPath.svg
layout/reftests/svg/reftest.list
layout/svg/nsSVGClipPathFrame.cpp
layout/svg/nsSVGClipPathFrame.h
layout/svg/nsSVGUtils.cpp
layout/svg/nsSVGUtils.h
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2705,17 +2705,18 @@ ComputeClipForMaskItem(nsDisplayListBuil
     Rect result = nsCSSClipPathInstance::GetBoundingRectForBasicShapeClip(
         aMaskedFrame, svgReset->mClipPath);
     combinedClip = Some(ThebesRect(result));
   } else if (maskUsage.shouldApplyClipPath) {
     gfxRect result = nsSVGUtils::GetBBox(aMaskedFrame,
         nsSVGUtils::eBBoxIncludeClipped |
         nsSVGUtils::eBBoxIncludeFill |
         nsSVGUtils::eBBoxIncludeMarkers |
-        nsSVGUtils::eBBoxIncludeStroke);
+        nsSVGUtils::eBBoxIncludeStroke |
+        nsSVGUtils::eDoNotClipToBBoxOfContentInsideClipPath);
     combinedClip = Some(cssToDevMatrix.TransformBounds(result));
   } else {
     // The code for this case is adapted from ComputeMaskGeometry().
 
     nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
     borderArea -= offsetToUserSpace;
 
     // Use an infinite dirty rect to pass into nsCSSRendering::
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/blur-inside-clipPath-ref.svg
@@ -0,0 +1,12 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="786" viewBox="0,0,512,512">
+	<defs>
+		<filter id="filter-wVmTgUOU" filterUnits="objectBoundingBox" x="-30%" y="-30%" width="160%" height="170%" color-interpolation-filters="sRGB">
+			<feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" in="SourceGraphic"/>
+			<feOffset dy="28"/>
+			<feGaussianBlur stdDeviation="18.67" result="blur1"/>
+		</filter>
+	</defs>
+  <g filter="url(#filter-wVmTgUOU)">
+    <rect x="58.88" y="58.88" width="394.24" height="394.24" color="#07773e" fill="red"/>
+  </g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/blur-inside-clipPath.svg
@@ -0,0 +1,17 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="786" viewBox="0,0,512,512">
+	<defs>
+		<clipPath id="clip-vXP8Ybe5">
+			<path d="M0,512v-512h512v512z"/>
+		</clipPath>		
+		<filter id="filter-wVmTgUOU" filterUnits="objectBoundingBox" x="-30%" y="-30%" width="160%" height="170%" color-interpolation-filters="sRGB">
+			<feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" in="SourceGraphic"/>
+			<feOffset dy="28"/>
+			<feGaussianBlur stdDeviation="18.67" result="blur1"/>
+		</filter>
+	</defs>
+  <g clip-path="url(#clip-vXP8Ybe5)">
+    <g filter="url(#filter-wVmTgUOU)">
+      <rect x="58.88" y="58.88" width="394.24" height="394.24" color="#07773e" fill="red"/>
+    </g>
+  </g>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -50,16 +50,18 @@ skip-if(Android) pref(layout.css.mix-ble
 skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-multiply.svg blend-multiply-ref.svg
 pref(layout.css.mix-blend-mode.enabled,true) == blend-normal.svg blend-normal-ref.svg
 #skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-overlay.svg blend-overlay-ref.svg
 #skip-if(Android)  pref(layout.css.mix-blend-mode.enabled,true) == blend-saturation.svg blend-saturation-ref.svg
 #skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-screen.svg blend-screen-ref.svg
 #skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-soft-light.svg blend-soft-light-ref.svg
 skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) fails-if(webrender) == blend-difference-stacking.html blend-difference-stacking-ref.html
 
+fuzzy(11,7155) == blur-inside-clipPath.svg blur-inside-clipPath-ref.svg
+
 == border-radius-01.html pass.svg
 
 == clip-01.svg pass.svg
 == clip-02a.svg clip-02-ref.svg
 == clip-02b.svg clip-02-ref.svg
 == clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
 == clip-use-element-01.svg pass.svg
 == clip-use-element-02.svg pass.svg
--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -470,17 +470,18 @@ nsSVGClipPathFrame::GetClipPathTransform
       : 0);
 
   return nsSVGUtils::AdjustMatrixForUnits(tm, clipPathUnits,
                                           aClippedFrame, flags);
 }
 
 SVGBBox
 nsSVGClipPathFrame::GetBBoxForClipPathFrame(const SVGBBox &aBBox,
-                                            const gfxMatrix &aMatrix)
+                                            const gfxMatrix &aMatrix,
+                                            uint32_t aFlags)
 {
   nsIContent* node = GetContent()->GetFirstChild();
   SVGBBox unionBBox, tmpBBox;
   for (; node; node = node->GetNextSibling()) {
     nsSVGElement* svgNode = static_cast<nsSVGElement*>(node);
     nsIFrame* frame = svgNode->GetPrimaryFrame();
     if (frame) {
       nsSVGDisplayableFrame* svg = do_QueryFrame(frame);
@@ -489,32 +490,34 @@ nsSVGClipPathFrame::GetBBoxForClipPathFr
         tmpBBox = svg->GetBBoxContribution(mozilla::gfx::ToMatrix(matrix),
                                            nsSVGUtils::eBBoxIncludeFill);
         SVGObserverUtils::EffectProperties effectProperties =
                               SVGObserverUtils::GetEffectProperties(frame);
         if (effectProperties.HasNoOrValidClipPath()) {
           nsSVGClipPathFrame *clipPathFrame =
             effectProperties.GetClipPathFrame();
           if (clipPathFrame) {
-            tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(tmpBBox, aMatrix);
+            tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(tmpBBox, aMatrix, aFlags);
           }
         }
-        tmpBBox.Intersect(aBBox);
+        if (!(aFlags & nsSVGUtils::eDoNotClipToBBoxOfContentInsideClipPath)) {
+          tmpBBox.Intersect(aBBox);
+        }
         unionBBox.UnionEdges(tmpBBox);
       }
     }
   }
 
   SVGObserverUtils::EffectProperties props =
     SVGObserverUtils::GetEffectProperties(this);
   if (props.mClipPath) {
     if (props.HasInvalidClipPath()) {
       unionBBox = SVGBBox();
     } else  {
       nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame();
       if (clipPathFrame) {
-        tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(aBBox, aMatrix);
+        tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(aBBox, aMatrix, aFlags);
         unionBBox.Intersect(tmpBBox);
       }
     }
   }
   return unionBBox;
 }
--- a/layout/svg/nsSVGClipPathFrame.h
+++ b/layout/svg/nsSVGClipPathFrame.h
@@ -127,17 +127,18 @@ public:
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGClipPath"), aResult);
   }
 #endif
 
   SVGBBox GetBBoxForClipPathFrame(const SVGBBox& aBBox,
-                                  const gfxMatrix& aMatrix);
+                                  const gfxMatrix& aMatrix,
+                                  uint32_t aFlags);
 
   /**
    * If the clipPath element transforms its children due to
    * clipPathUnits="objectBoundingBox" being set on it and/or due to the
    * 'transform' attribute being set on it, this function returns the resulting
    * transform.
    */
   gfxMatrix GetClipPathTransform(nsIFrame* aClippedFrame);
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1176,17 +1176,17 @@ nsSVGUtils::GetBBox(nsIFrame* aFrame, ui
         if (clipContent->IsUnitsObjectBoundingBox()) {
           matrix.PreTranslate(gfxPoint(x, y));
           matrix.PreScale(width, height);
         } else if (aFrame->IsSVGForeignObjectFrame()) {
           matrix = gfxMatrix();
         }
         matrix = clipContent->PrependLocalTransformsTo(matrix, eUserSpaceToParent);
         bbox =
-          clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix).ToThebesRect();
+          clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix, aFlags).ToThebesRect();
       }
 
       if (hasClip) {
         bbox = bbox.Intersect(clipRect);
       }
 
       if (bbox.IsEmpty()) {
         bbox = gfxRect(0, 0, 0, 0);
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -407,16 +407,19 @@ public:
     // given frame, instead of all continuations of it, while computing bbox if
     // this flag is set.
     eIncludeOnlyCurrentFrameForNonSVGElement = 1 << 8,
     // This flag is only has an effect when the target is a <use> element.
     // getBBox returns the bounds of the elements children in user space if
     // this flag is set; Otherwise, getBBox returns the union bounds in
     // the coordinate system formed by the <use> element.
     eUseUserSpaceOfUseElement = 1 << 9,
+    // For a frame with a clip-path, if this flag is set then the result
+    // will not be clipped to the bbox of the content inside the clip-path.
+    eDoNotClipToBBoxOfContentInsideClipPath = 1 << 10,
   };
   /**
    * This function in primarily for implementing the SVG DOM function getBBox()
    * and the SVG attribute value 'objectBoundingBox'.  However, it has been
    * extended with various extra parameters in order to become more of a
    * general purpose getter of all sorts of bounds that we might need to obtain
    * for SVG elements, or even for other elements that have SVG effects applied
    * to them.