Bug 1248913 - Let nsDisplayBackgroundImage specify the background blend mode. r?mattwoodrow draft
authorMarkus Stange <mstange@themasta.com>
Sat, 05 Mar 2016 17:27:29 -0500
changeset 338210 db35763f2c33d830197ceb2d9a0e45d065846e11
parent 338209 755cf28dcd931b07f2f69797686eb390e340e6e5
child 338211 27451f1e4e593428721e50e4d46560b038346ec4
push id12468
push usermstange@themasta.com
push dateTue, 08 Mar 2016 19:59:01 +0000
reviewersmattwoodrow
bugs1248913
milestone47.0a1
Bug 1248913 - Let nsDisplayBackgroundImage specify the background blend mode. r?mattwoodrow This is needed because blending for nsDisplayBackgroundImage items will soon happen outside of nsDisplayBackgroundImage::Paint, it will be done by an nsDisplayBlendMode item that wraps the nsDisplayBackgroundImage item. MozReview-Commit-ID: 20cILOGVFEG
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/base/nsDisplayList.cpp
layout/svg/nsSVGIntegrationUtils.cpp
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1647,17 +1647,18 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
 DrawResult
 nsCSSRendering::PaintBackground(nsPresContext* aPresContext,
                                 nsRenderingContext& aRenderingContext,
                                 nsIFrame* aForFrame,
                                 const nsRect& aDirtyRect,
                                 const nsRect& aBorderArea,
                                 uint32_t aFlags,
                                 nsRect* aBGClipRect,
-                                int32_t aLayer)
+                                int32_t aLayer,
+                                CompositionOp aCompositonOp)
 {
   PROFILER_LABEL("nsCSSRendering", "PaintBackground",
     js::ProfileEntry::Category::GRAPHICS);
 
   NS_PRECONDITION(aForFrame,
                   "Frame is expected to be provided to PaintBackground");
 
   nsStyleContext *sc;
@@ -1677,17 +1678,17 @@ nsCSSRendering::PaintBackground(nsPresCo
     }
 
     sc = aForFrame->StyleContext();
   }
 
   return PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
                                aDirtyRect, aBorderArea, sc,
                                *aForFrame->StyleBorder(), aFlags,
-                               aBGClipRect, aLayer);
+                               aBGClipRect, aLayer, aCompositonOp);
 }
 
 static bool
 IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::css::Side aSide)
 {
   if (aBorder.GetComputedBorder().Side(aSide) == 0)
     return true;
   switch (aBorder.GetBorderStyle(aSide)) {
@@ -2853,17 +2854,18 @@ nsCSSRendering::PaintBackgroundWithSC(ns
                                       nsRenderingContext& aRenderingContext,
                                       nsIFrame* aForFrame,
                                       const nsRect& aDirtyRect,
                                       const nsRect& aBorderArea,
                                       nsStyleContext* aBackgroundSC,
                                       const nsStyleBorder& aBorder,
                                       uint32_t aFlags,
                                       nsRect* aBGClipRect,
-                                      int32_t aLayer)
+                                      int32_t aLayer,
+                                      CompositionOp aCompositonOp)
 {
   NS_PRECONDITION(aForFrame,
                   "Frame is expected to be provided to PaintBackground");
 
   DrawResult result = DrawResult::SUCCESS;
 
   // Check to see if we have an appearance defined.  If so, we let the theme
   // renderer draw the background and bail out.
@@ -3028,39 +3030,43 @@ nsCSSRendering::PaintBackgroundWithSC(ns
             ctx->NewPath();
             ctx->SnappedRectangle(clip);
             ctx->Clip();
           }
         }
       }
       if ((aLayer < 0 || i == (uint32_t)startLayer) &&
           !clipState.mDirtyRectGfx.IsEmpty()) {
+        // When we're drawing a single layer, use the specified composition op,
+        // otherwise get the compositon op from the image layer.
+        CompositionOp co = (aLayer >= 0) ? aCompositonOp :
+          (paintMask ? GetGFXCompositeMode(layer.mComposite) :
+                       GetGFXBlendMode(layer.mBlendMode));
         nsBackgroundLayerState state =
           PrepareImageLayer(aPresContext, aForFrame,
                             aFlags, paintBorderArea, clipState.mBGClipArea,
-                            layer, paintMask);
+                            layer, co);
         result &= state.mImageRenderer.PrepareResult();
         if (!state.mFillArea.IsEmpty()) {
           // Always using OP_OVER mode while drawing the bottom mask layer.
           bool isBottomMaskLayer = paintMask ?
                                    (i == (layers.mImageCount - 1)) : false;
-          if (state.mCompositionOp != CompositionOp::OP_OVER &&
-              !isBottomMaskLayer) {
+          if (co != CompositionOp::OP_OVER && !isBottomMaskLayer) {
             NS_ASSERTION(ctx->CurrentOp() == CompositionOp::OP_OVER,
                          "It is assumed the initial op is OP_OVER, when it is restored later");
-            ctx->SetOp(state.mCompositionOp);
+            ctx->SetOp(co);
           }
 
           result &=
             state.mImageRenderer.DrawBackground(aPresContext, aRenderingContext,
                                                 state.mDestArea, state.mFillArea,
                                                 state.mAnchor + paintBorderArea.TopLeft(),
                                                 clipState.mDirtyRect);
 
-          if (state.mCompositionOp != CompositionOp::OP_OVER) {
+          if (co != CompositionOp::OP_OVER) {
             ctx->SetOp(CompositionOp::OP_OVER);
           }
         }
       }
     }
   }
 
   return result;
