Bug 1066270 - Resolve SVG lengths in nsSVGFilterInstance using the UserSpaceMetrics supplied in the nsFilterInstance constructor. r=roc
authorMarkus Stange <mstange@themasta.com>
Mon, 15 Sep 2014 12:12:50 +0200
changeset 205319 769fcdda208d71c97c6679987c94cbbbd30b7d9d
parent 205318 b6154a1235ed591d01adc23fcfb64ae5cd33ec4f
child 205320 5a40113656920ff608b1ed28523124068d6fe92c
push id27489
push userkwierso@gmail.com
push dateMon, 15 Sep 2014 22:18:35 +0000
treeherdermozilla-central@02c3915bf742 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1066270
milestone35.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 1066270 - Resolve SVG lengths in nsSVGFilterInstance using the UserSpaceMetrics supplied in the nsFilterInstance constructor. r=roc
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGFilterInstance.cpp
layout/svg/nsSVGFilterInstance.h
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -1,16 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Main header first:
 #include "nsFilterInstance.h"
 
+// MFBT headers next:
+#include "mozilla/UniquePtr.h"
+
 // Keep others in (case-insensitive) order:
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/Helpers.h"
 #include "nsISVGChildFrame.h"
 #include "nsRenderingContext.h"
 #include "nsCSSFilterInstance.h"
 #include "nsSVGFilterInstance.h"
@@ -19,25 +22,36 @@
 #include "SVGContentUtils.h"
 #include "FilterSupport.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
+static UniquePtr<UserSpaceMetrics>
+UserSpaceMetricsForFrame(nsIFrame* aFrame)
+{
+  if (aFrame->GetContent()->IsSVG()) {
+    nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
+    return MakeUnique<SVGElementMetrics>(element);
+  }
+  return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
+}
+
 nsresult
 nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
                                      nsRenderingContext *aContext,
                                      const gfxMatrix& aTransform,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRegion *aDirtyArea)
 {
   auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters;
-  nsFilterInstance instance(aFilteredFrame, filterChain, aPaintCallback, aTransform,
+  UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  nsFilterInstance instance(aFilteredFrame, *metrics, filterChain, aPaintCallback, aTransform,
                             aDirtyArea, nullptr, nullptr, nullptr);
   if (!instance.IsInitialized()) {
     return NS_OK;
   }
   return instance.Render(aContext->ThebesContext());
 }
 
 nsRegion
@@ -45,17 +59,18 @@ nsFilterInstance::GetPostFilterDirtyArea
                                          const nsRegion& aPreFilterDirtyRegion)
 {
   if (aPreFilterDirtyRegion.IsEmpty()) {
     return nsRegion();
   }
 
   gfxMatrix unused; // aPaintTransform arg not used since we're not painting
   auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters;
-  nsFilterInstance instance(aFilteredFrame, filterChain, nullptr, unused, nullptr,
+  UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  nsFilterInstance instance(aFilteredFrame, *metrics, filterChain, nullptr, unused, nullptr,
                             &aPreFilterDirtyRegion);
   if (!instance.IsInitialized()) {
     return nsRegion();
   }
 
   // We've passed in the source's dirty area so the instance knows about it.
   // Now we can ask the instance to compute the area of the filter output
   // that's dirty.
@@ -63,17 +78,18 @@ nsFilterInstance::GetPostFilterDirtyArea
 }
 
 nsRegion
 nsFilterInstance::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
                                          const nsRegion& aPostFilterDirtyRegion)
 {
   gfxMatrix unused; // aPaintTransform arg not used since we're not painting
   auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters;
-  nsFilterInstance instance(aFilteredFrame, filterChain, nullptr, unused,
+  UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  nsFilterInstance instance(aFilteredFrame, *metrics, filterChain, nullptr, unused,
                             &aPostFilterDirtyRegion);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
 
   // Now we can ask the instance to compute the area of the source
   // that's needed.
   return instance.ComputeSourceNeededRect();
@@ -92,35 +108,38 @@ nsFilterInstance::GetPostFilterBounds(ns
   nsRegion* preFilterRegionPtr = nullptr;
   if (aPreFilterBounds) {
     preFilterRegion = *aPreFilterBounds;
     preFilterRegionPtr = &preFilterRegion;
   }
 
   gfxMatrix unused; // aPaintTransform arg not used since we're not painting
   auto& filterChain = aFilteredFrame->StyleSVGReset()->mFilters;
-  nsFilterInstance instance(aFilteredFrame, filterChain, nullptr, unused, nullptr,
+  UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+  nsFilterInstance instance(aFilteredFrame, *metrics, filterChain, nullptr, unused, nullptr,
                             preFilterRegionPtr, aPreFilterBounds,
                             aOverrideBBox);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
 
   return instance.ComputePostFilterExtents();
 }
 
 nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
+                                   const UserSpaceMetrics& aMetrics,
                                    const nsTArray<nsStyleFilter>& aFilterChain,
                                    nsSVGFilterPaintCallback *aPaintCallback,
                                    const gfxMatrix& aPaintTransform,
                                    const nsRegion *aPostFilterDirtyRegion,
                                    const nsRegion *aPreFilterDirtyRegion,
                                    const nsRect *aPreFilterVisualOverflowRectOverride,
                                    const gfxRect *aOverrideBBox)
   : mTargetFrame(aTargetFrame)
+  , mMetrics(aMetrics)
   , mPaintCallback(aPaintCallback)
   , mPaintTransform(aPaintTransform)
   , mInitialized(false)
 {
 
   mTargetBBox = aOverrideBBox ?
     *aOverrideBBox : nsSVGUtils::GetBBox(mTargetFrame);
 
@@ -241,17 +260,18 @@ nsresult
 nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter)
 {
   NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f &&
                mFilterSpaceToUserSpaceScale.height > 0.0f,
                "scale factors between spaces should be positive values");
 
   if (aFilter.GetType() == NS_STYLE_FILTER_URL) {
     // Build primitives for an SVG filter.
-    nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame, mTargetBBox,
+    nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame->GetContent(),
+                                          mMetrics, mTargetBBox,
                                           mUserSpaceToFilterSpaceScale,
                                           mFilterSpaceToUserSpaceScale);
     if (!svgFilterInstance.IsInitialized()) {
       return NS_ERROR_FAILURE;
     }
 
     return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages);
   }
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -20,16 +20,22 @@
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "mozilla/gfx/2D.h"
 
 class gfxASurface;
 class nsIFrame;
 class nsSVGFilterPaintCallback;
 
