Bug 1341101 part 5 - Support building WR DisplayItems for BackgroundImageLayers that are gradients r=mattwoodrow draft
authorRyan Hunt <rhunt@eqrion.net>
Mon, 20 Mar 2017 19:31:39 -0400
changeset 552233 305262c57c72a37839f776a9c5eb4b592c1f0055
parent 552232 974b138f2b2f0a854d0fcbb64892a2852a1ce8d4
child 552234 818e9381e6460c29a6fc76989b4d9305c5f8162f
push id51292
push userbmo:rhunt@eqrion.net
push dateTue, 28 Mar 2017 06:03:40 +0000
reviewersmattwoodrow
bugs1341101
milestone55.0a1
Bug 1341101 part 5 - Support building WR DisplayItems for BackgroundImageLayers that are gradients r=mattwoodrow MozReview-Commit-ID: BNLT8Wbp672
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRendering.h
layout/painting/nsImageRenderer.cpp
layout/painting/nsImageRenderer.h
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -1933,16 +1933,75 @@ nsCSSRendering::PaintStyleImageLayer(con
     }
 
     sc = aParams.frame->StyleContext();
   }
 
   return PaintStyleImageLayerWithSC(aParams, aRenderingCtx, sc, *aParams.frame->StyleBorder());
 }
 
+bool
+nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(nsPresContext& aPresCtx,
+                                                                nsIFrame *aFrame,
+                                                                const nsStyleBackground* aBackgroundStyle,
+                                                                int32_t aLayer)
+{
+  if (!aBackgroundStyle) {
+    return false;
+  }
+
+  MOZ_ASSERT(aFrame &&
+             aLayer >= 0 &&
+             (uint32_t)aLayer < aBackgroundStyle->mImage.mLayers.Length());
+
+  // We cannot draw native themed backgrounds
+  const nsStyleDisplay* displayData = aFrame->StyleDisplay();
+  if (displayData->UsedAppearance()) {
+    nsITheme *theme = aPresCtx.GetTheme();
+    if (theme && theme->ThemeSupportsWidget(&aPresCtx,
+                                            aFrame,
+                                            displayData->UsedAppearance())) {
+      return false;
+    }
+  }
+
+  // We only support painting gradients for a single style image layer
+  return aBackgroundStyle->mImage.mLayers[aLayer].mImage.GetType() == eStyleImageType_Gradient;
+}
+
+void
+nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(const PaintBGParams& aParams,
+                                                             mozilla::wr::DisplayListBuilder& aBuilder,
+                                                             mozilla::layers::WebRenderDisplayItemLayer* aLayer)
+{
+  NS_PRECONDITION(aParams.frame,
+                  "Frame is expected to be provided to BuildWebRenderDisplayItemsForStyleImageLayer");
+
+  nsStyleContext *sc;
+  if (!FindBackground(aParams.frame, &sc)) {
+    // We don't want to bail out if moz-appearance is set on a root
+    // node. If it has a parent content node, bail because it's not
+    // a root, otherwise keep going in order to let the theme stuff
+    // draw the background. The canvas really should be drawing the
+    // bg, but there's no way to hook that up via css.
+    if (!aParams.frame->StyleDisplay()->UsedAppearance()) {
+      return;
+    }
+
+    nsIContent* content = aParams.frame->GetContent();
+    if (!content || content->GetParent()) {
+      return;
+    }
+
+    sc = aParams.frame->StyleContext();
+  }
+
+  return BuildWebRenderDisplayItemsForStyleImageLayerWithSC(aParams, aBuilder, aLayer, sc, *aParams.frame->StyleBorder());
+}
+
 static bool
 IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::Side aSide)
 {
   if (aBorder.GetComputedBorder().Side(aSide) == 0)
     return true;
   switch (aBorder.GetBorderStyle(aSide)) {
   case NS_STYLE_BORDER_STYLE_SOLID:
   case NS_STYLE_BORDER_STYLE_GROOVE:
@@ -2642,16 +2701,71 @@ nsCSSRendering::PaintStyleImageLayerWith
         ctx->SetOp(CompositionOp::OP_OVER);
       }
     }
   }
 
   return result;
 }
 
