Bug 1300401 - Part 1. Handle opacity in nsDisplayFilter. draft
authorcku <cku@mozilla.com>
Mon, 05 Sep 2016 11:47:18 +0800
changeset 410254 5fe2fcf1213cc1fbba72a5f48f86ff4e8a4de95f
parent 409737 dbe4b47941c7b3d6298a0ead5e40dd828096c808
child 410255 633337ad151f2bc9ecd3365a3b93a9fce9c2c18b
push id28697
push userbmo:cku@mozilla.com
push dateTue, 06 Sep 2016 13:43:54 +0000
bugs1300401
milestone51.0a1
Bug 1300401 - Part 1. Handle opacity in nsDisplayFilter. MozReview-Commit-ID: D4JXkDm64fN
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsFrame.cpp
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGIntegrationUtils.h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -6645,20 +6645,20 @@ nsCharClipDisplayItem::ComputeInvalidati
       !oldRect.IsEqualInterior(newRect) ||
       !geometry->mBorderRect.IsEqualInterior(GetBorderRect())) {
     aInvalidRegion->Or(oldRect, newRect);
   }
 }
 
 nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList,
-                                         bool aOpacityItemCreated)
+                                         bool aHandleOpacity)
   : nsDisplayWrapList(aBuilder, aFrame, aList)
   , mEffectsBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
-  , mOpacityItemCreated(aOpacityItemCreated)
+  , mHandleOpacity(aHandleOpacity)
 {
   MOZ_COUNT_CTOR(nsDisplaySVGEffects);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplaySVGEffects::~nsDisplaySVGEffects()
 {
   MOZ_COUNT_DTOR(nsDisplaySVGEffects);
@@ -6737,18 +6737,18 @@ bool nsDisplaySVGEffects::ValidateSVGFra
     }
   }
 
   return true;
 }
 
 nsDisplayMask::nsDisplayMask(nsDisplayListBuilder* aBuilder,
                                          nsIFrame* aFrame, nsDisplayList* aList,
-                                         bool aOpacityItemCreated)
-  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aOpacityItemCreated)
+                                         bool aHandleOpacity)
+  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity)
 {
   MOZ_COUNT_CTOR(nsDisplayMask);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayMask::~nsDisplayMask()
 {
   MOZ_COUNT_DTOR(nsDisplayMask);
@@ -6780,17 +6780,17 @@ already_AddRefed<Layer>
 nsDisplayMask::BuildLayer(nsDisplayListBuilder* aBuilder,
                           LayerManager* aManager,
                           const ContainerLayerParameters& aContainerParameters)
 {
   if (!ValidateSVGFrame()) {
     return nullptr;
   }
 
-  if (mFrame->StyleEffects()->mOpacity == 0.0f && !mOpacityItemCreated) {
+  if (mFrame->StyleEffects()->mOpacity == 0.0f && mHandleOpacity) {
     return nullptr;
   }
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
@@ -6830,17 +6830,18 @@ void
 nsDisplayMask::PaintAsLayer(nsDisplayListBuilder* aBuilder,
                             nsRenderingContext* aCtx,
                             LayerManager* aManager)
 {
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(),
                                                   mFrame,  mVisibleRect,
                                                   borderArea, aBuilder,
-                                                  aManager, mOpacityItemCreated);
+                                                  aManager,
+                                                  mHandleOpacity);
 
   image::DrawResult result =
     nsSVGIntegrationUtils::PaintMaskAndClipPath(params);
 
   nsDisplaySVGEffectsGeometry::UpdateDrawResult(this, result);
 }
 
 #ifdef MOZ_DUMP_PAINTING
@@ -6881,19 +6882,20 @@ nsDisplayMask::PrintEffects(nsACString& 
     }
     aTo += "mask";
   }
   aTo += ")";
 }
 #endif
 
 nsDisplayFilter::nsDisplayFilter(nsDisplayListBuilder* aBuilder,
-                                         nsIFrame* aFrame, nsDisplayList* aList,
-                                         bool aOpacityItemCreated)
-  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aOpacityItemCreated)
+                                         nsIFrame* aFrame,
+                                         nsDisplayList* aList,
+                                         bool aHandleOpacity)
+  : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity)
 {
   MOZ_COUNT_CTOR(nsDisplayFilter);
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayFilter::~nsDisplayFilter()
 {
   MOZ_COUNT_DTOR(nsDisplayFilter);
@@ -6904,17 +6906,17 @@ already_AddRefed<Layer>
 nsDisplayFilter::BuildLayer(nsDisplayListBuilder* aBuilder,
                            LayerManager* aManager,
                            const ContainerLayerParameters& aContainerParameters)
 {
   if (!ValidateSVGFrame()) {
     return nullptr;
   }
 
-  if (mFrame->StyleEffects()->mOpacity == 0.0f && !mOpacityItemCreated) {
+  if (mFrame->StyleEffects()->mOpacity == 0.0f && mHandleOpacity) {
     return nullptr;
   }
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
@@ -6985,17 +6987,18 @@ void
 nsDisplayFilter::PaintAsLayer(nsDisplayListBuilder* aBuilder,
                               nsRenderingContext* aCtx,
                               LayerManager* aManager)
 {
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(),
                                                   mFrame,  mVisibleRect,
                                                   borderArea, aBuilder,
-                                                  aManager, mOpacityItemCreated);
+                                                  aManager,
+                                                  mHandleOpacity);
 
   image::DrawResult result =
     nsSVGIntegrationUtils::PaintFilter(params);
 
   nsDisplaySVGEffectsGeometry::UpdateDrawResult(this, result);
 }
 
 #ifdef MOZ_DUMP_PAINTING
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -3780,17 +3780,17 @@ public:
 
 private:
   int32_t mAPD, mParentAPD;
 };
 
 class nsDisplaySVGEffects: public nsDisplayWrapList {
 public:
   nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                      nsDisplayList* aList, bool aOpacityItemCreated);
+                      nsDisplayList* aList, bool aHandleOpacity);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplaySVGEffects();
 #endif
 
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) override;
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState,
@@ -3815,29 +3815,28 @@ public:
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) override;
 protected:
   bool ValidateSVGFrame();
 
   // relative to mFrame
   nsRect mEffectsBounds;
