LibWeb: Fix color interpolation by premultiplying alpha

The current Color::interpolate_color method does not follow the specs
properly. Started improving it by handling premultiplied alpha in color
interpolation.

Only one WPT test covers this (color-transition-premultiplied), which we
currently pass due to a different approach in Color.mixed_with.
This commit is contained in:
norbiros 2025-08-11 17:15:26 +02:00 committed by Jelle Raaijmakers
parent d91d28dc2a
commit 783ae44462
Notes: github-actions[bot] 2025-08-14 09:10:17 +00:00
4 changed files with 38 additions and 6 deletions

View file

@ -890,6 +890,7 @@ Color interpolate_color(Color from, Color to, float delta, ColorSyntax syntax)
// https://drafts.csswg.org/css-color/#interpolation
// FIXME: Handle all interpolation methods.
// FIXME: Handle "analogous", "missing", and "powerless" components, somehow.
// FIXME: Remove duplicated code with Color::mixed_with(Color other, float weight)
// https://drafts.csswg.org/css-color/#interpolation-space
// If the host syntax does not define what color space interpolation should take place in, it defaults to Oklab.
@ -899,21 +900,30 @@ Color interpolate_color(Color from, Color to, float delta, ColorSyntax syntax)
Color result;
if (syntax == ColorSyntax::Modern) {
auto from_oklab = from.to_oklab();
auto to_oklab = to.to_oklab();
// 5. changing the color components to premultiplied form
auto from_oklab = from.to_premultiplied_oklab();
auto to_oklab = to.to_premultiplied_oklab();
// 6. linearly interpolating each component of the computed value of the color separately
// 7. undoing premultiplication
auto from_alpha = from.alpha() / 255.0f;
auto to_alpha = to.alpha() / 255.0f;
auto interpolated_alpha = interpolate_raw(from_alpha, to_alpha, delta);
result = Color::from_oklab(
interpolate_raw(from_oklab.L, to_oklab.L, delta),
interpolate_raw(from_oklab.a, to_oklab.a, delta),
interpolate_raw(from_oklab.b, to_oklab.b, delta));
interpolate_raw(from_oklab.L, to_oklab.L, delta) / interpolated_alpha,
interpolate_raw(from_oklab.a, to_oklab.a, delta) / interpolated_alpha,
interpolate_raw(from_oklab.b, to_oklab.b, delta) / interpolated_alpha,
interpolated_alpha);
} else {
result = Color {
interpolate_raw(from.red(), to.red(), delta),
interpolate_raw(from.green(), to.green(), delta),
interpolate_raw(from.blue(), to.blue(), delta),
interpolate_raw(from.alpha(), to.alpha(), delta)
};
}
result.set_alpha(interpolate_raw(from.alpha(), to.alpha(), delta));
return result;
}