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

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

@ -225,6 +225,17 @@ static Tuple<CSSPoint, CSSPoint, CSSCoord, CSSCoord> ComputeRadialGradientLine(
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) {
return aF1 + aFrac * (aF2 - aF1);
}
@ -563,22 +574,40 @@ static Maybe<double> GetSpecifiedGradientPosition(
return Some(pos.ResolveToCSSPixels(aLineLength) / aLineLength);
}
static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle,
const StyleGradient& aGradient,
CSSCoord aLineLength) {
auto items = aGradient.IsLinear() ? aGradient.AsLinear().items.AsSpan()
: aGradient.AsRadial().items.AsSpan();
// aLineLength argument is unused for conic-gradients.
static Maybe<double> GetSpecifiedGradientPosition(
const StyleGenericGradientItem<StyleColor, StyleAngleOrPercentage>& aItem,
CSSCoord aLineLength) {
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");
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
// positions, then this is the index of the first stop in that run.
Maybe<size_t> firstUnsetPosition;
for (size_t i = 0; i < items.Length(); ++i) {
const auto& stop = items[i];
for (size_t i = 0; i < aItems.Length(); ++i) {
const auto& stop = aItems[i];
double position;
Maybe<double> specifiedPosition =
@ -589,7 +618,7 @@ static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle,
} else if (i == 0) {
// First stop defaults to 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
position = 1.0;
} else {
@ -634,6 +663,21 @@ static nsTArray<ColorStop> ComputeColorStops(ComputedStyle* aComputedStyle,
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(
nsPresContext* aPresContext, ComputedStyle* aComputedStyle,
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
// the gradient.
CSSPoint lineStart, lineEnd;
CSSCoord radiusX = 0, radiusY = 0; // for radial gradients only
CSSPoint lineStart, lineEnd, center; // center is for conic gradients only
CSSCoord radiusX = 0, radiusY = 0; // for radial gradients only
float angle = 0.0; // for conic gradients only
if (aGradient.IsLinear()) {
Tie(lineStart, lineEnd) =
ComputeLinearGradientLine(aPresContext, aGradient, srcSize);
} else {
} else if (aGradient.IsRadial()) {
Tie(lineStart, lineEnd, radiusX, radiusY) =
ComputeRadialGradientLine(aGradient, srcSize);
} else {
MOZ_ASSERT(aGradient.IsConic());
Tie(center, angle) = ComputeConicGradientProperties(aGradient, srcSize);
}
// Avoid sending Infs or Nans to downwind draw targets.
if (!lineStart.IsFinite() || !lineEnd.IsFinite()) {
lineStart = lineEnd = CSSPoint(0, 0);
}
if (!center.IsFinite()) {
center = CSSPoint(0, 0);
}
CSSCoord lineLength =
NS_hypot(lineEnd.x - lineStart.x, lineEnd.y - lineStart.y);
@ -677,6 +728,11 @@ nsCSSGradientRenderer nsCSSGradientRenderer::Create(
};
renderer.mRadiusX = aPresContext->CSSPixelsToDevPixels(radiusX);
renderer.mRadiusY = aPresContext->CSSPixelsToDevPixels(radiusY);
renderer.mCenter = {
aPresContext->CSSPixelsToDevPixels(center.x),
aPresContext->CSSPixelsToDevPixels(center.y),
};
renderer.mAngle = angle;
return renderer;
}
@ -869,8 +925,7 @@ void nsCSSGradientRenderer::Paint(gfxContext& aContext, const nsRect& aDest,
gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y,
gradientEnd.x, gradientEnd.y);
} else {
MOZ_ASSERT(mGradient->IsRadial());
} else if (mGradient->IsRadial()) {
NS_ASSERTION(firstStop >= 0.0,
"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.PreTranslate(-mLineStart);
}
} else {
// conic-gradient is only implemented for WebRender
return;
}
// Use a pattern transform to take account of source and dest rects
matrix.PreTranslate(gfxPoint(mPresContext->CSSPixelsToDevPixels(aSrc.x),
@ -1120,7 +1178,8 @@ bool nsCSSGradientRenderer::TryPaintTilesWithExtendMode(
void nsCSSGradientRenderer::BuildWebRenderParameters(
float aOpacity, wr::ExtendMode& aMode, nsTArray<wr::GradientStop>& aStops,
LayoutDevicePoint& aLineStart, LayoutDevicePoint& aLineEnd,
LayoutDeviceSize& aGradientRadius) {
LayoutDeviceSize& aGradientRadius, LayoutDevicePoint& aGradientCenter,
float& aGradientAngle) {
aMode =
mGradient->Repeating() ? wr::ExtendMode::Repeat : wr::ExtendMode::Clamp;
@ -1136,6 +1195,8 @@ void nsCSSGradientRenderer::BuildWebRenderParameters(
aLineStart = LayoutDevicePoint(mLineStart.x, mLineStart.y);
aLineEnd = LayoutDevicePoint(mLineEnd.x, mLineEnd.y);
aGradientRadius = LayoutDeviceSize(mRadiusX, mRadiusY);
aGradientCenter = LayoutDevicePoint(mCenter.x, mCenter.y);
aGradientAngle = mAngle;
}
void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
@ -1151,8 +1212,10 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
LayoutDevicePoint lineStart;
LayoutDevicePoint lineEnd;
LayoutDeviceSize gradientRadius;
LayoutDevicePoint gradientCenter;
float gradientAngle;
BuildWebRenderParameters(aOpacity, extendMode, stops, lineStart, lineEnd,
gradientRadius);
gradientRadius, gradientCenter, gradientAngle);
nscoord appUnitsPerDevPixel = mPresContext->AppUnitsPerDevPixel();
@ -1188,6 +1251,9 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
lineStart.x = (lineStart.x - srcTransform.x) * srcTransform.width;
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()) {
lineEnd.x = (lineEnd.x - srcTransform.x) * srcTransform.width;
lineEnd.y = (lineEnd.y - srcTransform.y) * srcTransform.height;
@ -1199,8 +1265,7 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
mozilla::wr::ToLayoutPoint(lineEnd), stops, extendMode,
mozilla::wr::ToLayoutSize(firstTileBounds.Size()),
mozilla::wr::ToLayoutSize(tileSpacing));
} else {
MOZ_ASSERT(mGradient->IsRadial());
} else if (mGradient->IsRadial()) {
gradientRadius.width *= srcTransform.width;
gradientRadius.height *= srcTransform.height;
@ -1211,6 +1276,14 @@ void nsCSSGradientRenderer::BuildWebRenderDisplayItems(
mozilla::wr::ToLayoutSize(gradientRadius), stops, extendMode,
mozilla::wr::ToLayoutSize(firstTileBounds.Size()),
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,
LayoutDevicePoint& aLineStart,
LayoutDevicePoint& aLineEnd,
LayoutDeviceSize& aGradientRadius);
LayoutDeviceSize& aGradientRadius,
LayoutDevicePoint& aGradientCenter,
float& aGradientAngle);
/**
* Build display items for the gradient
@ -89,7 +91,8 @@ class nsCSSGradientRenderer final {
: mPresContext(nullptr),
mGradient(nullptr),
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
@ -107,8 +110,10 @@ class nsCSSGradientRenderer final {
nsPresContext* mPresContext;
const StyleGradient* mGradient;
nsTArray<ColorStop> mStops;
gfxPoint mLineStart, mLineEnd;
double mRadiusX, mRadiusY;
gfxPoint mLineStart, mLineEnd; // only for linear/radial gradients
double mRadiusX, mRadiusY; // only for radial gradients
gfxPoint mCenter; // only for conic gradients
float mAngle; // only for conic gradients
};
} // namespace mozilla

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

@ -441,7 +441,13 @@ inline imgRequestProxy* StyleComputedImageUrl::GetImage() const {
template <>
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 <>

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

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