Bug 1341156 - Add border image support. r=mattwoodrow draft
authorMorris Tseng <mtseng@mozilla.com>
Thu, 23 Feb 2017 16:52:17 +0800
changeset 490715 13cfaf15819f509b69ffb6d68af36b3b713d073a
parent 490714 6cfe78696c87f413d56c3bf75a5bdb4835ec5ba0
child 490716 9adb4766123e70ff47e15c8fd9c482766e9b72df
push id47204
push userbmo:mtseng@mozilla.com
push dateWed, 01 Mar 2017 07:32:07 +0000
reviewersmattwoodrow
bugs1341156
milestone54.0a1
Bug 1341156 - Add border image support. r=mattwoodrow MozReview-Commit-ID: 146FCaqEoi1
gfx/layers/Layers.h
gfx/layers/wr/WebRenderBorderLayer.cpp
layout/painting/nsCSSRenderingBorders.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -12,16 +12,17 @@
 #include <sys/types.h>                  // for int32_t, int64_t
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for LayerMargin, LayerPoint, ParentLayerIntRect
 #include "gfxContext.h"
 #include "gfxTypes.h"
 #include "gfxPoint.h"                   // for gfxPoint
 #include "gfxRect.h"                    // for gfxRect
 #include "gfx2DGlue.h"
+#include "imgIContainer.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2, etc
 #include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/EventForwards.h"      // for nsPaintEvent
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/Poison.h"
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
@@ -2541,16 +2542,58 @@ public:
 
   virtual void SetStyles(const BorderStyles& aBorderStyles)
   {
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Widths", this));
     PodCopy(&mBorderStyles[0], &aBorderStyles[0], 4);
     Mutated();
   }
 
+  virtual void SetImage(imgIContainer* aImage)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Image", this));
+    mImage = aImage;
+    Mutated();
+  }
+
+  virtual void SetImageSize(const mozilla::LayerSize& aSize)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ImageSize", this));
+    mImageSize = aSize;
+    Mutated();
+  }
+
+  virtual void SetSlice(const mozilla::Array<mozilla::LayerCoord, 4>& aSlice)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Slice", this));
+    PodCopy(&mSlice[0], &aSlice[0], 4);
+    Mutated();
+  }
+
+  virtual void SetOutset(const mozilla::Array<mozilla::LayerCoord, 4>& aOutset)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Outset", this));
+    PodCopy(&mOutset[0], &aOutset[0], 4);
+    Mutated();
+  }
+
+  virtual void SetRepeatModeHorizontal(uint8_t aRepeatMode)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) RepeatModeHorizontal", this));
+    mRepeatModeHorizontal = aRepeatMode;
+    Mutated();
+  }
+
+  virtual void SetRepeatModeVertical(uint8_t aRepeatMode)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) RepeatModeVertical", this));
+    mRepeatModeVertical = aRepeatMode;
+    Mutated();
+  }
+
   MOZ_LAYER_DECL_NAME("BorderLayer", TYPE_BORDER)
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
     mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
     ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
@@ -2569,16 +2612,24 @@ protected:
 
   virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
 
   BorderColors mColors;
   LayerRect mRect;
   BorderCorners mCorners;
   BorderWidths mWidths;
   BorderStyles mBorderStyles;
