Bug 1347469 - Add support for gradient border. r=mattwoodrow draft
authorMorris Tseng <mtseng@mozilla.com>
Tue, 14 Mar 2017 11:59:13 +0800
changeset 554246 71b8bd0c87f103758058b4c1c53564c01e289a41
parent 554245 92b85f86cc21c9bc876f887e8e48f6935ca34ce5
child 622285 62bbfe82cc3e86b4d9974ecfb00212835002d358
push id51878
push userbmo:mtseng@mozilla.com
push dateFri, 31 Mar 2017 08:53:32 +0000
reviewersmattwoodrow
bugs1347469
milestone55.0a1
Bug 1347469 - Add support for gradient border. r=mattwoodrow MozReview-Commit-ID: 7RfVsPFWhlo
layout/painting/nsCSSRenderingGradients.cpp
layout/painting/nsCSSRenderingGradients.h
layout/painting/nsDisplayList.cpp
layout/painting/nsImageRenderer.cpp
layout/painting/nsImageRenderer.h
--- a/layout/painting/nsCSSRenderingGradients.cpp
+++ b/layout/painting/nsCSSRenderingGradients.cpp
@@ -1004,39 +1004,52 @@ nsCSSGradientRenderer::Paint(gfxContext&
       }
       aContext.Fill();
       aContext.SetMatrix(ctm);
     }
   }
 }
 
 void
+nsCSSGradientRenderer::BuildWebRenderParameters(float aOpacity,
+                                                WrGradientExtendMode& aMode,
+                                                nsTArray<WrGradientStop>& aStops,
+                                                LayoutDevicePoint& aLineStart,
+                                                LayoutDevicePoint& aLineEnd)
+{
+  bool isRepeat = mGradient->mRepeating || mForceRepeatToCoverTiles;
+  aMode = isRepeat ? WrGradientExtendMode::Repeat : WrGradientExtendMode::Clamp;
+
+  aStops.SetLength(mStops.Length());
+  for(uint32_t i = 0; i < mStops.Length(); i++) {
+    aStops[i].color.r = mStops[i].mColor.r;
+    aStops[i].color.g = mStops[i].mColor.g;
+    aStops[i].color.b = mStops[i].mColor.b;
+    aStops[i].color.a = mStops[i].mColor.a * aOpacity;
+    aStops[i].offset = mStops[i].mPosition;
+  }
+
+  aLineStart = LayoutDevicePoint(mLineStart.x, mLineStart.y);
+  aLineEnd = LayoutDevicePoint(mLineEnd.x, mLineEnd.y);
+}
+
+void
 nsCSSGradientRenderer::BuildWebRenderDisplayItems(wr::DisplayListBuilder& aBuilder,
                                                   layers::WebRenderDisplayItemLayer* aLayer,
                                                   float aOpacity)
 {
-  bool isRepeat = mGradient->mRepeating || mForceRepeatToCoverTiles;
-  WrGradientExtendMode extendMode = isRepeat ? WrGradientExtendMode::Repeat : WrGradientExtendMode::Clamp;
-
-  nsTArray<WrGradientStop> stops(mStops.Length());
-  stops.SetLength(mStops.Length());
-  for(uint32_t i = 0; i < mStops.Length(); i++) {
-    stops[i].color.r = mStops[i].mColor.r;
-    stops[i].color.g = mStops[i].mColor.g;
-    stops[i].color.b = mStops[i].mColor.b;
-    stops[i].color.a = mStops[i].mColor.a * aOpacity;
-    stops[i].offset = mStops[i].mPosition;
-  }
+  WrGradientExtendMode extendMode;
+  nsTArray<WrGradientStop> stops;
+  LayoutDevicePoint lineStart;
+  LayoutDevicePoint lineEnd;
+  BuildWebRenderParameters(aOpacity, extendMode, stops, lineStart, lineEnd);
 
   double firstStop = mStops[0].mPosition;
   double lastStop = mStops[mStops.Length() - 1].mPosition;
 
-  LayoutDevicePoint lineStart = LayoutDevicePoint(mLineStart.x, mLineStart.y);
-  LayoutDevicePoint lineEnd = LayoutDevicePoint(mLineEnd.x, mLineEnd.y);
-
   // Do a naive tiling of the gradient by making multiple display items
   // TODO: this should be done on the WebRender side eventually
 
   nscoord appUnitsPerDevPixel = mPresContext->AppUnitsPerDevPixel();
   LayoutDeviceRect firstTileBounds = LayoutDevicePixel::FromAppUnits(mDest, appUnitsPerDevPixel);
   LayoutDeviceRect clipBounds = LayoutDevicePixel::FromAppUnits(mFillArea, appUnitsPerDevPixel);
 
   // Make the units relative to the parent stacking context
--- a/layout/painting/nsCSSRenderingGradients.h
+++ b/layout/painting/nsCSSRenderingGradients.h
@@ -51,20 +51,30 @@ public:
                                              const nsSize& aRepeatSize,
                                              const mozilla::CSSIntRect& aSrc,
                                              const nsSize& aIntrinsiceSize);
 
   void Paint(gfxContext& aContext,
              const nsRect& aDirtyRect,
              float aOpacity = 1.0);
 