+namespace mozilla {
+namespace dom {
+class UserSpaceMetrics;
+}
+}
+
 /**
  * This class performs all filter processing.
  *
  * We build a graph of the filter image data flow, essentially
  * converting the filter graph to SSA. This lets us easily propagate
  * analysis data (such as bounding-boxes) over the filter primitive graph.
  *
  * Definition of "filter space": filter space is a coordinate system that is
@@ -43,16 +49,17 @@ class nsSVGFilterPaintCallback;
  */
 class nsFilterInstance
 {
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
   typedef mozilla::gfx::FilterDescription FilterDescription;
+  typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
 
 public:
   /**
    * Paint the given filtered frame.
    * @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
    *   frame space (i.e. relative to its origin, the top-left corner of its
    *   border box).
    */
@@ -89,16 +96,17 @@ public:
    *   aFilteredFrame, if non-null.
    */
   static nsRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
                                     const gfxRect *aOverrideBBox = nullptr,
                                     const nsRect *aPreFilterBounds = nullptr);
 
   /**
    * @param aTargetFrame The frame of the filtered element under consideration.
+   * @param aMetrics The metrics to resolve SVG lengths against.
    * @param aFilterChain The list of filters to apply.
    * @param aPaintCallback [optional] The callback that Render() should use to
    *   paint. Only required if you will call Render().
    * @param aPaintTransform The transform to apply to convert to
    *   aTargetFrame's SVG user space. Only used when painting.
    * @param aPostFilterDirtyRegion [optional] The post-filter area
    *   that has to be repainted, in app units. Only required if you will
    *   call ComputeSourceNeededRect() or Render().
@@ -106,16 +114,17 @@ public:
    *   the filtered element that changed, in app units. Only required if you
    *   will call ComputePostFilterDirtyRegion().
    * @param aOverridePreFilterVisualOverflowRect [optional] Use a different
    *   visual overflow rect for the target element.
    * @param aOverrideBBox [optional] Use a different SVG bbox for the target
    *   element.
    */
   nsFilterInstance(nsIFrame *aTargetFrame,
+                   const UserSpaceMetrics& aMetrics,
                    const nsTArray<nsStyleFilter>& aFilterChain,
                    nsSVGFilterPaintCallback *aPaintCallback,
                    const gfxMatrix& aPaintTransform,
                    const nsRegion *aPostFilterDirtyRegion = nullptr,
                    const nsRegion *aPreFilterDirtyRegion = nullptr,
                    const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
                    const gfxRect *aOverrideBBox = nullptr);
 
