ladybird/Userland/Libraries/LibGfx/Color.cpp
Ben Wiederhake 6b7ce19161 Everywhere: Remove unused includes of LibC/stdlib.h
These instances were detected by searching for files that include
stdlib.h, but don't match the regex:

\\b(_abort|abort|abs|aligned_alloc|arc4random|arc4random_buf|arc4random_
uniform|atexit|atof|atoi|atol|atoll|bsearch|calloc|clearenv|div|div_t|ex
it|_Exit|EXIT_FAILURE|EXIT_SUCCESS|free|getenv|getprogname|grantpt|labs|
ldiv|ldiv_t|llabs|lldiv|lldiv_t|malloc|malloc_good_size|malloc_size|mble
n|mbstowcs|mbtowc|mkdtemp|mkstemp|mkstemps|mktemp|posix_memalign|posix_o
penpt|ptsname|ptsname_r|putenv|qsort|qsort_r|rand|RAND_MAX|random|reallo
c|realpath|secure_getenv|serenity_dump_malloc_stats|serenity_setenv|sete
nv|setprogname|srand|srandom|strtod|strtof|strtol|strtold|strtoll|strtou
l|strtoull|system|unlockpt|unsetenv|wcstombs|wctomb)\\b

(Without the linebreaks.)

This regex is pessimistic, so there might be more files that don't
actually use anything from the stdlib.

In theory, one might use LibCPP to detect things like this
automatically, but let's do this one step after another.
2023-01-02 20:27:20 -05:00

385 lines
12 KiB
C++