+  void BuildWebRenderParameters(float aOpacity,
+                                WrGradientExtendMode& aMode,
+                                nsTArray<WrGradientStop>& aStops,
+                                LayoutDevicePoint& aLineStart,
+                                LayoutDevicePoint& aLineEnd);
+
   void BuildWebRenderDisplayItems(wr::DisplayListBuilder& aBuilder,
                                   layers::WebRenderDisplayItemLayer* aLayer,
                                   float aOpacity = 1.0);
 
+  const nsTArray<ColorStop>& GetStops() const { return mStops; }
+  double GetRadiusX() const { return mRadiusX; }
+  double GetRadiusY() const { return mRadiusY; }
+
 private:
   nsCSSGradientRenderer() {}
 
   nsPresContext* mPresContext;
   nsStyleGradient* mGradient;
   CSSIntRect mSrc;
   nsRect mDest;
   nsRect mDirtyRect;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4606,17 +4606,19 @@ nsDisplayBorder::GetLayerState(nsDisplay
                                          nsRect(offset, mFrame->GetSize()),
                                          mFrame->StyleContext(),
                                          mFrame->GetSkipSides());
 
   const nsStyleBorder *styleBorder = mFrame->StyleContext()->StyleBorder();
   const nsStyleImage* image = &styleBorder->mBorderImageSource;
   mBorderRenderer = Nothing();
   mBorderImageRenderer = Nothing();