+void
+nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                                                   mozilla::wr::DisplayListBuilder& aBuilder,
+                                                                   mozilla::layers::WebRenderDisplayItemLayer* aLayer,
+                                                                   nsStyleContext *aBackgroundSC,
+                                                                   const nsStyleBorder& aBorder)
+{
+  MOZ_ASSERT(CanBuildWebRenderDisplayItemsForStyleImageLayer(aParams.presCtx,
+                                                             aParams.frame,
+                                                             aBackgroundSC->StyleBackground(),
+                                                             aParams.layer));
+
+  MOZ_ASSERT(!(aParams.paintFlags & PAINTBG_MASK_IMAGE));
+
+  nscoord appUnitsPerPixel = aParams.presCtx.AppUnitsPerDevPixel();
+  ImageLayerClipState clipState;
+
+  clipState.mBGClipArea = *aParams.bgClipRect;
+  clipState.mCustomClip = true;
+  clipState.mHasRoundedCorners = false;
+  SetupDirtyRects(clipState.mBGClipArea, aParams.dirtyRect, appUnitsPerPixel,
+                  &clipState.mDirtyRectInAppUnits,
+                  &clipState.mDirtyRectInDevPx);
+
+  // Compute the outermost boundary of the area that might be painted.
+  // Same coordinate space as aParams.borderArea & aParams.bgClipRect.
+  Sides skipSides = aParams.frame->GetSkipSides();
+  nsRect paintBorderArea =
+    ::BoxDecorationRectForBackground(aParams.frame, aParams.borderArea,
+                                     skipSides, &aBorder);
+
+  const nsStyleImageLayers& layers = aBackgroundSC->StyleBackground()->mImage;
+  const nsStyleImageLayers::Layer& layer = layers.mLayers[aParams.layer];
+
+  // Skip the following layer painting code if we found the dirty region is
+  // empty or the current layer is not selected for drawing.
+  if (clipState.mDirtyRectInDevPx.IsEmpty()) {
+    return;
+  }
+
+  nsBackgroundLayerState state =
+    PrepareImageLayer(&aParams.presCtx, aParams.frame,
+                      aParams.paintFlags, paintBorderArea,
+                      clipState.mBGClipArea, layer, nullptr);
+
+  if (!state.mFillArea.IsEmpty()) {
+    state.mImageRenderer.BuildWebRenderDisplayItemsForLayer(&aParams.presCtx,
+                                   aBuilder, aLayer,
+                                   state.mDestArea, state.mFillArea,
+                                   state.mAnchor + paintBorderArea.TopLeft(),
+                                   clipState.mDirtyRectInAppUnits,
+                                   state.mRepeatSize, aParams.opacity);
+  }
+}
+
 nsRect
 nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
                                                  nsIFrame* aForFrame,
                                                  const nsRect& aBorderArea,
                                                  const nsStyleImageLayers::Layer& aLayer,
                                                  nsIFrame** aAttachedToFrame,
                                                  bool* aOutIsTransformedFixed)
 {
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -28,18 +28,23 @@ namespace mozilla {
 
 namespace gfx {
 struct Color;
 class DrawTarget;
 } // namespace gfx
 
 namespace layers {
 class ImageContainer;
+class WebRenderDisplayItemLayer;
 } // namespace layers
 
+namespace wr {
+class DisplayListBuilder;
+} // namespace wr
+
 enum class PaintBorderFlags : uint8_t
 {
   SYNC_DECODE_IMAGES = 1 << 0
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(PaintBorderFlags)
 
 } // namespace mozilla
 
@@ -445,17 +450,16 @@ struct nsCSSRendering {
        layer(aLayer),
        compositionOp(aCompositionOp),
        opacity(aOpacity) {}
   };
 
   static DrawResult PaintStyleImageLayer(const PaintBGParams& aParams,
                                          nsRenderingContext& aRenderingCtx);
 
-
   /**
    * Same as |PaintStyleImageLayer|, except using the provided style structs.
    * This short-circuits the code that ensures that the root element's
    * {background|mask} is drawn on the canvas.
    * The aLayer parameter allows you to paint a single layer of the
    * {background|mask}.
    * 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
@@ -464,16 +468,30 @@ struct nsCSSRendering {
    * If all layers are painted, the image layer's blend mode (or the mask
    * layer's composition mode) will be used.
    */
   static DrawResult PaintStyleImageLayerWithSC(const PaintBGParams& aParams,
                                                nsRenderingContext& aRenderingCtx,
                                                nsStyleContext *mBackgroundSC,
                                                const nsStyleBorder& aBorder);
 
+  static bool CanBuildWebRenderDisplayItemsForStyleImageLayer(nsPresContext& aPresCtx,
+                                                              nsIFrame *aFrame,
+                                                              const nsStyleBackground* aBackgroundStyle,
+                                                              int32_t aLayer);
+  static void BuildWebRenderDisplayItemsForStyleImageLayer(const PaintBGParams& aParams,
+                                                           mozilla::wr::DisplayListBuilder& aBuilder,
+                                                           mozilla::layers::WebRenderDisplayItemLayer* aLayer);
+
+  static void BuildWebRenderDisplayItemsForStyleImageLayerWithSC(const PaintBGParams& aParams,
+                                                                 mozilla::wr::DisplayListBuilder& aBuilder,
+                                                                 mozilla::layers::WebRenderDisplayItemLayer* aLayer,
+                                                                 nsStyleContext *mBackgroundSC,
+                                                                 const nsStyleBorder& aBorder);
+
   /**
    * 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,
                                        const nsRect& aBorderArea,
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -3,16 +3,17 @@
 /* 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/. */
 
 /* utility functions for drawing borders and backgrounds */
 
 #include "nsImageRenderer.h"
 #include "nsCSSRenderingGradients.h"
+#include "mozilla/webrender/WebRenderAPI.h"
 
 nsSize
 CSSSizeOrRatio::ComputeConcreteSize() const
 {
   NS_ASSERTION(CanComputeConcreteSize(), "Cannot compute");
   if (mHasWidth && mHasHeight) {
     return nsSize(mWidth, mHeight);
   }
@@ -438,17 +439,16 @@ RGBALuminanceOperation(uint8_t *aData,
       *pixel = (((((*pixel & 0x00FF0000) >> 16) * redFactor) +
                  (((*pixel & 0x0000FF00) >>  8) * greenFactor) +
                   ((*pixel & 0x000000FF)        * blueFactor)) >> 8) << 24;
       pixel++;
     }
   }
 }
 
-
 DrawResult
 nsImageRenderer::Draw(nsPresContext*       aPresContext,
                       nsRenderingContext&  aRenderingContext,
                       const nsRect&        aDirtyRect,
                       const nsRect&        aDest,
                       const nsRect&        aFill,
                       const nsPoint&       aAnchor,
                       const nsSize&        aRepeatSize,
@@ -559,16 +559,54 @@ nsImageRenderer::Draw(nsPresContext*    
                     Rect(0, 0, tmpDTRect.width, tmpDTRect.height),
                     DrawSurfaceOptions(SamplingFilter::POINT),
                     DrawOptions(1.0f, aRenderingContext.ThebesContext()->CurrentOp()));
   }
 
   return result;
 }
 
