Bug 1615862 - Handle conic-gradients in nsCSSGradientRenderer for WebRender. r=emilio,mstange

Differential Revision: https://phabricator.services.mozilla.com/D63018

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tim Nguyen 2020-02-20 22:07:32 +00:00
Родитель 374e0856d0
Коммит 378e9808bf
5 изменённых файлов: 145 добавлений и 36 удалений

Просмотреть файл

@ -3663,18 +3663,21 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands(
bool noVerticalBorders = widths[0] <= epsilon && widths[2] < epsilon; bool noVerticalBorders = widths[0] <= epsilon && widths[2] < epsilon;
bool noHorizontalBorders = widths[1] <= epsilon && widths[3] < epsilon; bool noHorizontalBorders = widths[1] <= epsilon && widths[3] < epsilon;
// Border image with no border. It's a little silly but WebRender currently does // Border image with no border. It's a little silly but WebRender
// not handle this. We could fall back to a blob image but there are reftests that // currently does not handle this. We could fall back to a blob image
// are sensible to the test going through a blob while the reference doesn't. // but there are reftests that are sensible to the test going through a
// blob while the reference doesn't.
if (noVerticalBorders && noHorizontalBorders) { if (noVerticalBorders && noHorizontalBorders) {
aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering, key.value()); aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering,
key.value());
break; break;
} }
// Fall-back if we want to fill the middle area and opposite edges are // Fall-back if we want to fill the middle area and opposite edges are
// both empty. // both empty.
// TODO(bug 1609893): moving some of the repetition handling code out // TODO(bug 1609893): moving some of the repetition handling code out
// of the image shader will make it easier to handle these cases properly. // of the image shader will make it easier to handle these cases
// properly.
if (noHorizontalBorders || noVerticalBorders) { if (noHorizontalBorders || noVerticalBorders) {
return ImgDrawResult::NOT_SUPPORTED; return ImgDrawResult::NOT_SUPPORTED;
} }
@ -3704,8 +3707,11 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands(
LayoutDevicePoint lineStart; LayoutDevicePoint lineStart;
LayoutDevicePoint lineEnd; LayoutDevicePoint lineEnd;
LayoutDeviceSize gradientRadius; LayoutDeviceSize gradientRadius;
LayoutDevicePoint gradientCenter;
float gradientAngle;
renderer.BuildWebRenderParameters(1.0, extendMode, stops, lineStart, renderer.BuildWebRenderParameters(1.0, extendMode, stops, lineStart,
lineEnd, gradientRadius); lineEnd, gradientRadius, gradientCenter,
gradientAngle);
if (gradient.IsLinear()) { if (gradient.IsLinear()) {
LayoutDevicePoint startPoint = LayoutDevicePoint startPoint =
@ -3723,7 +3729,7 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands(
extendMode, extendMode,
wr::ToLayoutSideOffsets(outset[0], outset[1], outset[2], wr::ToLayoutSideOffsets(outset[0], outset[1], outset[2],
outset[3])); outset[3]));
} else { } else if (gradient.IsRadial()) {
aBuilder.PushBorderRadialGradient( aBuilder.PushBorderRadialGradient(
dest, clip, !aItem->BackfaceIsHidden(), dest, clip, !aItem->BackfaceIsHidden(),
wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]), wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
@ -3731,6 +3737,15 @@ ImgDrawResult nsCSSBorderImageRenderer::CreateWebRenderCommands(
wr::ToLayoutSize(gradientRadius), stops, extendMode, wr::ToLayoutSize(gradientRadius), stops, extendMode,
wr::ToLayoutSideOffsets(outset[0], outset[1], outset[2], wr::ToLayoutSideOffsets(outset[0], outset[1], outset[2],
outset[3])); outset[3]));
} else {
MOZ_ASSERT(gradient.IsConic());
aBuilder.PushBorderConicGradient(
dest, clip, !aItem->BackfaceIsHidden(),
wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
mFill, wr::ToLayoutPoint(gradientCenter), gradientAngle, stops,
extendMode,
wr::ToLayoutSideOffsets(outset[0], outset[1], outset[2],
outset[3]));
} }
break; break;
} }

Просмотреть файл