-  // True if the caller also created an nsDisplayOpacity item, and we should tell
-  // PaintFramesWithEffects that it doesn't need to handle opacity itself.
-  bool mOpacityItemCreated;
+  // True if we need to handle css opacity in this display item.
+  bool mHandleOpacity;
 };
 
 /**
  * A display item to paint a stacking context with mask and clip effects
  * set by the stacking context root frame's style.
  */
 class nsDisplayMask : public nsDisplaySVGEffects {
 public:
   nsDisplayMask(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                      nsDisplayList* aList, bool aOpacityItemCreated);
+                      nsDisplayList* aList, bool aHandleOpacity);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayMask();
 #endif
 
   NS_DISPLAY_DECL_NAME("Mask", TYPE_MASK)
 
   virtual bool TryMerge(nsDisplayItem* aItem) override;
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
@@ -3856,17 +3855,17 @@ public:
   void PaintAsLayer(nsDisplayListBuilder* aBuilder,
                     nsRenderingContext* aCtx,
                     LayerManager* aManager);
 };
 
 class nsDisplayFilter : public nsDisplaySVGEffects {
 public:
   nsDisplayFilter(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                      nsDisplayList* aList, bool aOpacityItemCreated);
+                  nsDisplayList* aList, bool aHandleOpacity);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayFilter();
 #endif
 
   NS_DISPLAY_DECL_NAME("Filter", TYPE_FILTER)
 
   virtual bool TryMerge(nsDisplayItem* aItem) override;
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2446,26 +2446,37 @@ nsIFrame::BuildDisplayListForStackingCon
 
     if (clipCapturedBy == ContainerItemType::eSVGEffects) {
       clipState.ExitStackingContextContents(&containerItemScrollClip);
     }
     // Revert to the post-filter dirty rect.
     buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects);
 
     // Skip all filter effects while generating glyph mask.