+void
+nsImageRenderer::BuildWebRenderDisplayItems(nsPresContext*       aPresContext,
+                                            mozilla::wr::DisplayListBuilder&            aBuilder,
+                                            mozilla::layers::WebRenderDisplayItemLayer* aLayer,
+                                            const nsRect&        aDirtyRect,
+                                            const nsRect&        aDest,
+                                            const nsRect&        aFill,
+                                            const nsPoint&       aAnchor,
+                                            const nsSize&        aRepeatSize,
+                                            const CSSIntRect&    aSrc,
+                                            float                aOpacity)
+{
+  if (!IsReady()) {
+    NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
+    return;
+  }
+  if (aDest.IsEmpty() || aFill.IsEmpty() ||
+      mSize.width <= 0 || mSize.height <= 0) {
+    return;
+  }
+
+  switch (mType) {
+    case eStyleImageType_Gradient:
+    {
+      Maybe<nsCSSGradientRenderer> renderer =
+        nsCSSGradientRenderer::Create(aPresContext, mGradientData,
+                                   aDest, aFill, aRepeatSize, aSrc, mSize);
+
+      if (renderer) {
+        renderer->BuildWebRenderDisplayItems(aBuilder, aLayer, aOpacity);
+      }
+      break;
+    }
+    default:
+      break;
+  }
+}
+
 already_AddRefed<gfxDrawable>
 nsImageRenderer::DrawableForElement(const nsRect& aImageRect,
                                     gfxContext&  aContext)
 {
   NS_ASSERTION(mType == eStyleImageType_Element,
                "DrawableForElement only makes sense if backed by an element");
   if (mPaintServerFrame) {
     // XXX(seth): In order to not pass FLAG_SYNC_DECODE_IMAGES here,
@@ -619,16 +657,44 @@ nsImageRenderer::DrawLayer(nsPresContext
   return Draw(aPresContext, aRenderingContext,
               aDirty, aDest, aFill, aAnchor, aRepeatSize,
               CSSIntRect(0, 0,
                          nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
                          nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
               aOpacity);
 }
 
+void
+nsImageRenderer::BuildWebRenderDisplayItemsForLayer(nsPresContext*       aPresContext,
+                                                    mozilla::wr::DisplayListBuilder& aBuilder,
+                                                    WebRenderDisplayItemLayer*       aLayer,
+                                                    const nsRect&        aDest,
+                                                    const nsRect&        aFill,
+                                                    const nsPoint&       aAnchor,
+                                                    const nsRect&        aDirty,
+                                                    const nsSize&        aRepeatSize,
+                                                    float                aOpacity)
+{
+  if (!IsReady()) {
+    NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
+    return;
+  }
+  if (aDest.IsEmpty() || aFill.IsEmpty() ||
+      mSize.width <= 0 || mSize.height <= 0) {
+    return;
+  }
+
+  BuildWebRenderDisplayItems(aPresContext, aBuilder, aLayer,
+                             aDirty, aDest, aFill, aAnchor, aRepeatSize,
+                             CSSIntRect(0, 0,
+                                        nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
+                                        nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
+                             aOpacity);
+}
+
 /**
  * Compute the size and position of the master copy of the image. I.e., a single
  * tile used to fill the dest rect.
  * aFill The destination rect to be filled
  * aHFill and aVFill are the repeat patterns for the component -
  * NS_STYLE_BORDER_IMAGE_REPEAT_* - i.e., how a tiling unit is used to fill aFill
  * aUnitSize The size of the source rect in dest coords.
  */
--- a/layout/painting/nsImageRenderer.h
+++ b/layout/painting/nsImageRenderer.h
@@ -8,16 +8,24 @@
 
 #include "nsLayoutUtils.h"
 #include "nsStyleStruct.h"
 #include "Units.h"
 
 class gfxDrawable;
 namespace mozilla {
 
+namespace layers {
+class WebRenderDisplayItemLayer;
+} // namespace layers
+
+namespace wr {
+class DisplayListBuilder;
+} // namespace wr
+
 // A CSSSizeOrRatio represents a (possibly partially specified) size for use
 // in computing image sizes. Either or both of the width and height might be
 // given. A ratio of width to height may also be given. If we at least two
 // of these then we can compute a concrete size, that is a width and height.
 struct CSSSizeOrRatio
 {
   CSSSizeOrRatio()
     : mRatio(0, 0)
@@ -188,16 +196,31 @@ public:
                        const nsRect&        aDest,
                        const nsRect&        aFill,
                        const nsPoint&       aAnchor,
                        const nsRect&        aDirty,
                        const nsSize&        aRepeatSize,
                        float                aOpacity);
 
   /**
+   * Builds WebRender DisplayItems for an image using
+   * {background|mask}-specific arguments.
+   * @see nsLayoutUtils::DrawImage() for parameters.
+   */
+  void BuildWebRenderDisplayItemsForLayer(nsPresContext*       aPresContext,
+                                          mozilla::wr::DisplayListBuilder& aBuilder,
+                                          mozilla::layers::WebRenderDisplayItemLayer* aLayer,
+                                          const nsRect&        aDest,
+                                          const nsRect&        aFill,
+                                          const nsPoint&       aAnchor,
+                                          const nsRect&        aDirty,
+                                          const nsSize&        aRepeatSize,
+                                          float                aOpacity);
+
+  /**
    * Draw the image to a single component of a border-image style rendering.
    * aFill The destination rect to be drawn into
    * aSrc is the part of the image to be rendered into a tile (aUnitSize in
    * aFill), if aSrc and the dest tile are different sizes, the image will be
    * scaled to map aSrc onto the dest tile.
    * aHFill and aVFill are the repeat patterns for the component -
    * NS_STYLE_BORDER_IMAGE_REPEAT_*
    * aUnitSize The scaled size of a single source rect (in destination coords)
@@ -252,16 +275,34 @@ private:
                   const nsRect&        aDest,
                   const nsRect&        aFill,
                   const nsPoint&       aAnchor,
                   const nsSize&        aRepeatSize,
                   const mozilla::CSSIntRect& aSrc,
                   float                aOpacity = 1.0);
 
   /**
+   * Builds WebRender DisplayItems for the image.
+   * aSrc is a rect on the source image which will be mapped to aDest; it's
+   * currently only used for gradients.
+   *
+   * @see nsLayoutUtils::DrawImage() for other parameters.
+   */
+  void BuildWebRenderDisplayItems(nsPresContext*       aPresContext,
+                                  mozilla::wr::DisplayListBuilder& aBuilder,
+                                  mozilla::layers::WebRenderDisplayItemLayer* aLayer,
+                                  const nsRect&        aDirtyRect,
+                                  const nsRect&        aDest,
+                                  const nsRect&        aFill,
+                                  const nsPoint&       aAnchor,
+                                  const nsSize&        aRepeatSize,
+                                  const mozilla::CSSIntRect& aSrc,
+                                  float                aOpacity = 1.0);
+
+  /**
    * Helper method for creating a gfxDrawable from mPaintServerFrame or
    * mImageElementSurface.
    * Requires mType is eStyleImageType_Element.
    * Returns null if we cannot create the drawable.
    */
   already_AddRefed<gfxDrawable> DrawableForElement(const nsRect& aImageRect,
                                                    gfxContext&  aContext);