Bug 1892653 - Fix repeating oklch gradients r=gfx-reviewers,lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D208960
This commit is contained in:
Ashley Hale 2024-04-30 03:40:52 +00:00
Родитель 13bb51755d
Коммит fd063f4485
5 изменённых файлов: 125 добавлений и 13 удалений

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

@ -1195,8 +1195,9 @@ class MOZ_STACK_CLASS WrColorStopInterpolator
WrColorStopInterpolator(
const nsTArray<ColorStop>& aStops,
const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
float aOpacity, nsTArray<wr::GradientStop>& aResult)
: ColorStopInterpolator(aStops, aStyleColorInterpolationMethod),
float aOpacity, nsTArray<wr::GradientStop>& aResult, bool aExtendLastStop)
: ColorStopInterpolator(aStops, aStyleColorInterpolationMethod,
aExtendLastStop),
mResult(aResult),
mOpacity(aOpacity),
mOutputStop(0) {}
@ -1268,8 +1269,15 @@ void nsCSSGradientRenderer::BuildWebRenderParameters(
// what can happen if we try to be clever here.
if (styleColorInterpolationMethod.space != StyleColorSpace::Srgb ||
gfxPlatform::GetCMSMode() == CMSMode::All) {
// For the specific case of longer hue interpolation on a CSS non-repeating
// gradient, we have to pretend there is another stop at position=1.0 that
// duplicates the last stop, this is probably only used for things like a
// color wheel. No such problem for SVG as it doesn't have that complexity.
bool extendLastStop = aMode == wr::ExtendMode::Clamp &&
styleColorInterpolationMethod.hue ==
StyleHueInterpolationMethod::Longer;
WrColorStopInterpolator interpolator(mStops, styleColorInterpolationMethod,
aOpacity, aStops);
aOpacity, aStops, extendLastStop);
interpolator.CreateStops();
} else {
aStops.SetLength(mStops.Length());

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

@ -44,21 +44,27 @@ class MOZ_STACK_CLASS ColorStopInterpolator {
public:
ColorStopInterpolator(
const nsTArray<ColorStop>& aStops,
const StyleColorInterpolationMethod& aStyleColorInterpolationMethod)
const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
bool aExtendLastStop)
: mStyleColorInterpolationMethod(aStyleColorInterpolationMethod),
mStops(aStops) {}
mStops(aStops),
mExtendLastStop(aExtendLastStop) {}
void CreateStops() {
// This loop intentionally iterates the last stop.
for (uint32_t i = 0; i < mStops.Length(); i++) {
// This loop intentionally iterates the last stop if extending.
uint32_t iterStops = mStops.Length() - (mExtendLastStop ? 0 : 1);
for (uint32_t i = 0; i < iterStops; i++) {
auto nextindex = i + 1 < mStops.Length() ? i + 1 : i;
const auto& start = mStops[i];
const auto& end = mStops[nextindex];
float startPosition = start.mPosition;
float endPosition = end.mPosition;
// For the sake of StyleHueInterpolationMethod == Longer we have to
// pretend there is a stop beyond the last stop
if (i == mStops.Length() - 1) {
// For CSS non-repeating gradients with longer hue specified, we have to
// pretend there is a stop beyond the last stop. This is never the case
// on SVG gradients as they only use shorter hue.
//
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1885716 for more info.
if (i == mStops.Length() - 1 && mExtendLastStop) {
endPosition = 1.0f;
}
uint32_t extraStops =
@ -82,6 +88,10 @@ class MOZ_STACK_CLASS ColorStopInterpolator {
protected:
StyleColorInterpolationMethod mStyleColorInterpolationMethod;
const nsTArray<ColorStop>& mStops;
// This indicates that we want to extend the endPosition on the last stop,
// which only matters if this is a CSS non-repeating gradient with
// StyleHueInterpolationMethod::Longer (only valid for hsl/hwb/lch/oklch).
bool mExtendLastStop;
// This could be made tunable, but at 1.0/128 the error is largely
// irrelevant, as WebRender re-encodes it to 128 pairs of stops.

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

@ -244,8 +244,10 @@ class MOZ_STACK_CLASS SVGColorStopInterpolator
public:
SVGColorStopInterpolator(
gfxPattern* aGradient, const nsTArray<ColorStop>& aStops,
const StyleColorInterpolationMethod& aStyleColorInterpolationMethod)
: ColorStopInterpolator(aStops, aStyleColorInterpolationMethod),
const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
bool aExtendLastStop)
: ColorStopInterpolator(aStops, aStyleColorInterpolationMethod,
aExtendLastStop),
mGradient(aGradient) {}
void CreateStop(float aPosition, DeviceColor aColor) {
@ -327,7 +329,8 @@ already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern(
if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) {
static constexpr auto interpolationMethod = StyleColorInterpolationMethod{
StyleColorSpace::SrgbLinear, StyleHueInterpolationMethod::Shorter};
SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod);
SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod,
false);
interpolator.CreateStops();
} else {
// setup standard sRGB stops

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

@ -0,0 +1,47 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Repeating linear gradients in HSL and OKLCH space</title>
<link rel="author" title="Ashley Hale" href="mailto:ahale@mozilla.com">
<style>
body {
background: #fff;
}
.a {
width: 100px;
height: 100px;
background: repeating-linear-gradient(
to bottom,
hsl(180 50% 70%) 0px 20px,
hsl(0 50% 50%) 20px 40px,
hsl(180 50% 70%) 40px 60px,
hsl(0 50% 50%) 60px 80px,
hsl(180 50% 70%) 80px 100px
);
}
.b {
width: 100px;
height: 100px;
background: repeating-linear-gradient(
to bottom,
oklch(70% 50% 180) 0px 20px,
oklch(50% 50% 0) 20px 40px,
oklch(70% 50% 180) 40px 60px,
oklch(50% 50% 0) 60px 80px,
oklch(70% 50% 180) 80px 100px
);
}
</style>
</head>
<body>
<p>repeating-linear-gradient with HSL</p>
<div class="a"></div>
<p>repeating-linear-gradient with OKLCH</p>
<div class="b"></div>
</body>
</html>

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

@ -0,0 +1,44 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Repeating linear gradients in HSL and OKLCH space</title>
<link rel="author" title="Ashley Hale" href="mailto:ahale@mozilla.com">
<link rel="help" href="https://www.w3.org/TR/css-color-4/#interpolation">
<meta name="assert" content="Tests that a multi-stop shorter hue gradient and a single-stop longer hue (wrapping) gradient match in appearance">
<link rel="match" href="repeating-gradient-hsl-and-oklch-ref.html">
<style>
body {
background: #fff;
}
.a {
width: 100px;
height: 100px;
background: repeating-linear-gradient(
to bottom,
hsl(180 50% 70%) 0px 20px,
hsl(0 50% 50%) 20px 40px
);
}
.b {
width: 100px;
height: 100px;
background: repeating-linear-gradient(
to bottom,
oklch(70% 50% 180) 0px 20px,
oklch(50% 50% 0) 20px 40px
);
}
</style>
</head>
<body>
<p>repeating-linear-gradient with HSL</p>
<div class="a"></div>
<p>repeating-linear-gradient with OKLCH</p>
<div class="b"></div>
</body>
</html>