Bug 1347469 - Add support for gradient border. r=mattwoodrow
authorMorris Tseng <mtseng@mozilla.com>
Mon, 10 Apr 2017 17:27:30 +0800
changeset 352763 b47985cfad21e8afb2bc3915483cd233424efa35
parent 352762 350d77ec1b74f2c1bbd39aab6f9e7de33f0ce2d0
child 352764 f7851b58db67c8906c9e8d2a59da98ca371d0d6f
push id31651
push userkwierso@gmail.com
push dateThu, 13 Apr 2017 17:42:52 +0000
treeherdermozilla-central@8dd662ed3387 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1347469
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -1007,36 +1007,51 @@ nsCSSGradientRenderer::Paint(gfxContext&
       }
       aContext.Fill();
       aContext.SetMatrix(ctm);
     }
   }
 }
 
 void
+nsCSSGradientRenderer::BuildWebRenderParameters(float aOpacity,
+                                                WrGradientExtendMode& aMode,
+                                                nsTArray<WrGradientStop>& aStops,
+                                                LayoutDevicePoint& aLineStart,
+                                                LayoutDevicePoint& aLineEnd,
+                                                LayoutDeviceSize& aGradientRadius)
+{
+  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);
+  aGradientRadius = LayoutDeviceSize(mRadiusX, mRadiusY);
+}
+
+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;
-  }
-
-  LayoutDevicePoint lineStart = LayoutDevicePoint(mLineStart.x, mLineStart.y);
-  LayoutDevicePoint lineEnd = LayoutDevicePoint(mLineEnd.x, mLineEnd.y);
-  LayoutDeviceSize gradientRadius = LayoutDeviceSize(mRadiusX, mRadiusY);
+  WrGradientExtendMode extendMode;
+  nsTArray<WrGradientStop> stops;
+  LayoutDevicePoint lineStart;
+  LayoutDevicePoint lineEnd;
+  LayoutDeviceSize gradientRadius;
+  BuildWebRenderParameters(aOpacity, extendMode, stops, lineStart, lineEnd, gradientRadius);
 
   // 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);
 
--- a/layout/painting/nsCSSRenderingGradients.h
+++ b/layout/painting/nsCSSRenderingGradients.h
@@ -52,16 +52,23 @@ 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,
+                                LayoutDeviceSize& aGradientRadius);
+
   void BuildWebRenderDisplayItems(wr::DisplayListBuilder& aBuilder,
                                   layers::WebRenderDisplayItemLayer* aLayer,
                                   float aOpacity = 1.0);
 
 private:
   nsCSSGradientRenderer() {}
 
   nsPresContext* mPresContext;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4605,17 +4605,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;
@@ -4723,76 +4725,127 @@ 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;
-  }
-
+  float widths[4];
+  float slice[4];
+  float outset[4];
   const int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+  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;
+  }
+
   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;
+      }
+
+      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> 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;
+      LayoutDeviceSize gradientRadius;
+      renderer->BuildWebRenderParameters(1.0, extendMode, stops, lineStart, lineEnd, gradientRadius);
+
+      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 {
+        aBuilder.PushBorderRadialGradient(wr::ToWrRect(dest),
+                                          aBuilder.BuildClipRegion(wr::ToWrRect(clip)),
+                                          wr::ToWrBorderWidths(widths[0], widths[1], widths[2], widths[3]),
+                                          wr::ToWrPoint(lineStart),
+                                          wr::ToWrSize(gradientRadius),
+                                          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
@@ -994,8 +994,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
@@ -257,16 +257,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.