-    if (StyleEffects()->HasFilters() && !aBuilder->IsForGenerateGlyphMask()) {
+    bool createFilter =
+      StyleEffects()->HasFilters() && !aBuilder->IsForGenerateGlyphMask();
+    bool createMask = nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
+
+    if (createFilter) {
+      // If we are going to create a mask display item, handle opacity effect
+      // in that mask display item; Otherwise, take care of opacity in this
+      // filter display item.
+      bool handleOpacity = !createMask && !useOpacity;
+
       /* List now emptied, so add the new list to the top. */
       resultList.AppendNewToTop(
-          new (aBuilder) nsDisplayFilter(aBuilder, this, &resultList, useOpacity));
-    }
-
-    if (nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) {
+        new (aBuilder) nsDisplayFilter(aBuilder, this, &resultList,
+                                       handleOpacity));
+    }
+
+    if (createMask) {
       /* List now emptied, so add the new list to the top. */
       resultList.AppendNewToTop(
-          new (aBuilder) nsDisplayMask(aBuilder, this, &resultList, useOpacity));
+          new (aBuilder) nsDisplayMask(aBuilder, this, &resultList,
+                                       !useOpacity));
     }
 
     // Also add the hoisted scroll info items. We need those for APZ scrolling
     // because nsDisplayMask items can't build active layers.
     aBuilder->ExitSVGEffectsContents();
     resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
   }
 
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -160,18 +160,17 @@ nsSVGIntegrationUtils::UsingEffectsForFr
          style->mMask.HasLayerWithImage();
 }
 
 bool
 nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(const nsIFrame* aFrame)
 {
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
   return style->HasClipPath() ||
-         style->mMask.HasLayerWithImage() ||
-         (aFrame->StyleEffects()->mOpacity != 1.0f);
+         style->mMask.HasLayerWithImage();
 }
 
 // For non-SVG frames, this gives the offset to the frame's "user space".
 // For SVG frames, this returns a zero offset.
 static nsPoint
 GetOffsetToBoundingBox(nsIFrame* aFrame)
 {
   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
@@ -612,25 +611,20 @@ GenerateMaskSurface(const nsSVGIntegrati
   aOutMaskSurface = maskDT->Snapshot();
   return DrawResult::SUCCESS;
 }
 
 static float
 ComputeOpacity(const nsSVGIntegrationUtils::PaintFramesParams& aParams)
 {
   nsIFrame* frame = aParams.frame;
-
-  MOZ_ASSERT(!nsSVGUtils::CanOptimizeOpacity(frame) ||
-             !aParams.callerPaintsOpacity,
-             "How can we be optimizing the opacity into the svg as well as having the caller paint it?");
-
   float opacity = frame->StyleEffects()->mOpacity;
 
   if (opacity != 1.0f &&
-      (nsSVGUtils::CanOptimizeOpacity(frame) || aParams.callerPaintsOpacity)) {
+      (nsSVGUtils::CanOptimizeOpacity(frame) || !aParams.handleOpacity)) {
     return 1.0f;
   }
 
   return opacity;
 }
 
 static bool
 ValidateSVGFrame(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
@@ -964,24 +958,41 @@ nsSVGIntegrationUtils::PaintFilter(const
   RefPtr<gfxContext> target =
     (aParams.frame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL)
     ? RefPtr<gfxContext>(&aParams.ctx).forget()
     : CreateBlendTarget(aParams, targetOffset);
   if (!target) {
     return DrawResult::TEMPORARY_ERROR;
   }
 
+  if (opacity != 1.0f) {
+    context.Save();
+    nsRect clipRect =
+      frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
+    context.Clip(NSRectToSnappedRect(clipRect,
+                                  frame->PresContext()->AppUnitsPerDevPixel(),
+                                  *context.GetDrawTarget()));
+
+    target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
+                                  nullptr, Matrix());
+  }
+
   /* Paint the child and apply filters */
   RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
                                      offsetToUserSpace);
   nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox;
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
   nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(),
                                        tm, &callback, &dirtyRegion);
 
+  if (opacity != 1.0f) {
+    target->PopGroupAndBlend();
+    context.Restore();
+  }
+
   if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
     MOZ_ASSERT(target != &aParams.ctx);
     BlendToTarget(aParams, target, targetOffset);
   }
 
   return result;
 }
 
--- a/layout/svg/nsSVGIntegrationUtils.h
+++ b/layout/svg/nsSVGIntegrationUtils.h
@@ -131,26 +131,27 @@ public:
 
   struct PaintFramesParams {
     gfxContext& ctx;
     nsIFrame* frame;
     const nsRect& dirtyRect;
     const nsRect& borderArea;
     nsDisplayListBuilder* builder;
     mozilla::layers::LayerManager* layerManager;
-    bool callerPaintsOpacity;
+    bool handleOpacity; // If true, PaintMaskAndClipPath/ PaintFilter should
+                        // apply css opacity.
     explicit PaintFramesParams(gfxContext& aCtx, nsIFrame* aFrame,
                                const nsRect& aDirtyRect,
                                const nsRect& aBorderArea,
                                nsDisplayListBuilder* aBuilder,
                                mozilla::layers::LayerManager* aLayerManager,
-                               bool aCallerPaintsOpacity)
+                               bool aHandleOpacity)
       : ctx(aCtx), frame(aFrame), dirtyRect(aDirtyRect),
         borderArea(aBorderArea), builder(aBuilder),
-        layerManager(aLayerManager), callerPaintsOpacity(aCallerPaintsOpacity)
+        layerManager(aLayerManager), handleOpacity(aHandleOpacity)
     { }
   };
 
   /**
    * Paint non-SVG frame with SVG effects.
    */
   static DrawResult
   PaintMaskAndClipPath(const PaintFramesParams& aParams);