@ -225,6 +225,17 @@ static Tuple<CSSPoint, CSSPoint, CSSCoord, CSSCoord> ComputeRadialGradientLine(
return MakeTuple(start, end, radiusX, radiusY); return MakeTuple(start, end, radiusX, radiusY);
} }
// Compute the center and the start angle of the conic gradient.
static Tuple<CSSPoint, float> ComputeConicGradientProperties(
const StyleGradient& aGradient, const CSSSize& aBoxSize) {
const auto& conic = aGradient.AsConic();
const Position& position = conic.position;
float angle = static_cast<float>(conic.angle.ToRadians());
CSSPoint center = ResolvePosition(position, aBoxSize);
return MakeTuple(center, angle);
}
static float Interpolate(float aF1, float aF2, float aFrac) { static float Interpolate(float aF1, float aF2, float aFrac) {
return aF1 + aFrac * (aF2 - aF1); return aF1 + aFrac * (aF2 - aF1);
} }
@ -563,22 +574,40 @@ static Maybe<double> GetSpecifiedGradientPosition(
return Some(pos.ResolveToCSSPixels(aLineLength) / aLineLength); return Some(pos.ResolveToCSSPixels(aLineLength) / aLineLength);
} }
static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle, // aLineLength argument is unused for conic-gradients.
const StyleGradient& aGradient, static Maybe<double> GetSpecifiedGradientPosition(
CSSCoord aLineLength) { const StyleGenericGradientItem<StyleColor, StyleAngleOrPercentage>& aItem,
auto items = aGradient.IsLinear() ? aGradient.AsLinear().items.AsSpan() CSSCoord aLineLength) {
: aGradient.AsRadial().items.AsSpan(); if (aItem.IsSimpleColorStop()) {
return Nothing();
}
MOZ_ASSERT(items.Length() >= 2, const StyleAngleOrPercentage& pos = aItem.IsComplexColorStop()
? aItem.AsComplexColorStop().position
: aItem.AsInterpolationHint();
if (pos.IsPercentage()) {
return Some(pos.AsPercentage()._0);
}
return Some(pos.AsAngle().ToRadians() / (2 * M_PI));
}
template <typename T>
static nsTArray<ColorStop> ComputeColorStopsForItems(
ComputedStyle* aComputedStyle,
Span<const StyleGenericGradientItem<StyleColor, T>> aItems,
CSSCoord aLineLength) {
MOZ_ASSERT(aItems.Length() >= 2,
"The parser should reject gradients with less than two stops"); "The parser should reject gradients with less than two stops");
nsTArray<ColorStop> stops(items.Length()); nsTArray<ColorStop> stops(aItems.Length());
// If there is a run of stops before stop i that did not have specified // If there is a run of stops before stop i that did not have specified
// positions, then this is the index of the first stop in that run. // positions, then this is the index of the first stop in that run.
Maybe<size_t> firstUnsetPosition; Maybe<size_t> firstUnsetPosition;
for (size_t i = 0; i < items.Length(); ++i) { for (size_t i = 0; i < aItems.Length(); ++i) {
const auto& stop = items[i]; const auto& stop = aItems[i];
double position; double position;
Maybe<double> specifiedPosition = Maybe<double> specifiedPosition =
@ -589,7 +618,7 @@ static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle,
} else if (i == 0) { } else if (i == 0) {
// First stop defaults to position 0.0 // First stop defaults to position 0.0
position = 0.0; position = 0.0;
} else if (i == items.Length() - 1) { } else if (i == aItems.Length() - 1) {
// Last stop defaults to position 1.0 // Last stop defaults to position 1.0
position = 1.0; position = 1.0;
} else { } else {
@ -634,6 +663,21 @@ static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle,
return stops; return stops;
} }
static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle,
const StyleGradient& aGradient,
CSSCoord aLineLength) {
if (aGradient.IsLinear()) {
return ComputeColorStopsForItems(
aComputedStyle, aGradient.AsLinear().items.AsSpan(), aLineLength);
}
if (aGradient.IsRadial()) {
return ComputeColorStopsForItems(
aComputedStyle, aGradient.AsRadial().items.AsSpan(), aLineLength);
}
return ComputeColorStopsForItems(
aComputedStyle, aGradient.AsConic().items.AsSpan(), aLineLength);
}
nsCSSGradientRenderer nsCSSGradientRenderer::Create( nsCSSGradientRenderer nsCSSGradientRenderer::Create(
nsPresContext* aPresContext, ComputedStyle* aComputedStyle, nsPresContext* aPresContext, ComputedStyle* aComputedStyle,
const StyleGradient& aGradient, const nsSize& aIntrinsicSize) { const StyleGradient& aGradient, const nsSize& aIntrinsicSize) {
@ -641,19 +685,26 @@ nsCSSGradientRenderer nsCSSGradientRenderer::Create(
// Compute "gradient line" start and end relative to the intrinsic size of // Compute "gradient line" start and end relative to the intrinsic size of
// the gradient. // the gradient.
CSSPoint lineStart, lineEnd; CSSPoint lineStart, lineEnd, center; // center is for conic gradients only
CSSCoord radiusX = 0, radiusY = 0; // for radial gradients only CSSCoord radiusX = 0, radiusY = 0; // for radial gradients only
float angle = 0.0; // for conic gradients only
if (aGradient.IsLinear()) { if (aGradient.IsLinear()) {
Tie(lineStart, lineEnd) = Tie(lineStart, lineEnd) =
ComputeLinearGradientLine(aPresContext, aGradient, srcSize); ComputeLinearGradientLine(aPresContext, aGradient, srcSize);
} else { } else if (aGradient.IsRadial()) {
Tie(lineStart, lineEnd, radiusX, radiusY) = Tie(lineStart, lineEnd, radiusX, radiusY) =
ComputeRadialGradientLine(aGradient, srcSize); ComputeRadialGradientLine(aGradient, srcSize);
} else {
MOZ_ASSERT(aGradient.IsConic());
Tie(center, angle) = ComputeConicGradientProperties(aGradient, srcSize);
} }
// Avoid sending Infs or Nans to downwind draw targets. // Avoid sending Infs or Nans to downwind draw targets.
if (!lineStart.IsFinite() || !lineEnd.IsFinite()) { if (!lineStart.IsFinite() || !lineEnd.IsFinite()) {
lineStart = lineEnd = CSSPoint(0, 0); lineStart = lineEnd = CSSPoint(0, 0);
} }
if (!center.IsFinite()) {
center = CSSPoint(0, 0);
}
CSSCoord lineLength = CSSCoord lineLength =
NS_hypot(lineEnd.x - lineStart.x, lineEnd.y - lineStart.y); NS_hypot(lineEnd.x - lineStart.x, lineEnd.y - lineStart.y);
@ -677,6 +728,11 @@ nsCSSGradientRenderer nsCSSGradientRenderer::Create(
}; };
renderer.mRadiusX = aPresContext->CSSPixelsToDevPixels(radiusX); renderer.mRadiusX = aPresContext->CSSPixelsToDevPixels(radiusX);
renderer.mRadiusY = aPresContext->CSSPixelsToDevPixels(radiusY); renderer.mRadiusY = aPresContext->CSSPixelsToDevPixels(radiusY);
renderer.mCenter = {
aPresContext->CSSPixelsToDevPixels(center.x),
aPresContext->CSSPixelsToDevPixels(center.y),
};
renderer.mAngle = angle;
return renderer; return renderer;
} }
@ -869,8 +925,7 @@ void nsCSSGradientRenderer::Paint(gfxContext& aContext, const nsRect& aDest,
gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y, gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y,
gradientEnd.x, gradientEnd.y); gradientEnd.x, gradientEnd.y);
} else { } else if (mGradient->IsRadial()) {
MOZ_ASSERT(mGradient->IsRadial());
NS_ASSERTION(firstStop >= 0.0, NS_ASSERTION(firstStop >= 0.0,
"Negative stops not allowed for radial gradients"); "Negative stops not allowed for radial gradients");
@ -895,6 +950,9 @@ void nsCSSGradientRenderer::Paint(gfxContext& aContext, const nsRect& aDest,
matrix.PreScale(1.0, mRadiusX / mRadiusY); matrix.PreScale(1.0, mRadiusX / mRadiusY);
matrix.PreTranslate(-mLineStart); matrix.PreTranslate(-mLineStart);
} }
} else {
// conic-gradient is only implemented for WebRender
return;
} }
// Use a pattern transform to take account of source and dest rects // Use a pattern transform to take account of source and dest rects
matrix.PreTranslate(gfxPoint(mPresContext->CSSPixelsToDevPixels(aSrc.x), matrix.PreTranslate(gfxPoint(mPresContext->CSSPixelsToDevPixels(aSrc.x),
@ -1120,7 +1178,8 @@ bool nsCSSGradientRenderer::TryPaintTilesWithExtendMode(
void nsCSSGradientRenderer::BuildWebRenderParameters( void nsCSSGradientRenderer::BuildWebRenderParameters(
float aOpacity, wr::ExtendMode& aMode, nsTArray<wr::GradientStop>& aStops, float aOpacity, wr::ExtendMode& aMode, nsTArray<wr::GradientStop>& aStops,
LayoutDevicePoint& aLineStart, LayoutDevicePoint& aLineEnd, LayoutDevicePoint& aLineStart, LayoutDevicePoint& aLineEnd,
LayoutDeviceSize& aGradientRadius) { LayoutDeviceSize& aGradientRadius, LayoutDevicePoint& aGradientCenter,
float& aGradientAngle) {
aMode = aMode =
mGradient->Repeating() ? wr::ExtendMode::Repeat : wr::ExtendMode::Clamp; mGradient->Repeating() ? wr::ExtendMode::Repeat : wr::ExtendMode::Clamp;
@ -1136,6 +1195,8 @@ void nsCSSGradientRenderer::BuildWebRenderParameters(
aLineStart = LayoutDevicePoint(mLineStart.x, mLineStart.y); aLineStart = LayoutDevicePoint(mLineStart.x, mLineStart.y);
aLineEnd = LayoutDevicePoint(mLineEnd.x, mLineEnd.y); aLineEnd = LayoutDevicePoint(mLineEnd.x, mLineEnd.y);
aGradientRadius = LayoutDeviceSize(mRadiusX, mRadiusY); aGradientRadius = LayoutDeviceSize(mRadiusX, mRadiusY);
aGradientCenter = LayoutDevicePoint(mCenter.x, mCenter.y);
aGradientAngle = mAngle;
} }
void nsCSSGradientRenderer::BuildWebRenderDisplayItems( void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
@ -1151,8 +1212,10 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
LayoutDevicePoint lineStart; LayoutDevicePoint lineStart;
LayoutDevicePoint lineEnd; LayoutDevicePoint lineEnd;
LayoutDeviceSize gradientRadius; LayoutDeviceSize gradientRadius;
LayoutDevicePoint gradientCenter;
float gradientAngle;
BuildWebRenderParameters(aOpacity, extendMode, stops, lineStart, lineEnd, BuildWebRenderParameters(aOpacity, extendMode, stops, lineStart, lineEnd,
gradientRadius); gradientRadius, gradientCenter, gradientAngle);
nscoord appUnitsPerDevPixel = mPresContext->AppUnitsPerDevPixel(); nscoord appUnitsPerDevPixel = mPresContext->AppUnitsPerDevPixel();
@ -1188,6 +1251,9 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
lineStart.x = (lineStart.x - srcTransform.x) * srcTransform.width; lineStart.x = (lineStart.x - srcTransform.x) * srcTransform.width;
lineStart.y = (lineStart.y - srcTransform.y) * srcTransform.height; lineStart.y = (lineStart.y - srcTransform.y) * srcTransform.height;
gradientCenter.x = (gradientCenter.x - srcTransform.x) * srcTransform.width;
gradientCenter.y = (gradientCenter.y - srcTransform.y) * srcTransform.height;
if (mGradient->IsLinear()) { if (mGradient->IsLinear()) {
lineEnd.x = (lineEnd.x - srcTransform.x) * srcTransform.width; lineEnd.x = (lineEnd.x - srcTransform.x) * srcTransform.width;
lineEnd.y = (lineEnd.y - srcTransform.y) * srcTransform.height; lineEnd.y = (lineEnd.y - srcTransform.y) * srcTransform.height;
@ -1199,8 +1265,7 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
mozilla::wr::ToLayoutPoint(lineEnd), stops, extendMode, mozilla::wr::ToLayoutPoint(lineEnd), stops, extendMode,
mozilla::wr::ToLayoutSize(firstTileBounds.Size()), mozilla::wr::ToLayoutSize(firstTileBounds.Size()),
mozilla::wr::ToLayoutSize(tileSpacing)); mozilla::wr::ToLayoutSize(tileSpacing));
} else { } else if (mGradient->IsRadial()) {
MOZ_ASSERT(mGradient->IsRadial());
gradientRadius.width *= srcTransform.width; gradientRadius.width *= srcTransform.width;
gradientRadius.height *= srcTransform.height; gradientRadius.height *= srcTransform.height;
@ -1211,6 +1276,14 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
mozilla::wr::ToLayoutSize(gradientRadius), stops, extendMode, mozilla::wr::ToLayoutSize(gradientRadius), stops, extendMode,
mozilla::wr::ToLayoutSize(firstTileBounds.Size()), mozilla::wr::ToLayoutSize(firstTileBounds.Size()),
mozilla::wr::ToLayoutSize(tileSpacing)); mozilla::wr::ToLayoutSize(tileSpacing));
} else {
MOZ_ASSERT(mGradient->IsConic());
aBuilder.PushConicGradient(
mozilla::wr::ToLayoutRect(gradientBounds),
mozilla::wr::ToLayoutRect(clipBounds), aIsBackfaceVisible,
mozilla::wr::ToLayoutPoint(gradientCenter), gradientAngle, stops,
extendMode, mozilla::wr::ToLayoutSize(firstTileBounds.Size()),
mozilla::wr::ToLayoutSize(tileSpacing));
} }
} }

Просмотреть файл

@ -65,7 +65,9 @@ class nsCSSGradientRenderer final {
nsTArray<wr::GradientStop>& aStops, nsTArray<wr::GradientStop>& aStops,
LayoutDevicePoint& aLineStart, LayoutDevicePoint& aLineStart,
LayoutDevicePoint& aLineEnd, LayoutDevicePoint& aLineEnd,
LayoutDeviceSize& aGradientRadius); LayoutDeviceSize& aGradientRadius,
LayoutDevicePoint& aGradientCenter,
float& aGradientAngle);
/** /**
* Build display items for the gradient * Build display items for the gradient
@ -89,7 +91,8 @@ class nsCSSGradientRenderer final {
: mPresContext(nullptr), : mPresContext(nullptr),
mGradient(nullptr), mGradient(nullptr),
mRadiusX(0.0), mRadiusX(0.0),
mRadiusY(0.0) {} mRadiusY(0.0),
mAngle(0.0) {}
/** /**
* Attempts to paint the tiles for a gradient by painting it once to an * Attempts to paint the tiles for a gradient by painting it once to an
@ -107,8 +110,10 @@ class nsCSSGradientRenderer final {
nsPresContext* mPresContext; nsPresContext* mPresContext;
const StyleGradient* mGradient; const StyleGradient* mGradient;
nsTArray<ColorStop> mStops; nsTArray<ColorStop> mStops;
gfxPoint mLineStart, mLineEnd; gfxPoint mLineStart, mLineEnd; // only for linear/radial gradients
double mRadiusX, mRadiusY; double mRadiusX, mRadiusY; // only for radial gradients
gfxPoint mCenter; // only for conic gradients
float mAngle; // only for conic gradients
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -441,7 +441,13 @@ inline imgRequestProxy* StyleComputedImageUrl::GetImage() const {
template <> template <>
inline bool StyleGradient::Repeating() const { inline bool StyleGradient::Repeating() const {
return IsLinear() ? AsLinear().repeating : AsRadial().repeating; if (IsLinear()) {
return AsLinear().repeating;
}
if (IsRadial()) {
return AsRadial().repeating;
}
return AsConic().repeating;
} }
template <> template <>

Просмотреть файл

@ -1402,11 +1402,10 @@ nsChangeHint nsStyleTableBorder::CalcDifference(
} }
} }
template <> template <typename T>
bool StyleGradient::IsOpaque() const { static bool GradientItemsAreOpaque(
auto items = Span<const StyleGenericGradientItem<StyleColor, T>> aItems) {
IsLinear() ? AsLinear().items.AsSpan() : AsRadial().items.AsSpan(); for (auto& stop : aItems) {
for (auto& stop : items) {
if (stop.IsInterpolationHint()) { if (stop.IsInterpolationHint()) {
continue; continue;
} }
@ -1423,6 +1422,17 @@ bool StyleGradient::IsOpaque() const {
return true; return true;
} }
template <>
bool StyleGradient::IsOpaque() const {
if (IsLinear()) {
return GradientItemsAreOpaque(AsLinear().items.AsSpan());
}
if (IsRadial()) {
return GradientItemsAreOpaque(AsRadial().items.AsSpan());
}
return GradientItemsAreOpaque(AsConic().items.AsSpan());
}
// -------------------- // --------------------
// CachedBorderImageData // CachedBorderImageData