Bug 1347469 - Add support for gradient border. r=mattwoodrow
MozReview-Commit-ID: 7RfVsPFWhlo
--- 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.