Bug 1228280 - Part 4. Create nsSVGMaskProperty to carry multiple mask info; draft
authorCJKu <cku@mozilla.com>
Thu, 19 May 2016 12:41:09 +0800
changeset 368599 eeb156080c14918e6977112cb1936c1c1490a73c
parent 368598 c2e590bac05459b3657f1dce07f4b2b44bbe9430
child 368600 4250562144d20d2494793c4bd5a2504b85c7cfcd
push id18601
push usercku@mozilla.com
push dateThu, 19 May 2016 04:45:09 +0000
bugs1228280
milestone49.0a1
Bug 1228280 - Part 4. Create nsSVGMaskProperty to carry multiple mask info; MozReview-Commit-ID: 8NoeoA18jdZ
layout/base/nsDisplayList.cpp
layout/svg/nsSVGEffects.cpp
layout/svg/nsSVGEffects.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGUtils.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -6835,17 +6835,17 @@ nsDisplaySVGEffects::PrintEffects(nsACSt
   }
   if (effectProperties.HasValidFilter()) {
     if (!first) {
       aTo += ", ";
     }
     aTo += "filter";
     first = false;
   }
-  if (effectProperties.GetMaskFrame(&isOK)) {
+  if (effectProperties.GetFirstMaskFrame()) {
     if (!first) {
       aTo += ", ";
     }
     aTo += "mask";
   }
   aTo += ")";
 }
 #endif
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -359,16 +359,30 @@ nsSVGMarkerProperty::DoUpdate()
     // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
     // XXXSDL KILL THIS!!!
     nsSVGUtils::ScheduleReflowSVG(frame);
   }
   frame->PresContext()->RestyleManager()->PostRestyleEvent(
     frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
 }
 
+NS_IMPL_ISUPPORTS(nsSVGMaskProperty, nsISupports)
+
+nsSVGMaskProperty::nsSVGMaskProperty(nsIFrame* aFrame)
+{
+  const nsStyleSVGReset *svgReset = aFrame->StyleSVGReset();
+
+  for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) {
+    nsSVGPaintingProperty* prop =
+      new nsSVGPaintingProperty(svgReset->mMask.mLayers[i].mSourceURI, aFrame,
+                                false);
+    mProperties.AppendElement(prop);
+  }
+}
+
 bool
 nsSVGTextPathProperty::TargetIsValid()
 {
   Element* target = GetTarget();
   return target && target->IsSVGElement(nsGkAtoms::path);
 }
 
 void
@@ -482,16 +496,30 @@ GetOrCreateFilterProperty(nsIFrame *aFra
   prop = new nsSVGFilterProperty(effects->mFilters, aFrame);
   if (!prop)
     return nullptr;
   NS_ADDREF(prop);
   props.Set(nsSVGEffects::FilterProperty(), prop);
   return prop;
 }
 
+static nsSVGMaskProperty*
+GetOrCreateMaskProperty(nsIFrame *aFrame)
+{
+  FrameProperties props = aFrame->Properties();
+  nsSVGMaskProperty *prop = props.Get(nsSVGEffects::MaskProperty());
+  if (prop)
+    return prop;
+
+  prop = new nsSVGMaskProperty(aFrame);
+  NS_ADDREF(prop);
+  props.Set(nsSVGEffects::MaskProperty(), prop);
+  return prop;
+}
+
 nsSVGMarkerProperty *
 nsSVGEffects::GetMarkerProperty(nsIURI *aURI, nsIFrame *aFrame,
                                 ObserverPropertyDescriptor aProp)
 {
   MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame &&
                static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable(),
              "Bad frame");
   return static_cast<nsSVGMarkerProperty*>(
@@ -548,30 +576,29 @@ nsSVGEffects::GetPaintingPropertyForURI(
 
 nsSVGEffects::EffectProperties
 nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   EffectProperties result;
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
+
   result.mFilter = GetOrCreateFilterProperty(aFrame);
+
   if (style->mClipPath.GetType() == NS_STYLE_CLIP_PATH_URL) {
     result.mClipPath =
       GetPaintingProperty(style->mClipPath.GetURL(), aFrame, ClipPathProperty());
   } else {
     result.mClipPath = nullptr;
   }
 
-  // FIXME: Bug 1228280.
-  // Before fixing bug 1228280, we support only single svg mask as before.
   MOZ_ASSERT(style->mMask.mImageCount > 0);
-  nsCOMPtr<nsIURI> uri = style->mMask.mLayers[0].mSourceURI;
-  result.mMask = uri ? GetPaintingProperty(uri, aFrame, MaskProperty()) :
-                         nullptr;
+  result.mMask = style->mMask.HasLayerWithImage()
+                 ? GetOrCreateMaskProperty(aFrame) : nullptr;
 
   return result;
 }
 
 nsSVGPaintServerFrame *
 nsSVGEffects::GetPaintServer(nsIFrame *aTargetFrame, const nsStyleSVGPaint *aPaint,
                              ObserverPropertyDescriptor aType)
 {
@@ -616,22 +643,49 @@ nsSVGEffects::EffectProperties::GetClipP
     (mClipPath->GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK));
   if (frame && aOK && *aOK) {
     *aOK = frame->IsValid();
   }
   return frame;
 }
 
 nsSVGMaskFrame *
