Bug 1341156 - Add border image support. r=mattwoodrow draft
authorMorris Tseng <mtseng@mozilla.com>
Mon, 06 Mar 2017 10:40:06 +0800
changeset 493799 49b5b506841de1901411d050392712d2a7f90c6e
parent 493798 2ad7b76750ae812faf26aa79604c67750dbcb5bf
child 547930 41294b60a8b1b9d17782fd70a7c1ee29b0ed5573
push id47843
push usermtseng@mozilla.com
push dateMon, 06 Mar 2017 02:45:16 +0000
reviewersmattwoodrow
bugs1341156
milestone54.0a1
Bug 1341156 - Add border image support. r=mattwoodrow MozReview-Commit-ID: 146FCaqEoi1 * * * commit 363ddb5f00aa7d71c5a2916a645bd069d2658ab5 Author: Morris Tseng <mtseng@mozilla.com> p4 MozReview-Commit-ID: Ax2WjDC0V5q
layout/painting/nsCSSRenderingBorders.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/layout/painting/nsCSSRenderingBorders.h
+++ b/layout/painting/nsCSSRenderingBorders.h
@@ -299,16 +299,18 @@ private:
   nsMargin mSlice;
   nsMargin mWidths;
   nsMargin mImageOutset;
   nsRect mArea;
   nsRect mClip;
   uint8_t mRepeatModeHorizontal;
   uint8_t mRepeatModeVertical;
   uint8_t mFill;