@@ -3218,17 +3224,17 @@ ComputeDrawnSizeForBackground(const CSSS
 
 nsBackgroundLayerState
 nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext,
                                   nsIFrame* aForFrame,
                                   uint32_t aFlags,
                                   const nsRect& aBorderArea,
                                   const nsRect& aBGClipRect,
                                   const nsStyleImageLayers::Layer& aLayer,
-                                  bool aMask)
+                                  CompositionOp aCompositonOp)
 {
   /*
    * The properties we need to keep in mind when drawing style image
    * layers are:
    *
    *   background-image/ mask-image
    *   background-repeat/ mask-repeat
    *   background-attachment
@@ -3380,19 +3386,16 @@ nsCSSRendering::PrepareImageLayer(nsPres
     } else {
       repeatMode = ExtendMode::REPEAT_Y;
     }
   }
   state.mImageRenderer.SetExtendMode(repeatMode);
 
   state.mFillArea.IntersectRect(state.mFillArea, bgClipRect);
 
-  state.mCompositionOp = aMask ? GetGFXCompositeMode(aLayer.mComposite) :
-                                 GetGFXBlendMode(aLayer.mBlendMode);
-
   return state;
 }
 
 nsRect
 nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext,
                                        nsIFrame* aForFrame,
                                        const nsRect& aBorderArea,
                                        const nsRect& aClipRect,
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -310,17 +310,16 @@ struct nsBackgroundLayerState {
   typedef mozilla::gfx::CompositionOp CompositionOp;
 
   /**
    * @param aFlags some combination of nsCSSRendering::PAINTBG_* flags
    */
   nsBackgroundLayerState(nsIFrame* aForFrame, const nsStyleImage* aImage,
                          uint32_t aFlags)
     : mImageRenderer(aForFrame, aImage, aFlags)
-    , mCompositionOp(CompositionOp::OP_OVER)
   {}
 
   /**
    * The nsImageRenderer that will be used to draw the background.
    */
   nsImageRenderer mImageRenderer;
   /**
    * A rectangle that one copy of the image tile is mapped onto. Same
@@ -335,20 +334,16 @@ struct nsBackgroundLayerState {
    */
   nsRect mFillArea;
   /**
    * The anchor point that should be snapped to a pixel corner. Same
    * coordinate system as aBorderArea/aBGClipRect passed into
    * PrepareImageLayer.
    */
   nsPoint mAnchor;
-  /**
-   * The composition operation that the image should use.
-   */
-  CompositionOp mCompositionOp;
 };
 
 struct nsCSSRendering {
   typedef mozilla::gfx::CompositionOp CompositionOp;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::gfx::Rect Rect;
@@ -540,17 +535,17 @@ struct nsCSSRendering {
 
   static nsBackgroundLayerState
   PrepareImageLayer(nsPresContext* aPresContext,
                     nsIFrame* aForFrame,
                     uint32_t aFlags,
                     const nsRect& aBorderArea,
                     const nsRect& aBGClipRect,
                     const nsStyleImageLayers::Layer& aLayer,
-                    bool aMask = false);
+                    CompositionOp aCompositionOp = CompositionOp::OP_OVER);
 
   struct ImageLayerClipState {
     nsRect mBGClipArea;  // Affected by mClippedRadii
     nsRect mAdditionalBGClipArea;  // Not affected by mClippedRadii
     nsRect mDirtyRect;
     gfxRect mDirtyRectGfx;
 
     nscoord mRadii[8];
@@ -597,38 +592,43 @@ struct nsCSSRendering {
   };
   static DrawResult PaintBackground(nsPresContext* aPresContext,
                                     nsRenderingContext& aRenderingContext,
                                     nsIFrame* aForFrame,
                                     const nsRect& aDirtyRect,
                                     const nsRect& aBorderArea,
                                     uint32_t aFlags,
                                     nsRect* aBGClipRect = nullptr,
-                                    int32_t aLayer = -1);
+                                    int32_t aLayer = -1,
+                                    CompositionOp aCompositionOp = CompositionOp::OP_OVER);
 
 
   /**
    * Same as |PaintBackground|, except using the provided style structs.
    * This short-circuits the code that ensures that the root element's
    * background is drawn on the canvas.
    * The aLayer parameter allows you to paint a single layer of the background.
    * The default value for aLayer, -1, means that all layers will be painted.
    * The background color will only be painted if the back-most layer is also
    * being painted.
+   * aCompositionOp is only respected if a single layer is specified (aLayer != -1).
+   * If all layers are painted, the image layer's blend mode (or the mask
+   * layer's composition mode) will be used.
    */
   static DrawResult PaintBackgroundWithSC(nsPresContext* aPresContext,
                                           nsRenderingContext& aRenderingContext,
                                           nsIFrame* aForFrame,
                                           const nsRect& aDirtyRect,
                                           const nsRect& aBorderArea,
                                           nsStyleContext *aStyleContext,
                                           const nsStyleBorder& aBorder,
                                           uint32_t aFlags,
                                           nsRect* aBGClipRect = nullptr,
-                                          int32_t aLayer = -1);
+                                          int32_t aLayer = -1,
+                                          CompositionOp aCompositionOp = CompositionOp::OP_OVER);
 
   /**
    * Returns the rectangle covered by the given background layer image, taking
    * into account background positioning, sizing, and repetition, but not
    * clipping.
    */
   static nsRect GetBackgroundLayerRect(nsPresContext* aPresContext,
                                        nsIFrame* aForFrame,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2970,21 +2970,23 @@ nsDisplayBackgroundImage::Paint(nsDispla
 void
 nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
                                         nsRenderingContext* aCtx, const nsRect& aBounds,
                                         nsRect* aClipRect) {
   nsPoint offset = ToReferenceFrame();
   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
   CheckForBorderItem(this, flags);
 
+  const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
   image::DrawResult result =
     nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame,
                                     aBounds,
                                     nsRect(offset, mFrame->GetSize()),
-                                    flags, aClipRect, mLayer);
+                                    flags, aClipRect, mLayer,
+                                    nsCSSRendering::GetGFXBlendMode(layer.mBlendMode));
 
   nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
 }
 
 void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                          const nsDisplayItemGeometry* aGeometry,
                                                          nsRegion* aInvalidRegion)
 {
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -580,19 +580,17 @@ nsSVGIntegrationUtils::PaintFramesWithEf
       nsRenderingContext rc(target);
       nsCSSRendering::PaintBackgroundWithSC(aFrame->PresContext(),
                                             rc,
                                             aFrame,
                                             aDirtyRect,
                                             aBorderArea,
                                             firstFrame->StyleContext(),
                                             *aFrame->StyleBorder(),
-                                            flags,
-                                            nullptr,
-                                            -1);
+                                            flags);
       maskSurface = targetDT->Snapshot();
 
       // Compute mask transform.
       Matrix mat = ToMatrix(aContext.CurrentMatrix());
       mat.Invert();
       maskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat;
     }