-nsSVGEffects::EffectProperties::GetMaskFrame(bool *aOK)
+nsSVGEffects::EffectProperties::GetFirstMaskFrame(bool *aOK)
 {
+  if (!mMask) {
+    return nullptr;
+  }
+
+  const nsTArray<RefPtr<nsSVGPaintingProperty>>& props = mMask->GetProps();
+
+  if (props.IsEmpty()) {
+    return nullptr;
+  }
+
+  return static_cast<nsSVGMaskFrame *>
+    (props[0]->GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK));
+}
+
+nsTArray<nsSVGMaskFrame *>
+nsSVGEffects::EffectProperties::GetMaskFrames()
+{
+  nsTArray<nsSVGMaskFrame *> result;
   if (!mMask)
-    return nullptr;
-  return static_cast<nsSVGMaskFrame *>
-    (mMask->GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK));
+    return result;
+
+  bool ok = false;
+  const nsTArray<RefPtr<nsSVGPaintingProperty>>& props = mMask->GetProps();
+  for (size_t i = 0; i < props.Length(); i++) {
+    nsSVGMaskFrame* maskFrame =
+      static_cast<nsSVGMaskFrame *>(props[i]->GetReferencedFrame(
+                                                nsGkAtoms::svgMaskFrame, &ok));
+    result.AppendElement(maskFrame);
+  }
+
+  return result;
 }
 
 void
 nsSVGEffects::UpdateEffects(nsIFrame *aFrame)
 {
   NS_ASSERTION(aFrame->GetContent()->IsElement(),
                "aFrame's content should be an element");
 
--- a/layout/svg/nsSVGEffects.h
+++ b/layout/svg/nsSVGEffects.h
@@ -317,16 +317,34 @@ class nsSVGPaintingProperty final : publ
 public:
   nsSVGPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
     : nsSVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
 
 protected:
   virtual void DoUpdate() override;
 };
 