+
+  friend class nsDisplayBorder;
 };
 
 namespace mozilla {
 #ifdef DEBUG_NEW_BORDERS
 #include <stdarg.h>
 
 static inline void PrintAsString(const mozilla::gfx::Point& p) {
   fprintf (stderr, "[%f,%f]", p.x, p.y);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -77,16 +77,17 @@
 #include "mozilla/GeckoRestyleManager.h"
 #include "nsCaret.h"
 #include "nsISelection.h"
 #include "nsDOMTokenList.h"
 #include "mozilla/RuleNodeCacheConditions.h"
 #include "nsCSSProps.h"
 #include "nsPluginFrame.h"
 #include "nsSVGMaskFrame.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/WebRenderDisplayItemLayer.h"
 #include "mozilla/layers/WebRenderMessages.h"
 #include "mozilla/layers/WebRenderDisplayItemLayer.h"
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount().
 #ifdef GetCurrentTime
@@ -2745,18 +2746,19 @@ nsDisplayItem::GetClippedBounds(nsDispla
   return GetClip().ApplyNonRoundedIntersection(r);
 }
 
 already_AddRefed<Layer>
 nsDisplayItem::BuildDisplayItemLayer(nsDisplayListBuilder* aBuilder,
                                      LayerManager* aManager,
                                      const ContainerLayerParameters& aContainerParameters)
 {
-  RefPtr<DisplayItemLayer> layer = static_cast<DisplayItemLayer*>
-    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+  RefPtr<Layer> oldLayer = aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this);
+  RefPtr<DisplayItemLayer> layer = oldLayer ? oldLayer->AsDisplayItemLayer() : nullptr;
+
   if (!layer) {
     layer = aManager->CreateDisplayItemLayer();
 
     if (!layer) {
       return nullptr;
     }
   }
 
@@ -4564,42 +4566,83 @@ nsDisplayBorder::GetLayerState(nsDisplay
   Maybe<nsCSSBorderRenderer> br =
     nsCSSRendering::CreateBorderRenderer(mFrame->PresContext(),
                                          nullptr,
                                          mFrame,
                                          nsRect(),
                                          nsRect(offset, mFrame->GetSize()),
                                          mFrame->StyleContext(),
                                          mFrame->GetSkipSides());
-  if (!br) {
+
+  const nsStyleBorder *styleBorder = mFrame->StyleContext()->StyleBorder();
+  const nsStyleImage* image = &styleBorder->mBorderImageSource;
+  mBorderImageRenderer = Nothing();
+  if ((!image || image->GetType() != eStyleImageType_Image) && !br) {
     return LAYER_NONE;
   }
 
   LayersBackend backend = aManager->GetBackendType();
   if (backend == layers::LayersBackend::LAYERS_WR) {
-    bool hasCompositeColors;
-    br->AllBordersSolid(&hasCompositeColors);
-    if (hasCompositeColors) {
-      return LAYER_NONE;
-    }
-
-    NS_FOR_CSS_SIDES(i) {
-      mColors[i] = ToDeviceColor(br->mBorderColors[i]);
-      mWidths[i] = br->mBorderWidths[i];
-      mBorderStyles[i] = br->mBorderStyles[i];
-    }
-    NS_FOR_CSS_FULL_CORNERS(corner) {
-      mCorners[corner] = LayerSize(br->mBorderRadii[corner].width, br->mBorderRadii[corner].height);
-    }
-
-    mRect = ViewAs<LayerPixel>(br->mOuterRect);
+    if (br) {
+      bool hasCompositeColors;
+      br->AllBordersSolid(&hasCompositeColors);
+      if (hasCompositeColors) {
+        return LAYER_NONE;
+      }
+
+      NS_FOR_CSS_SIDES(i) {
+        mColors[i] = ToDeviceColor(br->mBorderColors[i]);
+        mWidths[i] = br->mBorderWidths[i];
+        mBorderStyles[i] = br->mBorderStyles[i];
+      }
+
+      NS_FOR_CSS_FULL_CORNERS(corner) {
+        mCorners[corner] = LayerSize(br->mBorderRadii[corner].width, br->mBorderRadii[corner].height);
+      }
+
+      mRect = ViewAs<LayerPixel>(br->mOuterRect);
+    } else {
+      if (styleBorder->mBorderImageRepeatH == NS_STYLE_BORDER_IMAGE_REPEAT_ROUND ||
+          styleBorder->mBorderImageRepeatH == NS_STYLE_BORDER_IMAGE_REPEAT_SPACE ||
+          styleBorder->mBorderImageRepeatV == NS_STYLE_BORDER_IMAGE_REPEAT_ROUND ||
+          styleBorder->mBorderImageRepeatV == NS_STYLE_BORDER_IMAGE_REPEAT_SPACE) {
+        // WebRender not supports this currently
+        return LAYER_NONE;
+      }
+
+      uint32_t flags = 0;
+      if (aBuilder->ShouldSyncDecodeImages()) {
+        flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
+      }
+
+      image::DrawResult result;
+      Maybe<nsCSSBorderImageRenderer> renderer =
+        nsCSSBorderImageRenderer::CreateBorderImageRenderer(mFrame->PresContext(),
+                                                            mFrame,
+                                                            nsRect(offset, mFrame->GetSize()),
+                                                            *(mFrame->StyleContext()->StyleBorder()),
+                                                            mVisibleRect,
+                                                            mFrame->GetSkipSides(),
+                                                            flags,
+                                                            &result);
+
+      if (!renderer) {
+        return LAYER_NONE;
+      }
+
+      mBorderImageRenderer = renderer;
+    }
 
     return LAYER_ACTIVE;
   }
 
+  if (!br) {
+    return LAYER_NONE;
+  }
+
   bool hasCompositeColors;
   if (!br->AllBordersSolid(&hasCompositeColors) || hasCompositeColors) {
     return LAYER_NONE;
   }
 
   // We don't support this yet as we don't copy the values to
   // the layer, and BasicBorderLayer doesn't support it yet.
   if (!br->mNoBorderRadius) {
@@ -4630,34 +4673,99 @@ nsDisplayBorder::GetLayerState(nsDisplay
   return LAYER_ACTIVE;
 }
 
 already_AddRefed<Layer>
 nsDisplayBorder::BuildLayer(nsDisplayListBuilder* aBuilder,
                             LayerManager* aManager,
                             const ContainerLayerParameters& aContainerParameters)
 {
-  RefPtr<BorderLayer> layer = static_cast<BorderLayer*>
-    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+  if (mBorderImageRenderer) {
+    return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
+  }
+
+  RefPtr<Layer> oldLayer = aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this);
+  RefPtr<BorderLayer> layer = oldLayer ? oldLayer->AsBorderLayer() : nullptr;
+
   if (!layer) {
     layer = aManager->CreateBorderLayer();
     if (!layer)
       return nullptr;
   }
   layer->SetRect(mRect);
   layer->SetCornerRadii(mCorners);
   layer->SetColors(mColors);
   layer->SetWidths(mWidths);
   layer->SetStyles(mBorderStyles);
   layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
                                                       aContainerParameters.mOffset.y, 0));
   return layer.forget();
 }
 
 void
+nsDisplayBorder::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
+                                         nsTArray<WebRenderParentCommand>& aParentCommands,
+                                         WebRenderDisplayItemLayer* aLayer)
+{
+  // Only support border-image currently
+  MOZ_ASSERT(mBorderImageRenderer);
+
+  nsDisplayListBuilder* builder = aLayer->GetDisplayListBuilder();
+  uint32_t flags = builder->ShouldSyncDecodeImages() ?
+                   imgIContainer::FLAG_SYNC_DECODE :
+                   imgIContainer::FLAG_NONE;
+
+  RefPtr<imgIContainer> img = mBorderImageRenderer->mImageRenderer.GetImage();
+  RefPtr<layers::ImageContainer> container = img->GetImageContainer(aLayer->WrManager(), flags);
+  if (!container) {
+    return;
+  }
+
+  uint64_t externalImageId = aLayer->SendImageContainer(container);
+
+  const int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+  Rect destRect =
+    NSRectToRect(mBorderImageRenderer->mArea, appUnitsPerDevPixel);
+  Rect destRectTransformed = aLayer->RelativeToParent(destRect);
+  IntRect dest = RoundedToInt(destRectTransformed);
+
+  IntRect clip = dest;
+  if (!mBorderImageRenderer->mClip.IsEmpty()) {
+    Rect clipRect =
+      NSRectToRect(mBorderImageRenderer->mClip, appUnitsPerDevPixel);
+    Rect clipRectTransformed = aLayer->RelativeToParent(clipRect);
+    clip = RoundedToInt(clipRectTransformed);
+  }
+
+  float widths[4];
+  float slice[4];
+  float outset[4];
+  NS_FOR_CSS_SIDES(i) {
+    slice[i] = (float)(mBorderImageRenderer->mSlice.Side(i)) / appUnitsPerDevPixel;
+    widths[i] = (float)(mBorderImageRenderer->mWidths.Side(i)) / appUnitsPerDevPixel;
+    outset[i] = (float)(mBorderImageRenderer->mImageOutset.Side(i)) / appUnitsPerDevPixel;
+  }
+
+  WrImageKey key;
+  key.mNamespace = aLayer->WrBridge()->GetNamespace();
+  key.mHandle = aLayer->WrBridge()->GetNextResourceId();
+  aParentCommands.AppendElement(OpAddExternalImage(externalImageId, key));
+  aBuilder.PushBorderImage(wr::ToWrRect(dest), wr::ToWrRect(clip),
+                           wr::ToWrBorderWidths(widths[0], widths[1], widths[2], widths[3]),
+                           key,
+                           wr::ToWrNinePatchDescriptor(
+                             (float)(mBorderImageRenderer->mImageSize.width) / appUnitsPerDevPixel,
+                             (float)(mBorderImageRenderer->mImageSize.height) / appUnitsPerDevPixel,
+                             wr::ToWrSideOffsets2Du32(slice[0], slice[1], slice[2], slice[3])),
+                           wr::ToWrSideOffsets2Df32(outset[0], outset[1], outset[2], outset[3]),
+                           wr::ToWrRepeatMode(mBorderImageRenderer->mRepeatModeHorizontal),
+                           wr::ToWrRepeatMode(mBorderImageRenderer->mRepeatModeVertical));
+}
+
+void
 nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
                        nsRenderingContext* aCtx) {
   nsPoint offset = ToReferenceFrame();
 
   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
                          ? PaintBorderFlags::SYNC_DECODE_IMAGES
                          : PaintBorderFlags();
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2853,19 +2853,22 @@ public:
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) override;
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
+  virtual void CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                       nsTArray<WebRenderParentCommand>& aParentCommands,
+                                       WebRenderDisplayItemLayer* aLayer) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
   NS_DISPLAY_DECL_NAME("Border", TYPE_BORDER)
-  
+
   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
 
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) override;
 
   virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
   {
@@ -2877,16 +2880,19 @@ protected:
   nsRegion CalculateBounds(const nsStyleBorder& aStyleBorder);
 
   mozilla::Array<mozilla::gfx::Color, 4> mColors;
   mozilla::Array<mozilla::LayerCoord, 4> mWidths;
   mozilla::Array<mozilla::LayerSize, 4> mCorners;
   mozilla::Array<uint8_t, 4> mBorderStyles;
   mozilla::LayerRect mRect;
 
+  // For border image
+  mozilla::Maybe<nsCSSBorderImageRenderer> mBorderImageRenderer;
+
   nsRect mBounds;
 };
 
 /**
  * A simple display item that just renders a solid color across the
  * specified bounds. For canvas frames (in the CSS sense) we split off the
  * drawing of the background color into this class (from nsDisplayBackground
  * via nsDisplayCanvasBackground). This is done so that we can always draw a