diff --git a/Libraries/LibGfx/Color.cpp b/Libraries/LibGfx/Color.cpp index f585b4a8e97..5f67a15cf09 100644 --- a/Libraries/LibGfx/Color.cpp +++ b/Libraries/LibGfx/Color.cpp @@ -25,6 +25,73 @@ namespace Gfx { +namespace { + +char nth_digit(u32 value, u8 digit) +{ + // This helper is used to format integers. + // nth_digit(745, 1) -> '5' + // nth_digit(745, 2) -> '4' + // nth_digit(745, 3) -> '7' + + VERIFY(value < 1000); + VERIFY(digit <= 3); + VERIFY(digit > 0); + + while (digit > 1) { + value /= 10; + digit--; + } + + return '0' + value % 10; +} + +Array format_to_8bit_compatible(u8 value) +{ + // This function formats to the shortest string that roundtrips at 8 bits. + // As an example: + // 127 / 255 = 0.498 ± 0.001 + // 128 / 255 = 0.502 ± 0.001 + // But round(.5 * 255) == 128, so this function returns (note that it's only the fractional part): + // 127 -> "498" + // 128 -> "5" + + u32 const three_digits = (value * 1000u + 127) / 255; + u32 const rounded_to_two_digits = (three_digits + 5) / 10 * 10; + + if ((rounded_to_two_digits * 255 / 100 + 5) / 10 != value) + return { nth_digit(three_digits, 3), nth_digit(three_digits, 2), nth_digit(three_digits, 1), '\0' }; + + u32 const rounded_to_one_digit = (three_digits + 50) / 100 * 100; + if ((rounded_to_one_digit * 255 / 100 + 5) / 10 != value) + return { nth_digit(rounded_to_two_digits, 3), nth_digit(rounded_to_two_digits, 2), '\0', '\0' }; + + return { nth_digit(rounded_to_one_digit, 3), '\0', '\0', '\0' }; +} + +} + +// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values +void Color::serialize_a_srgb_value(StringBuilder& builder) const +{ + // The serialized form is derived from the computed value and thus, uses either the rgb() or rgba() form + // (depending on whether the alpha is exactly 1, or not), with lowercase letters for the function name. + // NOTE: Since we use Gfx::Color, having an "alpha of 1" means its value is 255. + if (alpha() == 0) + builder.appendff("rgba({}, {}, {}, 0)", red(), green(), blue()); + else if (alpha() == 255) + builder.appendff("rgb({}, {}, {})", red(), green(), blue()); + else + builder.appendff("rgba({}, {}, {}, 0.{})", red(), green(), blue(), format_to_8bit_compatible(alpha()).data()); +} + +String Color::serialize_a_srgb_value() const +{ + StringBuilder builder; + serialize_a_srgb_value(builder); + return builder.to_string_without_validation(); +} + String Color::to_string(HTMLCompatibleSerialization html_compatible_serialization) const { // If the following conditions are all true: diff --git a/Libraries/LibGfx/Color.h b/Libraries/LibGfx/Color.h index 2d56c726eb3..2a66b287569 100644 --- a/Libraries/LibGfx/Color.h +++ b/Libraries/LibGfx/Color.h @@ -502,6 +502,9 @@ public: String to_string_without_alpha() const; Utf16String to_utf16_string_without_alpha() const; + void serialize_a_srgb_value(StringBuilder&) const; + String serialize_a_srgb_value() const; + ByteString to_byte_string() const; ByteString to_byte_string_without_alpha() const; static Optional from_string(StringView); diff --git a/Libraries/LibWeb/CSS/Serialize.cpp b/Libraries/LibWeb/CSS/Serialize.cpp index eb32d45ae66..bd42938b324 100644 --- a/Libraries/LibWeb/CSS/Serialize.cpp +++ b/Libraries/LibWeb/CSS/Serialize.cpp @@ -127,66 +127,6 @@ void serialize_unicode_ranges(StringBuilder& builder, Vector }); } -namespace { - -char nth_digit(u32 value, u8 digit) -{ - // This helper is used to format integers. - // nth_digit(745, 1) -> '5' - // nth_digit(745, 2) -> '4' - // nth_digit(745, 3) -> '7' - - VERIFY(value < 1000); - VERIFY(digit <= 3); - VERIFY(digit > 0); - - while (digit > 1) { - value /= 10; - digit--; - } - - return '0' + value % 10; -} - -Array format_to_8bit_compatible(u8 value) -{ - // This function formats to the shortest string that roundtrips at 8 bits. - // As an example: - // 127 / 255 = 0.498 ± 0.001 - // 128 / 255 = 0.502 ± 0.001 - // But round(.5 * 255) == 128, so this function returns (note that it's only the fractional part): - // 127 -> "498" - // 128 -> "5" - - u32 const three_digits = (value * 1000u + 127) / 255; - u32 const rounded_to_two_digits = (three_digits + 5) / 10 * 10; - - if ((rounded_to_two_digits * 255 / 100 + 5) / 10 != value) - return { nth_digit(three_digits, 3), nth_digit(three_digits, 2), nth_digit(three_digits, 1), '\0' }; - - u32 const rounded_to_one_digit = (three_digits + 50) / 100 * 100; - if ((rounded_to_one_digit * 255 / 100 + 5) / 10 != value) - return { nth_digit(rounded_to_two_digits, 3), nth_digit(rounded_to_two_digits, 2), '\0', '\0' }; - - return { nth_digit(rounded_to_one_digit, 3), '\0', '\0', '\0' }; -} - -} - -// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values -void serialize_a_srgb_value(StringBuilder& builder, Color color) -{ - // The serialized form is derived from the computed value and thus, uses either the rgb() or rgba() form - // (depending on whether the alpha is exactly 1, or not), with lowercase letters for the function name. - // NOTE: Since we use Gfx::Color, having an "alpha of 1" means its value is 255. - if (color.alpha() == 0) - builder.appendff("rgba({}, {}, {}, 0)", color.red(), color.green(), color.blue()); - else if (color.alpha() == 255) - builder.appendff("rgb({}, {}, {})", color.red(), color.green(), color.blue()); - else - builder.appendff("rgba({}, {}, {}, 0.{})", color.red(), color.green(), color.blue(), format_to_8bit_compatible(color.alpha()).data()); -} - // https://drafts.csswg.org/cssom/#serialize-a-css-value void serialize_a_number(StringBuilder& builder, double value) { @@ -218,13 +158,6 @@ String serialize_a_url(StringView url) return builder.to_string_without_validation(); } -String serialize_a_srgb_value(Color color) -{ - StringBuilder builder; - serialize_a_srgb_value(builder, color); - return builder.to_string_without_validation(); -} - String serialize_a_number(double value) { StringBuilder builder; diff --git a/Libraries/LibWeb/CSS/Serialize.h b/Libraries/LibWeb/CSS/Serialize.h index 3f1352ff950..07a6dda1211 100644 --- a/Libraries/LibWeb/CSS/Serialize.h +++ b/Libraries/LibWeb/CSS/Serialize.h @@ -23,13 +23,11 @@ WEB_API void serialize_an_identifier(StringBuilder&, StringView ident); void serialize_a_string(StringBuilder&, StringView string); WEB_API void serialize_a_url(StringBuilder&, StringView url); void serialize_unicode_ranges(StringBuilder&, Vector const& unicode_ranges); -void serialize_a_srgb_value(StringBuilder&, Color color); void serialize_a_number(StringBuilder&, double value); String serialize_an_identifier(StringView ident); String serialize_a_string(StringView string); String serialize_a_url(StringView url); -String serialize_a_srgb_value(Color color); String serialize_a_number(double value); // https://www.w3.org/TR/cssom/#serialize-a-comma-separated-list diff --git a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp index 41fa5164b42..a0b949da265 100644 --- a/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/FilterValueListStyleValue.cpp @@ -57,7 +57,7 @@ String FilterValueListStyleValue::to_string(SerializationMode mode) const [&](FilterOperation::DropShadow const& drop_shadow) { builder.append("drop-shadow("sv); if (drop_shadow.color.has_value()) { - serialize_a_srgb_value(builder, *drop_shadow.color); + drop_shadow.color->serialize_a_srgb_value(builder); builder.append(' '); } builder.appendff("{} {}", drop_shadow.offset_x, drop_shadow.offset_y); diff --git a/Libraries/LibWeb/CSS/StyleValues/HSLColorStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/HSLColorStyleValue.cpp index 77ab97ed986..2437193dc08 100644 --- a/Libraries/LibWeb/CSS/StyleValues/HSLColorStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/HSLColorStyleValue.cpp @@ -40,7 +40,7 @@ bool HSLColorStyleValue::equals(StyleValue const& other) const String HSLColorStyleValue::to_string(SerializationMode mode) const { if (auto color = to_color({}); color.has_value()) - return serialize_a_srgb_value(color.value()); + return color->serialize_a_srgb_value(); StringBuilder builder; diff --git a/Libraries/LibWeb/CSS/StyleValues/HWBColorStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/HWBColorStyleValue.cpp index c7ed34eb9d1..7974d31979b 100644 --- a/Libraries/LibWeb/CSS/StyleValues/HWBColorStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/HWBColorStyleValue.cpp @@ -53,7 +53,7 @@ bool HWBColorStyleValue::equals(StyleValue const& other) const String HWBColorStyleValue::to_string(SerializationMode mode) const { if (auto color = to_color({}); color.has_value()) - return serialize_a_srgb_value(color.value()); + return color->serialize_a_srgb_value(); StringBuilder builder; builder.append("hwb("sv); diff --git a/Libraries/LibWeb/CSS/StyleValues/RGBColorStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/RGBColorStyleValue.cpp index 97235bb401f..42af7dfe3c1 100644 --- a/Libraries/LibWeb/CSS/StyleValues/RGBColorStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/RGBColorStyleValue.cpp @@ -90,7 +90,7 @@ String RGBColorStyleValue::to_string(SerializationMode mode) const return m_properties.name.value().to_string().to_ascii_lowercase(); if (auto color = to_color({}); color.has_value()) - return serialize_a_srgb_value(color.value()); + return color->serialize_a_srgb_value(); StringBuilder builder; builder.append("rgb("sv);