+class nsSVGMaskProperty final : public nsISupports
+{
+public:
+  explicit nsSVGMaskProperty(nsIFrame* aFrame);
+
+  // nsISupports
+  NS_DECL_ISUPPORTS
+
+  const nsTArray<RefPtr<nsSVGPaintingProperty>>& GetProps() const
+  {
+    return mProperties;
+  }
+
+private:
+  virtual ~nsSVGMaskProperty() {}
+  nsTArray<RefPtr<nsSVGPaintingProperty>> mProperties;
+};
+
 /**
  * A manager for one-shot nsSVGRenderingObserver tracking.
  * nsSVGRenderingObservers can be added or removed. They are not strongly
  * referenced so an observer must be removed before it dies.
  * When InvalidateAll is called, all outstanding references get
  * InvalidateViaReferencedElement()
  * called on them and the list is cleared. The intent is that
  * the observer will force repainting of whatever part of the document
@@ -402,17 +420,17 @@ public:
     // has now become invalid.
     aProp->DetachFromFrame();
 
     aProp->Release();
   }
 
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(FilterProperty, nsSVGFilterProperty,
                                       DestroyFilterProperty)
-  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MaskProperty, nsISupports)
+  NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MaskProperty, nsSVGMaskProperty)
   NS_DECLARE_FRAME_PROPERTY_RELEASABLE(ClipPathProperty, nsISupports)
   NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerBeginProperty, nsISupports)
   NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerMiddleProperty, nsISupports)
   NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerEndProperty, nsISupports)
   NS_DECLARE_FRAME_PROPERTY_RELEASABLE(FillProperty, nsISupports)
   NS_DECLARE_FRAME_PROPERTY_RELEASABLE(StrokeProperty, nsISupports)
   NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefProperty, nsISupports)
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(BackgroundImageProperty,
@@ -422,33 +440,38 @@ public:
    * Get the paint server for a aTargetFrame.
    */
   static nsSVGPaintServerFrame *GetPaintServer(nsIFrame *aTargetFrame,
                                                const nsStyleSVGPaint *aPaint,
                                                ObserverPropertyDescriptor aProperty);
 
   struct EffectProperties {
     nsSVGFilterProperty*   mFilter;
-    nsSVGPaintingProperty* mMask;
+    nsSVGMaskProperty*     mMask;
     nsSVGPaintingProperty* mClipPath;
 
     /**
      * @return the clip-path frame, or null if there is no clip-path frame
      * @param aOK if a clip-path was specified and the designated element
      * exists but is an element of the wrong type, *aOK is set to false.
      * Otherwise *aOK is untouched.
      */
     nsSVGClipPathFrame *GetClipPathFrame(bool *aOK);
     /**
-     * @return the mask frame, or null if there is no mask frame
+     * @return the first mask frame, or null if there is no mask frame
      * @param aOK if a mask was specified and the designated element
      * exists but is an element of the wrong type, *aOK is set to false.
      * Otherwise *aOK is untouched.
      */
-    nsSVGMaskFrame *GetMaskFrame(bool *aOK);
+    nsSVGMaskFrame *GetFirstMaskFrame(bool *aOK = nullptr);
+
+    /**
+     * @return an array which contains all SVG mask frames.
+     */
+    nsTArray<nsSVGMaskFrame*> GetMaskFrames();
 
     bool HasValidFilter() {
       return mFilter && mFilter->ReferencesValidResources();
     }
 
     bool HasNoFilterOrHasValidFilter() {
       return !mFilter || mFilter->ReferencesValidResources();
     }
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -411,17 +411,17 @@ private:
 
 static bool
 HasMaskToDraw(const nsStyleSVGReset* aSVGReset,
               nsSVGEffects::EffectProperties& aEffectProperties)
 {
   bool isOK = true;
   // Keep moving forward even if svgMaskFrame is nullptr or isOK is false.
   // This source is not a svg mask, but it still can be a correct mask image.
-  nsSVGMaskFrame *svgMaskFrame = aEffectProperties.GetMaskFrame(&isOK);
+  nsSVGMaskFrame *svgMaskFrame = aEffectProperties.GetFirstMaskFrame(&isOK);
 
   // hasMaskToDraw is true means we have at least one drawable mask resource.
   // We need to apply mask only if hasMaskToDraw is true.
   bool hasMaskToDraw = (svgMaskFrame != nullptr);
   if (!hasMaskToDraw) {
     NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, aSVGReset->mMask) {
       if (!aSVGReset->mMask.mLayers[i].mImage.IsEmpty()) {
         hasMaskToDraw = true;
@@ -438,20 +438,19 @@ GenerateMaskSurface(const nsSVGIntegrati
                     float aOpacity, nsStyleContext* aSC,
                     nsSVGEffects::EffectProperties& aEffectProperties,
                     gfxMatrix aOriginMatrix, Matrix& aOutMaskTransform,
                     RefPtr<SourceSurface>& aOutMaskSurface)
 {
   const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
   MOZ_ASSERT(HasMaskToDraw(svgReset, aEffectProperties) > 0);
 
-  bool isOK = true;
-  // Keep moving forward even if svgMaskFrame is nullptr or isOK is false.
+  // Keep moving forward even if svgMaskFrame is nullptr.
   // This source is not a svg mask, but it still can be a correct mask image.
-  nsSVGMaskFrame *svgMaskFrame = aEffectProperties.GetMaskFrame(&isOK);
+  nsSVGMaskFrame *svgMaskFrame = aEffectProperties.GetFirstMaskFrame();
   gfxContext& ctx = aParams.ctx;
 
   if (svgMaskFrame) {
     gfxMatrix cssPxToDevPxMatrix =
       nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
 
     // Generate aOutMaskSurface from a SVG mask.
     aOutMaskSurface = svgMaskFrame->GetMaskForMaskedFrame(&ctx,
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -572,17 +572,17 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
 
   if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
     opacity = 1.0f;
 
   DrawTarget* drawTarget = aContext.GetDrawTarget();
   bool complexEffects = false;
 
   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
-  nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
+  nsSVGMaskFrame *maskFrame = effectProperties.GetFirstMaskFrame(&isOK);
 
   bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
 
   if (!isOK) {
     // Some resource is invalid. We shouldn't paint anything.
     return;
   }