+
+  // For border image
+  RefPtr<imgIContainer> mImage;
+  mozilla::LayerSize mImageSize;
+  mozilla::Array<mozilla::LayerCoord, 4> mSlice;
+  mozilla::Array<mozilla::LayerCoord, 4> mOutset;
+  uint8_t mRepeatModeHorizontal;
+  uint8_t mRepeatModeVertical;
 };
 
 /**
  * A Layer for HTML Canvas elements.  It's backed by either a
  * gfxASurface or a GLContext (for WebGL layers), and has some control
  * for intelligent updating from the source if necessary (for example,
  * if hardware compositing is not available, for reading from the GL
  * buffer into an image surface that we can layer composite.)
--- a/gfx/layers/wr/WebRenderBorderLayer.cpp
+++ b/gfx/layers/wr/WebRenderBorderLayer.cpp
@@ -59,25 +59,53 @@ WebRenderBorderLayer::RenderLayer()
       OpDPPushStackingContext(wr::ToWrRect(relBounds),
                               wr::ToWrRect(overflow),
                               Nothing(),
                               1.0f,
                               GetAnimations(),
                               transform,
                               WrMixBlendMode::Normal,
                               FrameMetrics::NULL_SCROLL_ID));
-  WrBridge()->AddWebRenderCommand(
-    OpDPPushBorder(wr::ToWrRect(rect), wr::ToWrRect(clip),
-                   wr::ToWrBorderWidth(mWidths[0], mWidths[1], mWidths[2], mWidths[3]),
-                   wr::ToWrNormalBorder(
-                     wr::ToWrBorderSide(mColors[0], mBorderStyles[0]),
-                     wr::ToWrBorderSide(mColors[1], mBorderStyles[1]),
-                     wr::ToWrBorderSide(mColors[2], mBorderStyles[2]),
-                     wr::ToWrBorderSide(mColors[3], mBorderStyles[3]),
-                     wr::ToWrBorderRadius(mCorners[0], mCorners[1], mCorners[3], mCorners[2]))));
+
+  if (!mImage) {
+    WrBridge()->AddWebRenderCommand(
+      OpDPPushBorder(wr::ToWrRect(rect), wr::ToWrRect(clip),
+                     wr::ToWrBorderWidth(mWidths[0], mWidths[1], mWidths[2], mWidths[3]),
+                     wr::ToWrNormalBorder(
+                       wr::ToWrBorderSide(mColors[0], mBorderStyles[0]),
+                       wr::ToWrBorderSide(mColors[1], mBorderStyles[1]),
+                       wr::ToWrBorderSide(mColors[2], mBorderStyles[2]),
+                       wr::ToWrBorderSide(mColors[3], mBorderStyles[3]),
+                       wr::ToWrBorderRadius(mCorners[0], mCorners[1], mCorners[3], mCorners[2]))));
+  } else {
+    uint32_t flags = imgIContainer::FLAG_NONE;
+
+    RefPtr<layers::ImageContainer> container =
+      mImage->GetImageContainer(WrManager(), flags);
+    if (!container) {
+      return;
+    }
+
+    uint64_t externalImageId = SendImageContainer(container);
+
+    WrImageKey key;
+    key.mNamespace = WrBridge()->GetNamespace();
+    key.mHandle = WrBridge()->GetNextResourceId();
+    WrBridge()->AddWebRenderCommand(OpAddExternalImage(LayerIntRegion(), externalImageId, key));
+    WrBridge()->AddWebRenderCommand(
+      OpDPPushBorderImage(wr::ToWrRect(rect), wr::ToWrRect(clip),
+                          wr::ToWrBorderWidth(mWidths[0], mWidths[1], mWidths[2], mWidths[3]),
+                          key,
+                          wr::ToWrNinePatchDescriptor(
+                            mImageSize.width, mImageSize.height,
+                            wr::ToWrSideOffsets2Du32(mSlice[0], mSlice[1], mSlice[2], mSlice[3])),
+                          wr::ToWrSideOffsets2Df32(mOutset[0], mOutset[1], mOutset[2], mOutset[3]),
+                          wr::ToWrRepeatMode(mRepeatModeHorizontal),
+                          wr::ToWrRepeatMode(mRepeatModeVertical)));
+  }
   WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
 }
 
 uint64_t
 WebRenderBorderLayer::SendImageContainer(ImageContainer* aContainer)
 {
   if (mImageContainer != aContainer) {
     AutoLockImage autoLock(aContainer);
--- 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
@@ -4558,42 +4558,95 @@ 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;
+  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()),
+                                                            nsRect(),
+                                                            mFrame->GetSkipSides(),
+                                                            flags,
+                                                            &result);
+
+      if (!renderer) {
+        return LAYER_NONE;
+      }
+
+      nscoord twipsPerPixel = mFrame->PresContext()->DevPixelsToAppUnits(1);
+
+      mImage = renderer->mImageRenderer.GetImage();
+      mRepeatModeHorizontal = renderer->mRepeatModeHorizontal;
+      mRepeatModeVertical = renderer->mRepeatModeVertical;
+      mImageSize.width = (float)(renderer->mImageSize.width) / twipsPerPixel;
+      mImageSize.height = (float)(renderer->mImageSize.height) / twipsPerPixel;
+      NS_FOR_CSS_SIDES(i) {
+        mSlice[i] = (float)(renderer->mSlice.Side(i)) / twipsPerPixel;
+        mWidths[i] = (float)(renderer->mWidths.Side(i)) / twipsPerPixel;
+        mOutset[i] = (float)(renderer->mImageOutset.Side(i)) / twipsPerPixel;
+      }
+
+      mRect = ViewAs<LayerPixel>(NSRectToRect(renderer->mArea, twipsPerPixel));
+    }
 
     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) {
@@ -4631,23 +4684,33 @@ nsDisplayBorder::BuildLayer(nsDisplayLis
 {
   RefPtr<BorderLayer> layer = static_cast<BorderLayer*>
     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
   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));
+  if (!mImage) {
+    layer->SetCornerRadii(mCorners);
+    layer->SetColors(mColors);
+    layer->SetStyles(mBorderStyles);
+  } else {
+    layer->SetImage(mImage);
+    layer->SetImageSize(mImageSize);
+    layer->SetSlice(mSlice);
+    layer->SetOutset(mOutset);
+    layer->SetRepeatModeVertical(mRepeatModeVertical);
+    layer->SetRepeatModeHorizontal(mRepeatModeHorizontal);
+  }
   return layer.forget();
 }
 
 void
 nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
                        nsRenderingContext* aCtx) {
   nsPoint offset = ToReferenceFrame();
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2870,16 +2870,24 @@ 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
+  RefPtr<imgIContainer> mImage;
+  mozilla::LayerSize mImageSize;
+  mozilla::Array<mozilla::LayerCoord, 4> mSlice;
+  mozilla::Array<mozilla::LayerCoord, 4> mOutset;
+  uint8_t mRepeatModeHorizontal;
+  uint8_t mRepeatModeVertical;
+
   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