Bug 1287492 - Part 3. (Main) Shrink mTargetBBoxInFilterSpace draft
authorcku <cku@mozilla.com>
Thu, 09 Feb 2017 23:17:43 +0800
changeset 481581 ea54d04b0660c81f50b3c3899a6aabb356edbc34
parent 481261 9407f887b938da7d2fd99c078e20568272f75400
child 481582 640a35a4982042bd1efc859b227b6a78060ac240
push id44853
push userbmo:cku@mozilla.com
push dateFri, 10 Feb 2017 02:37:59 +0000
bugs1287492, 20000
milestone54.0a1
Bug 1287492 - Part 3. (Main) Shrink mTargetBBoxInFilterSpace 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.
    */