LibWeb+LibGfx: Serialize HTML canvas fill/strokeStyle colors correctly

Before this change we were serializing them in a bogus 8-digit hex color
format that isn't actually recognized by HTML.

This code will need more work when we start supporting color spaces
other than sRGB.
This commit is contained in:
Andreas Kling 2024-10-04 14:10:07 +02:00 committed by Andreas Kling
parent f4648f7551
commit 4590c081c2
Notes: github-actions[bot] 2024-10-04 18:02:27 +00:00
7 changed files with 58 additions and 9 deletions

View file

@ -0,0 +1,5 @@
`green` -> `#008000`
`rgba(128, 128, 128, 0.4)` -> `rgba(128, 128, 128, 0.4)`
`rgba(128, 128, 128, 0)` -> `rgba(128, 128, 128, 0)`
`rgba(128, 128, 128, 1)` -> `#808080`
`rgb(128, 128, 128)` -> `#808080`

View file

@ -1,5 +1,5 @@
1. "#00ff00ff" 1. "#00ff00"
2. "#ff0000ff" 2. "#ff0000"
3. "#0000ffff" 3. "#0000ff"
4. "#00ff00ff" 4. "#00ff00"
5. "#ff0000ff" 5. "#ff0000"

View file

@ -0,0 +1,18 @@
<script src="../include.js"></script>
<canvas id="c" width=300 height=300></canvas>
<script>
test(() => {
let x = c.getContext("2d");
function go(color) {
x.fillStyle = color;
println("`" + color + "` -> `" + x.fillStyle + "`");
}
go("green");
go("rgba(128, 128, 128, 0.4)");
go("rgba(128, 128, 128, 0)");
go("rgba(128, 128, 128, 1)");
go("rgb(128, 128, 128)");
});
</script>

View file

@ -23,9 +23,29 @@
namespace Gfx { namespace Gfx {
String Color::to_string() const String Color::to_string(HTMLCompatibleSerialization html_compatible_serialization) const
{ {
return MUST(String::formatted("#{:02x}{:02x}{:02x}{:02x}", red(), green(), blue(), alpha())); // If the following conditions are all true:
// 1. The color space is sRGB
// NOTE: This is currently always true for Gfx::Color.
// 2. The alpha is 1
// NOTE: An alpha value of 1 will be stored as 255 currently.
// 3. The RGB component values are internally represented as integers between 0 and 255 inclusive (i.e. 8-bit unsigned integer)
// NOTE: This is currently always true for Gfx::Color.
// 4. HTML-compatible serialization is requested
if (alpha() == 255
&& html_compatible_serialization == HTMLCompatibleSerialization::Yes) {
return MUST(String::formatted("#{:02x}{:02x}{:02x}", red(), green(), blue()));
}
// Otherwise, for sRGB the CSS serialization of sRGB values is used and for other color spaces, the relevant serialization of the <color> value.
if (alpha() < 255)
return MUST(String::formatted("rgba({}, {}, {}, {})", red(), green(), blue(), alpha() / 255.0));
return MUST(String::formatted("rgb({}, {}, {})", red(), green(), blue()));
} }
String Color::to_string_without_alpha() const String Color::to_string_without_alpha() const

View file

@ -446,7 +446,12 @@ public:
return m_value == other.m_value; return m_value == other.m_value;
} }
String to_string() const; enum class HTMLCompatibleSerialization {
No,
Yes,
};
[[nodiscard]] String to_string(HTMLCompatibleSerialization = HTMLCompatibleSerialization::No) const;
String to_string_without_alpha() const; String to_string_without_alpha() const;
ByteString to_byte_string() const; ByteString to_byte_string() const;

View file

@ -2847,6 +2847,7 @@ RefPtr<CSSStyleValue> Parser::parse_rgb_color_value(TokenStream<ComponentValue>&
inner_tokens.skip_whitespace(); inner_tokens.skip_whitespace();
alpha = parse_number_percentage_value(inner_tokens); alpha = parse_number_percentage_value(inner_tokens);
inner_tokens.skip_whitespace(); inner_tokens.skip_whitespace();
if (inner_tokens.has_next_token()) if (inner_tokens.has_next_token())

View file

@ -63,7 +63,7 @@ public:
{ {
return m_fill_or_stroke_style.visit( return m_fill_or_stroke_style.visit(
[&](Gfx::Color color) -> JsFillOrStrokeStyle { [&](Gfx::Color color) -> JsFillOrStrokeStyle {
return color.to_string(); return color.to_string(Gfx::Color::HTMLCompatibleSerialization::Yes);
}, },
[&](auto handle) -> JsFillOrStrokeStyle { [&](auto handle) -> JsFillOrStrokeStyle {
return handle; return handle;