Bug 1287492 - Part 3. (Main) Shrink mTargetBBoxInFilterSpace r=mstange
authorcku <cku@mozilla.com>
Thu, 09 Feb 2017 23:17:43 +0800
changeset 341746 c8fd0f9baf346181cf5fa8befdaeae9dac6394b0
parent 341745 158d2fb726da084da6089ef269925bc7211a99a0
child 341747 b209aadec5648cf28b08c548dd47f42f558b0dce
push id37205
push usercku@mozilla.com
push dateFri, 10 Feb 2017 02:46:16 +0000
treeherderautoland@b209aadec564 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1287492, 20000
milestone54.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 1287492 - Part 3. (Main) Shrink mTargetBBoxInFilterSpace r=mstange Clip mTargetBBoxInFilterSpace by the bounds of parent SVG frame. Fix this bug and good for both rendering performance and memory consumption. The root cause of this bug <svg width="100" height="100" style="filter: opacity(100%);"> <g transform="matrix(200,0,0,200,-20000,-20000)"> <rect width="200" height="200" style="fill:lime"> </g> </svg> In this example, <rect> is going to be a huge graphic object because of the scale transform in <g>. The bounding-box of <svg> is an union of all descedants, so the size of mTargetBBoxInFilterSpace is huage too. We are not able to create such a huge surface because of the limitation at nsFilterInstance::OutputFilterSpaceBounds[1]. [1] https://hg.mozilla.org/mozilla-central/file/f4f6790e3926/layout/svg/nsFilterInstance.cpp#l556 MozReview-Commit-ID: 4Fdj5mgcE0V
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -181,30 +181,25 @@ nsFilterInstance::nsFilterInstance(nsIFr
   if (aOverrideBBox) {
     mTargetBBox = *aOverrideBBox;
   } else {
     MOZ_ASSERT(mTargetFrame, "Need to supply a frame when there's no aOverrideBBox");
     mTargetBBox = nsSVGUtils::GetBBox(mTargetFrame);
   }
 
   // Compute user space to filter space transforms.
-  nsresult rv = ComputeUserSpaceToFilterSpaceScale();
-  if (NS_FAILED(rv)) {
+  if (!ComputeUserSpaceToFilterSpaceScale()) {
     return;
   }
 
-  gfxRect targetBBoxInFilterSpace = UserSpaceToFilterSpace(mTargetBBox);
-  targetBBoxInFilterSpace.RoundOut();
-  if (!gfxUtils::GfxRectToIntRect(targetBBoxInFilterSpace, &mTargetBBoxInFilterSpace)) {
-    // The target's bbox is way too big if there is float->int overflow.
+  if (!ComputeTargetBBoxInFilterSpace()) {
     return;
   }
 
   // Get various transforms:
-
   gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f,
                               0.0f, mFilterSpaceToUserSpaceScale.height,
                               0.0f, 0.0f);
 
   mFilterSpaceToFrameSpaceInCSSPxTransform =
     filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
   // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
   mFrameSpaceInCSSPxToFilterSpaceTransform =
@@ -217,47 +212,79 @@ nsFilterInstance::nsFilterInstance(nsIFr
       FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride);
   } else if (mTargetFrame) {
     nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect();
     targetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
   }
   mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
 
   // Build the filter graph.
-  rv = BuildPrimitives(aFilterChain, aTargetFrame, aFilterInputIsTainted);
-  if (NS_FAILED(rv)) {
+  if (NS_FAILED(BuildPrimitives(aFilterChain, aTargetFrame,
+                                aFilterInputIsTainted))) {
     return;
   }
 
   // Convert the passed in rects from frame space to filter space:
   mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion);
   mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
 
   mInitialized = true;
 }
 
-nsresult
+bool
+nsFilterInstance::ComputeTargetBBoxInFilterSpace()
+{
+  gfxRect targetBBoxInFilterSpace = UserSpaceToFilterSpace(mTargetBBox);
+  targetBBoxInFilterSpace.RoundOut();
+  if (!gfxUtils::GfxRectToIntRect(targetBBoxInFilterSpace,
+                                  &mTargetBBoxInFilterSpace)) {
+    // The target's bbox is way too big if there is float->int overflow.
+    return false;
+  }
+
+  if (!mTargetFrame || !mTargetFrame->IsFrameOfType(nsIFrame::eSVG)) {
+    return true;
+  }
+
+  // SVG graphic elements will always be clipped by svg::svg element, so we
+  // should clip mTargetBBoxInFilterSpace by the bounded parent SVG frame
+  // anyway to shrink the size of surface that we are going to create later in
+  // BuildSourcePaint and BuildSourceImage.
+  MOZ_ASSERT(mTargetFrame->IsFrameOfType(nsIFrame::eSVG));
+  nsIFrame* svgFrame = nsSVGUtils::GetNearestSVGParent(mTargetFrame);
+  if (svgFrame) {
+    nscoord A2D = svgFrame->PresContext()->AppUnitsPerCSSPixel();
+    nsIntRect bounds =
+      svgFrame->GetVisualOverflowRect().ToOutsidePixels(A2D);
+
+    mTargetBBoxInFilterSpace = mTargetBBoxInFilterSpace.Intersect(bounds);
+  }
+
+  return true;
+}
+
+bool
 nsFilterInstance::ComputeUserSpaceToFilterSpaceScale()
 {
   gfxMatrix canvasTransform;
   if (mTargetFrame) {
     mUserSpaceToFilterSpaceScale = mPaintTransform.ScaleFactors(true);
     if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
         mUserSpaceToFilterSpaceScale.height <= 0.0f) {
       // Nothing should be rendered.
-      return NS_ERROR_FAILURE;
+      return false;
     }
   } else {
     mUserSpaceToFilterSpaceScale = gfxSize(1.0, 1.0);
   }
 
   mFilterSpaceToUserSpaceScale =
     gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
             1.0f / mUserSpaceToFilterSpaceScale.height);
-  return NS_OK;
+  return true;
 }
 
 gfxRect
 nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
 {
   gfxRect filterSpaceRect = aUserSpaceRect;
   filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
                         mUserSpaceToFilterSpaceScale.height);
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -265,17 +265,17 @@ private:
   /**
    * Returns the output bounds of the final FilterPrimitiveDescription.
    */
   nsIntRect OutputFilterSpaceBounds() const;
 
   /**
    * Compute the scale factors between user space and filter space.
    */
-  nsresult ComputeUserSpaceToFilterSpaceScale();
+  bool ComputeUserSpaceToFilterSpaceScale();
 
   /**
    * Transform a rect between user space and filter space.
    */
   gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const;
   gfxRect FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const;
 
   /**
@@ -298,16 +298,18 @@ 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;
 
+  bool ComputeTargetBBoxInFilterSpace();
+
   /**
    * The frame for the element that is currently being filtered.
    */
   nsIFrame* mTargetFrame;
 
   /**
    * The filtered element.
    */