@@ -264,16 +273,21 @@ private:
    */
   gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const;
 
   /**
    * The frame for the element that is currently being filtered.
    */
   nsIFrame* mTargetFrame;
 
+  /**
+   * The user space metrics of the filtered frame.
+   */
+  const UserSpaceMetrics& mMetrics;
+
   nsSVGFilterPaintCallback* mPaintCallback;
 
   /**
    * The SVG bbox of the element that is being filtered, in user space.
    */
   gfxRect mTargetBBox;
 
   /**
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -19,22 +19,24 @@
 #include "FilterSupport.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter,
-                                         nsIFrame *aTargetFrame,
+                                         nsIContent* aTargetContent,
+                                         const UserSpaceMetrics& aMetrics,
                                          const gfxRect& aTargetBBox,
                                          const gfxSize& aUserSpaceToFilterSpaceScale,
                                          const gfxSize& aFilterSpaceToUserSpaceScale) :
   mFilter(aFilter),
-  mTargetFrame(aTargetFrame),
+  mTargetContent(aTargetContent),
+  mMetrics(aMetrics),
   mTargetBBox(aTargetBBox),
   mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale),
   mFilterSpaceToUserSpaceScale(aFilterSpaceToUserSpaceScale),
   mSourceAlphaAvailable(false),
   mInitialized(false) {
 
   // Get the filter frame.
   mFilterFrame = GetFilterFrame();
@@ -81,17 +83,17 @@ nsSVGFilterInstance::ComputeBounds()
     sizeof(mFilterElement->mLengthAttributes));
   XYWH[0] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_X);
   XYWH[1] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_Y);
   XYWH[2] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_WIDTH);
   XYWH[3] = *mFilterFrame->GetLengthValue(SVGFilterElement::ATTR_HEIGHT);
   uint16_t filterUnits =
     mFilterFrame->GetEnumValue(SVGFilterElement::FILTERUNITS);
   gfxRect userSpaceBounds = nsSVGUtils::GetRelativeRect(filterUnits,
-    XYWH, mTargetBBox, mTargetFrame);
+    XYWH, mTargetBBox, mMetrics);
 
   // Transform the user space bounds to filter space, so we
   // can align them with the pixel boundries of the offscreen surface.
   // The offscreen surface has the same scale as filter space.
   gfxRect filterSpaceBounds = UserSpaceToFilterSpace(userSpaceBounds);
   filterSpaceBounds.RoundOut();
   if (filterSpaceBounds.width <= 0 || filterSpaceBounds.height <= 0) {
     // 0 disables rendering, < 0 is error. dispatch error console warning
@@ -121,26 +123,24 @@ nsSVGFilterInstance::GetFilterFrame()
   nsIURI* url = mFilter.GetURL();
   if (!url) {
     NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL");
     return nullptr;
   }
 
   // Get the target element to use as a point of reference for looking up the
   // filter element.
-  nsIContent* targetElement = mTargetFrame->GetContent();
-  if (!targetElement) {
-    // There is no element associated with the target frame.
+  if (!mTargetContent) {
     return nullptr;
   }
 
   // Look up the filter element by URL.
   nsReferencedElement filterElement;
   bool watch = false;
-  filterElement.Reset(targetElement, url, watch);
+  filterElement.Reset(mTargetContent, url, watch);
   Element* element = filterElement.get();
   if (!element) {
     // The URL points to no element.
     return nullptr;
   }
 
   // Get the frame of the filter element.
   nsIFrame* frame = element->GetPrimaryFrame();
@@ -158,17 +158,17 @@ nsSVGFilterInstance::GetPrimitiveNumber(
   nsSVGLength2 val;
   val.Init(aCtxType, 0xff, aValue,
            nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
 
   float value;
   if (mPrimitiveUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
     value = nsSVGUtils::ObjectSpace(mTargetBBox, &val);
   } else {
-    value = nsSVGUtils::UserSpace(mTargetFrame, &val);
+    value = nsSVGUtils::UserSpace(mMetrics, &val);
   }
 
   switch (aCtxType) {
   case SVGContentUtils::X:
     return value * mUserSpaceToFilterSpaceScale.width;
   case SVGContentUtils::Y:
     return value * mUserSpaceToFilterSpaceScale.height;
   case SVGContentUtils::XY:
@@ -189,17 +189,17 @@ nsSVGFilterInstance::ConvertLocation(con
               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   // Dummy width/height values
   val[2].Init(SVGContentUtils::X, 0xff, 0,
               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
   val[3].Init(SVGContentUtils::Y, 0xff, 0,
               nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
 
   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
-    val, mTargetBBox, mTargetFrame);
+    val, mTargetBBox, mMetrics);
   gfxRect r = UserSpaceToFilterSpace(feArea);
   return Point3D(r.x, r.y, GetPrimitiveNumber(SVGContentUtils::XY, aPoint.z));
 }
 
 gfxRect
 nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
 {
   gfxRect filterSpaceRect = aUserSpaceRect;
@@ -235,17 +235,17 @@ nsSVGFilterInstance::ComputeFilterPrimit
 
       defaultFilterSubregion = defaultFilterSubregion.Union(inputSubregion);
     }
   } else {
     defaultFilterSubregion = ToIntRect(mFilterSpaceBounds);
   }
 
   gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
-    &fE->mLengthAttributes[nsSVGFE::ATTR_X], mTargetBBox, mTargetFrame);
+    &fE->mLengthAttributes[nsSVGFE::ATTR_X], mTargetBBox, mMetrics);
   Rect region = ToRect(UserSpaceToFilterSpace(feArea));
 
   if (!fE->mLengthAttributes[nsSVGFE::ATTR_X].IsExplicitlySet())
     region.x = defaultFilterSubregion.X();
   if (!fE->mLengthAttributes[nsSVGFE::ATTR_Y].IsExplicitlySet())
     region.y = defaultFilterSubregion.Y();
   if (!fE->mLengthAttributes[nsSVGFE::ATTR_WIDTH].IsExplicitlySet())
     region.width = defaultFilterSubregion.Width();
@@ -382,17 +382,17 @@ nsSVGFilterInstance::BuildPrimitives(nsT
       primitives.AppendElement(primitive);
     }
   }
 
   // Maps source image name to source index.
   nsDataHashtable<nsStringHashKey, int32_t> imageTable(8);
 
   // The principal that we check principals of any loaded images against.
-  nsCOMPtr<nsIPrincipal> principal = mTargetFrame->GetContent()->NodePrincipal();
+  nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal();
 
   for (uint32_t primitiveElementIndex = 0;
        primitiveElementIndex < primitives.Length();
        ++primitiveElementIndex) {
     nsSVGFE* filter = primitives[primitiveElementIndex];
 
     nsAutoTArray<int32_t,2> sourceIndices;
     nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices);
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -65,28 +65,30 @@ class SVGFilterElement;
  *   "filter space point" = (20, 20)
  */
 class nsSVGFilterInstance
 {
   typedef mozilla::gfx::Point3D Point3D;
   typedef mozilla::gfx::IntRect IntRect;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
+  typedef mozilla::dom::UserSpaceMetrics UserSpaceMetrics;
 
 public:
   /**
    * @param aFilter The SVG filter reference from the style system. This class
    *   stores aFilter by reference, so callers should avoid modifying or
    *   deleting aFilter during the lifetime of nsSVGFilterInstance.
-   * @param aTargetFrame The frame of the filtered element under consideration.
+   * @param aTargetContent The filtered element.
    * @param aTargetBBox The SVG bbox to use for the target frame, computed by
    *   the caller. The caller may decide to override the actual SVG bbox.
    */
   nsSVGFilterInstance(const nsStyleFilter& aFilter,
-                      nsIFrame *aTargetFrame,
+                      nsIContent* aTargetContent,
+                      const UserSpaceMetrics& aMetrics,
                       const gfxRect& aTargetBBox,
                       const gfxSize& aUserSpaceToFilterSpaceScale,
                       const gfxSize& aFilterSpaceToUserSpaceScale);
 
   /**
    * Returns true if the filter instance was created successfully.
    */
   bool IsInitialized() const { return mInitialized; }
@@ -203,19 +205,24 @@ private:
   nsresult ComputeBounds();
 
   /**
    * The SVG reference filter originally from the style system.
    */
   const nsStyleFilter& mFilter;
 
   /**
-   * The frame for the element that is currently being filtered.
+   * The filtered element.
    */
-  nsIFrame* mTargetFrame;
+  nsIContent* mTargetContent;
+
+  /**
+   * The SVG user space metrics that SVG lengths are resolved against.
+   */
+  const UserSpaceMetrics& mMetrics;
 
   /**
    * The filter element referenced by mTargetFrame's element.
    */
   const mozilla::dom::SVGFilterElement* mFilterElement;
 
   /**
    * The frame for the SVG filter element.