/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Assertions.h>
#include <AK/DeprecatedString.h>
#include <AK/FloatingPointStringConversions.h>
#include <AK/Optional.h>
#include <AK/Vector.h>
#include <LibGfx/Color.h>
#include <LibGfx/SystemTheme.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <ctype.h>
namespace Gfx {
DeprecatedString Color::to_deprecated_string() const
{
return DeprecatedString::formatted("#{:02x}{:02x}{:02x}{:02x}", red(), green(), blue(), alpha());
}
DeprecatedString Color::to_deprecated_string_without_alpha() const
{
return DeprecatedString::formatted("#{:02x}{:02x}{:02x}", red(), green(), blue());
}
static Optional<Color> parse_rgb_color(StringView string)
{
VERIFY(string.starts_with("rgb("sv, CaseSensitivity::CaseInsensitive));
VERIFY(string.ends_with(')'));
auto substring = string.substring_view(4, string.length() - 5);
auto parts = substring.split_view(',');
if (parts.size() != 3)
return {};
auto r = parts[0].to_uint().value_or(256);
auto g = parts[1].to_uint().value_or(256);
auto b = parts[2].to_uint().value_or(256);
if (r > 255 || g > 255 || b > 255)
return {};
return Color(r, g, b);
}
static Optional<Color> parse_rgba_color(StringView string)
{
VERIFY(string.starts_with("rgba("sv, CaseSensitivity::CaseInsensitive));
VERIFY(string.ends_with(')'));
auto substring = string.substring_view(5, string.length() - 6);
auto parts = substring.split_view(',');
if (parts.size() != 4)
return {};
auto r = parts[0].to_int().value_or(256);
auto g = parts[1].to_int().value_or(256);
auto b = parts[2].to_int().value_or(256);
double alpha = 0;
char const* start = parts[3].characters_without_null_termination();
auto alpha_result = parse_first_floating_point(start, start + parts[3].length());
if (alpha_result.parsed_value())
alpha = alpha_result.value;
unsigned a = alpha * 255;
if (r > 255 || g > 255 || b > 255 || a > 255)
return {};
return Color(r, g, b, a);
}
Optional<Color> Color::from_string(StringView string)
{
if (string.is_empty())
return {};
struct ColorAndWebName {
constexpr ColorAndWebName(ARGB32 c, char const* n)
: color(c)
, name(n != nullptr ? StringView { n, __builtin_strlen(n) } : StringView {})
{
}
ARGB32 color;
StringView name;
};
constexpr ColorAndWebName web_colors[] = {
// CSS Level 1
{ 0x000000, "black" },
{ 0xc0c0c0, "silver" },
{ 0x808080, "gray" },
{ 0xffffff, "white" },
{ 0x800000, "maroon" },
{ 0xff0000, "red" },
{ 0x800080, "purple" },
{ 0xff00ff, "fuchsia" },
{ 0x008000, "green" },
{ 0x00ff00, "lime" },
{ 0x808000, "olive" },
{ 0xffff00, "yellow" },
{ 0x000080, "navy" },
{ 0x0000ff, "blue" },
{ 0x008080, "teal" },
{ 0x00ffff, "aqua" },
// CSS Level 2 (Revision 1)
{ 0xffa500, "orange" },
// CSS Color Module Level 3
{ 0xf0f8ff, "aliceblue" },
{ 0xfaebd7, "antiquewhite" },
{ 0x7fffd4, "aquamarine" },
{ 0xf0ffff, "azure" },
{ 0xf5f5dc, "beige" },
{ 0xffe4c4, "bisque" },
{ 0xffebcd, "blanchedalmond" },
{ 0x8a2be2, "blueviolet" },
{ 0xa52a2a, "brown" },
{ 0xdeb887, "burlywood" },
{ 0x5f9ea0, "cadetblue" },
{ 0x7fff00, "chartreuse" },
{ 0xd2691e, "chocolate" },
{ 0xff7f50, "coral" },
{ 0x6495ed, "cornflowerblue" },
{ 0xfff8dc, "cornsilk" },
{ 0xdc143c, "crimson" },
{ 0x00ffff, "cyan" },
{ 0x00008b, "darkblue" },
{ 0x008b8b, "darkcyan" },
{ 0xb8860b, "darkgoldenrod" },
{ 0xa9a9a9, "darkgray" },
{ 0x006400, "darkgreen" },
{ 0xa9a9a9, "darkgrey" },
{ 0xbdb76b, "darkkhaki" },
{ 0x8b008b, "darkmagenta" },
{ 0x556b2f, "darkolivegreen" },
{ 0xff8c00, "darkorange" },
{ 0x9932cc, "darkorchid" },
{ 0x8b0000, "darkred" },
{ 0xe9967a, "darksalmon" },
{ 0x8fbc8f, "darkseagreen" },
{ 0x483d8b, "darkslateblue" },
{ 0x2f4f4f, "darkslategray" },
{ 0x2f4f4f, "darkslategrey" },
{ 0x00ced1, "darkturquoise" },
{ 0x9400d3, "darkviolet" },
{ 0xff1493, "deeppink" },
{ 0x00bfff, "deepskyblue" },
{ 0x696969, "dimgray" },
{ 0x696969, "dimgrey" },
{ 0x1e90ff, "dodgerblue" },
{ 0xb22222, "firebrick" },
{ 0xfffaf0, "floralwhite" },
{ 0x228b22, "forestgreen" },
{ 0xdcdcdc, "gainsboro" },
{ 0xf8f8ff, "ghostwhite" },
{ 0xffd700, "gold" },
{ 0xdaa520, "goldenrod" },
{ 0xadff2f, "greenyellow" },
{ 0x808080, "grey" },
{ 0xf0fff0, "honeydew" },
{ 0xff69b4, "hotpink" },
{ 0xcd5c5c, "indianred" },
{ 0x4b0082, "indigo" },
{ 0xfffff0, "ivory" },
{ 0xf0e68c, "khaki" },
{ 0xe6e6fa, "lavender" },
{ 0xfff0f5, "lavenderblush" },
{ 0x7cfc00, "lawngreen" },
{ 0xfffacd, "lemonchiffon" },
{ 0xadd8e6, "lightblue" },
{ 0xf08080, "lightcoral" },
{ 0xe0ffff, "lightcyan" },
{ 0xfafad2, "lightgoldenrodyellow" },
{ 0xd3d3d3, "lightgray" },
{ 0x90ee90, "lightgreen" },
{ 0xd3d3d3, "lightgrey" },
{ 0xffb6c1, "lightpink" },
{ 0xffa07a, "lightsalmon" },
{ 0x20b2aa, "lightseagreen" },
{ 0x87cefa, "lightskyblue" },
{ 0x778899, "lightslategray" },
{ 0x778899, "lightslategrey" },
{ 0xb0c4de, "lightsteelblue" },
{ 0xffffe0, "lightyellow" },
{ 0x32cd32, "limegreen" },
{ 0xfaf0e6, "linen" },
{ 0xff00ff, "magenta" },
{ 0x66cdaa, "mediumaquamarine" },
{ 0x0000cd, "mediumblue" },
{ 0xba55d3, "mediumorchid" },
{ 0x9370db, "mediumpurple" },
{ 0x3cb371, "mediumseagreen" },
{ 0x7b68ee, "mediumslateblue" },
{ 0x00fa9a, "mediumspringgreen" },
{ 0x48d1cc, "mediumturquoise" },
{ 0xc71585, "mediumvioletred" },
{ 0x191970, "midnightblue" },
{ 0xf5fffa, "mintcream" },
{ 0xffe4e1, "mistyrose" },
{ 0xffe4b5, "moccasin" },
{ 0xffdead, "navajowhite" },
{ 0xfdf5e6, "oldlace" },
{ 0x6b8e23, "olivedrab" },
{ 0xff4500, "orangered" },
{ 0xda70d6, "orchid" },
{ 0xeee8aa, "palegoldenrod" },
{ 0x98fb98, "palegreen" },
{ 0xafeeee, "paleturquoise" },
{ 0xdb7093, "palevioletred" },
{ 0xffefd5, "papayawhip" },
{ 0xffdab9, "peachpuff" },
{ 0xcd853f, "peru" },
{ 0xffc0cb, "pink" },
{ 0xdda0dd, "plum" },
{ 0xb0e0e6, "powderblue" },
{ 0xbc8f8f, "rosybrown" },
{ 0x4169e1, "royalblue" },
{ 0x8b4513, "saddlebrown" },
{ 0xfa8072, "salmon" },
{ 0xf4a460, "sandybrown" },
{ 0x2e8b57, "seagreen" },
{ 0xfff5ee, "seashell" },
{ 0xa0522d, "sienna" },
{ 0x87ceeb, "skyblue" },
{ 0x6a5acd, "slateblue" },
{ 0x708090, "slategray" },
{ 0x708090, "slategrey" },
{ 0xfffafa, "snow" },
{ 0x00ff7f, "springgreen" },
{ 0x4682b4, "steelblue" },
{ 0xd2b48c, "tan" },
{ 0xd8bfd8, "thistle" },
{ 0xff6347, "tomato" },
{ 0x40e0d0, "turquoise" },
{ 0xee82ee, "violet" },
{ 0xf5deb3, "wheat" },
{ 0xf5f5f5, "whitesmoke" },
{ 0x9acd32, "yellowgreen" },
// CSS Color Module Level 4
{ 0x663399, "rebeccapurple" },
// (Fallback)
{ 0x000000, nullptr }
};
if (string.equals_ignoring_case("transparent"sv))
return Color::from_argb(0x00000000);
for (size_t i = 0; !web_colors[i].name.is_null(); ++i) {
if (string.equals_ignoring_case(web_colors[i].name))
return Color::from_rgb(web_colors[i].color);
}
if (string.starts_with("rgb("sv, CaseSensitivity::CaseInsensitive) && string.ends_with(')'))
return parse_rgb_color(string);
if (string.starts_with("rgba("sv, CaseSensitivity::CaseInsensitive) && string.ends_with(')'))
return parse_rgba_color(string);
if (string[0] != '#')
return {};
auto hex_nibble_to_u8 = [](char nibble) -> Optional<u8> {
if (!isxdigit(nibble))
return {};
if (nibble >= '0' && nibble <= '9')
return nibble - '0';
return 10 + (tolower(nibble) - 'a');
};
if (string.length() == 4) {
Optional<u8> r = hex_nibble_to_u8(string[1]);
Optional<u8> g = hex_nibble_to_u8(string[2]);
Optional<u8> b = hex_nibble_to_u8(string[3]);
if (!r.has_value() || !g.has_value() || !b.has_value())
return {};
return Color(r.value() * 17, g.value() * 17, b.value() * 17);
}
if (string.length() == 5) {
Optional<u8> r = hex_nibble_to_u8(string[1]);
Optional<u8> g = hex_nibble_to_u8(string[2]);
Optional<u8> b = hex_nibble_to_u8(string[3]);
Optional<u8> a = hex_nibble_to_u8(string[4]);
if (!r.has_value() || !g.has_value() || !b.has_value() || !a.has_value())
return {};
return Color(r.value() * 17, g.value() * 17, b.value() * 17, a.value() * 17);
}
if (string.length() != 7 && string.length() != 9)
return {};
auto to_hex = [&](char c1, char c2) -> Optional<u8> {
auto nib1 = hex_nibble_to_u8(c1);
auto nib2 = hex_nibble_to_u8(c2);
if (!nib1.has_value() || !nib2.has_value())
return {};
return nib1.value() << 4 | nib2.value();
};
Optional<u8> r = to_hex(string[1], string[2]);
Optional<u8> g = to_hex(string[3], string[4]);
Optional<u8> b = to_hex(string[5], string[6]);
Optional<u8> a = string.length() == 9 ? to_hex(string[7], string[8]) : Optional<u8>(255);
if (!r.has_value() || !g.has_value() || !b.has_value() || !a.has_value())
return {};
return Color(r.value(), g.value(), b.value(), a.value());
}
Color Color::mixed_with(Color other, float weight) const
{
if (alpha() == other.alpha() || with_alpha(0) == other.with_alpha(0)) {
return Gfx::Color {
round_to<u8>(mix<float>(red(), other.red(), weight)),
round_to<u8>(mix<float>(green(), other.green(), weight)),
round_to<u8>(mix<float>(blue(), other.blue(), weight)),
round_to<u8>(mix<float>(alpha(), other.alpha(), weight)),
};
}
// Fallback to slower, but more visually pleasing premultiplied alpha mix.
// This is needed for linear-gradient()s in LibWeb.
auto mixed_alpha = mix<float>(alpha(), other.alpha(), weight);
auto premultiplied_mix_channel = [&](float channel, float other_channel, float weight) {
return round_to<u8>(mix<float>(channel * alpha(), other_channel * other.alpha(), weight) / mixed_alpha);
};
return Gfx::Color {
premultiplied_mix_channel(red(), other.red(), weight),
premultiplied_mix_channel(green(), other.green(), weight),
premultiplied_mix_channel(blue(), other.blue(), weight),
round_to<u8>(mixed_alpha),
};
}
Vector<Color> Color::shades(u32 steps, float max) const
{
float shade = 1.f;
float step = max / steps;
Vector<Color> shades;
for (u32 i = 0; i < steps; i++) {
shade -= step;
shades.append(this->darkened(shade));
}
return shades;
}
Vector<Color> Color::tints(u32 steps, float max) const
{
float shade = 1.f;
float step = max / steps;
Vector<Color> tints;
for (u32 i = 0; i < steps; i++) {
shade += step;
tints.append(this->lightened(shade));
}
return tints;
}
}
template<>
bool IPC::encode(Encoder& encoder, Color const& color)
{
encoder << color.value();
return true;
}
template<>
ErrorOr<Gfx::Color> IPC::decode(Decoder& decoder)
{
auto rgba = TRY(decoder.decode<u32>());
return Gfx::Color::from_argb(rgba);
}
ErrorOr<void> AK::Formatter<Gfx::Color>::format(FormatBuilder& builder, Gfx::Color value)
{
return Formatter<StringView>::format(builder, value.to_deprecated_string());
}