-  if ((!image || image->GetType() != eStyleImageType_Image) && !br) {
+  if ((!image ||
+       image->GetType() != eStyleImageType_Image ||
+       image->GetType() != eStyleImageType_Gradient) && !br) {
     return LAYER_NONE;
   }
 
   LayersBackend backend = aManager->GetBackendType();
   if (backend == layers::LayersBackend::LAYERS_WR) {
     if (br) {
       if (!br->CanCreateWebRenderCommands()) {
         return LAYER_NONE;
@@ -4724,76 +4726,157 @@ nsDisplayBorder::BuildLayer(nsDisplayLis
   }
 }
 
 void
 nsDisplayBorder::CreateBorderImageWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                                     nsTArray<WebRenderParentCommand>& aParentCommands,
                                                     WebRenderDisplayItemLayer* aLayer)
 {
-  // Only support border-image currently
   MOZ_ASSERT(mBorderImageRenderer);
   if (!mBorderImageRenderer->mImageRenderer.IsReady()) {
     return;
   }
 
-  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);
-  if (!externalImageId) {
-    return;
-  }
-
-  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),
-                           aBuilder.BuildClipRegion(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));
+  switch (mBorderImageRenderer->mImageRenderer.GetType()) {
+    case eStyleImageType_Image:
+    {
+      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);
+      if (!externalImageId) {
+        return;
+      }
+
+      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),
+                               aBuilder.BuildClipRegion(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));
+      break;
+    }
+    case eStyleImageType_Gradient:
+    {
+      RefPtr<nsStyleGradient> gradient = mBorderImageRenderer->mImageRenderer.GetGradientData();
+      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;
+      }
+
+      RefPtr<nsStyleGradient> gradientData = mBorderImageRenderer->mImageRenderer.GetGradientData();
+      Maybe<nsCSSGradientRenderer> renderer =
+        nsCSSGradientRenderer::Create(mFrame->PresContext(), gradientData,
+                                      mBorderImageRenderer->mArea,
+                                      mBorderImageRenderer->mArea,
+                                      mBorderImageRenderer->mArea.Size(),
+                                      CSSIntRect(),
+                                      mBorderImageRenderer->mImageSize);
+
+      WrGradientExtendMode extendMode;
+      nsTArray<WrGradientStop> stops;
+      LayoutDevicePoint lineStart;
+      LayoutDevicePoint lineEnd;
+      renderer->BuildWebRenderParameters(1.0, extendMode, stops, lineStart, lineEnd);
+
+      if (gradientData->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
+        Point startPoint = dest.TopLeft();
+        startPoint = startPoint + Point(lineStart.x, lineStart.y);
+        Point endPoint = dest.TopLeft();
+        endPoint = endPoint + Point(lineEnd.x, lineEnd.y);
+
+        aBuilder.PushBorderGradient(wr::ToWrRect(dest),
+                                    aBuilder.BuildClipRegion(wr::ToWrRect(clip)),
+                                    wr::ToWrBorderWidths(widths[0], widths[1], widths[2], widths[3]),
+                                    wr::ToWrPoint(startPoint),
+                                    wr::ToWrPoint(endPoint),
+                                    stops,
+                                    extendMode,
+                                    wr::ToWrSideOffsets2Df32(outset[0], outset[1], outset[2], outset[3]));
+      } else {
+        double firstStop = renderer->GetStops()[0].mPosition;
+        double lastStop = renderer->GetStops()[renderer->GetStops().Length() - 1].mPosition;
+        double innerRadius = renderer->GetRadiusX() * firstStop;
+        double outerRadius = renderer->GetRadiusX() * lastStop;
+
+        aBuilder.PushBorderRadialGradient(wr::ToWrRect(dest),
+                                          aBuilder.BuildClipRegion(wr::ToWrRect(clip)),
+                                          wr::ToWrBorderWidths(widths[0], widths[1], widths[2], widths[3]),
+                                          wr::ToWrPoint(lineStart),
+                                          wr::ToWrPoint(lineStart),
+                                          innerRadius,
+                                          outerRadius,
+                                          stops,
+                                          extendMode,
+                                          wr::ToWrSideOffsets2Df32(outset[0], outset[1], outset[2], outset[3]));
+      }
+      break;
+    }
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unsupport border image type");
+  }
 }
 
 void
 nsDisplayBorder::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                                          nsTArray<WebRenderParentCommand>& aParentCommands,
                                          WebRenderDisplayItemLayer* aLayer)
 {
   MOZ_ASSERT(mBorderImageRenderer || mBorderRenderer);
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -940,8 +940,15 @@ nsImageRenderer::PurgeCacheForViewportCh
   // Check if we should flush the cached data - only vector images need to do
   // the check since they might not have fixed ratio.
   if (mImageContainer &&
       mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
     mImage->PurgeCacheForViewportChange(aSVGViewportSize, aHasIntrinsicRatio);
   }
 }
 
+already_AddRefed<nsStyleGradient>
+nsImageRenderer::GetGradientData()
+{
+  RefPtr<nsStyleGradient> res = mGradientData;
+  return res.forget();
+}
+
--- a/layout/painting/nsImageRenderer.h
+++ b/layout/painting/nsImageRenderer.h
@@ -255,16 +255,18 @@ public:
 
   bool IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags);
   bool IsReady() const { return mPrepareResult == DrawResult::SUCCESS; }
   DrawResult PrepareResult() const { return mPrepareResult; }
   void SetExtendMode(mozilla::gfx::ExtendMode aMode) { mExtendMode = aMode; }
   void SetMaskOp(uint8_t aMaskOp) { mMaskOp = aMaskOp; }
   void PurgeCacheForViewportChange(const mozilla::Maybe<nsSize>& aSVGViewportSize,
                                    const bool aHasRatio);
+  nsStyleImageType GetType() const { return mType; }
+  already_AddRefed<nsStyleGradient> GetGradientData();
 
 private:
   /**
    * Draws the image to the target rendering context.
    * 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.