2020-01-18 11:38:21 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
|
|
*
|
2021-04-22 11:24:48 +03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 11:38:21 +03:00
|
|
|
*/
|
|
|
|
|
2019-01-14 17:25:34 +03:00
|
|
|
#include <AK/Assertions.h>
|
2020-02-15 01:28:42 +03:00
|
|
|
#include <AK/Optional.h>
|
|
|
|
#include <AK/String.h>
|
2020-03-21 21:06:38 +03:00
|
|
|
#include <AK/Vector.h>
|
2020-02-06 14:04:00 +03:00
|
|
|
#include <LibGfx/Color.h>
|
|
|
|
#include <LibGfx/SystemTheme.h>
|
2020-03-29 20:03:13 +03:00
|
|
|
#include <LibIPC/Decoder.h>
|
2020-05-12 19:52:51 +03:00
|
|
|
#include <LibIPC/Encoder.h>
|
2019-08-03 12:32:37 +03:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
2020-05-05 16:49:38 +03:00
|
|
|
#include <stdlib.h>
|
2018-10-10 17:49:36 +03:00
|
|
|
|
2020-02-15 01:28:42 +03:00
|
|
|
namespace Gfx {
|
|
|
|
|
2019-03-18 06:53:09 +03:00
|
|
|
String Color::to_string() const
|
|
|
|
{
|
2021-04-21 22:11:16 +03:00
|
|
|
return String::formatted("#{:02x}{:02x}{:02x}{:02x}", red(), green(), blue(), alpha());
|
2020-04-29 16:31:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
String Color::to_string_without_alpha() const
|
|
|
|
{
|
2021-04-21 22:11:16 +03:00
|
|
|
return String::formatted("#{:02x}{:02x}{:02x}", red(), green(), blue());
|
2019-03-18 06:53:09 +03:00
|
|
|
}
|
2019-08-03 12:32:37 +03:00
|
|
|
|
2020-03-21 21:06:38 +03:00
|
|
|
static Optional<Color> parse_rgb_color(const StringView& string)
|
|
|
|
{
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(string.starts_with("rgb("));
|
|
|
|
VERIFY(string.ends_with(")"));
|
2020-03-21 21:06:38 +03:00
|
|
|
|
|
|
|
auto substring = string.substring_view(4, string.length() - 5);
|
|
|
|
auto parts = substring.split_view(',');
|
|
|
|
|
|
|
|
if (parts.size() != 3)
|
|
|
|
return {};
|
|
|
|
|
2020-06-12 22:07:52 +03:00
|
|
|
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);
|
2020-03-21 21:06:38 +03:00
|
|
|
|
2020-06-12 22:07:52 +03:00
|
|
|
if (r > 255 || g > 255 || b > 255)
|
2020-03-21 21:06:38 +03:00
|
|
|
return {};
|
|
|
|
|
|
|
|
return Color(r, g, b);
|
|
|
|
}
|
|
|
|
|
2020-05-05 16:49:38 +03:00
|
|
|
static Optional<Color> parse_rgba_color(const StringView& string)
|
|
|
|
{
|
2021-02-23 22:42:32 +03:00
|
|
|
VERIFY(string.starts_with("rgba("));
|
|
|
|
VERIFY(string.ends_with(")"));
|
2020-05-05 16:49:38 +03:00
|
|
|
|
|
|
|
auto substring = string.substring_view(5, string.length() - 6);
|
|
|
|
auto parts = substring.split_view(',');
|
|
|
|
|
|
|
|
if (parts.size() != 4)
|
|
|
|
return {};
|
|
|
|
|
2020-06-12 22:07:52 +03:00
|
|
|
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);
|
2020-05-05 16:49:38 +03:00
|
|
|
|
2020-05-06 20:18:24 +03:00
|
|
|
double alpha = strtod(parts[3].to_string().characters(), nullptr);
|
2020-06-12 22:07:52 +03:00
|
|
|
unsigned a = alpha * 255;
|
2020-05-05 16:49:38 +03:00
|
|
|
|
2020-06-12 22:07:52 +03:00
|
|
|
if (r > 255 || g > 255 || b > 255 || a > 255)
|
2020-05-05 16:49:38 +03:00
|
|
|
return {};
|
|
|
|
|
|
|
|
return Color(r, g, b, a);
|
|
|
|
}
|
|
|
|
|
2019-08-03 12:32:37 +03:00
|
|
|
Optional<Color> Color::from_string(const StringView& string)
|
|
|
|
{
|
|
|
|
if (string.is_empty())
|
|
|
|
return {};
|
|
|
|
|
2019-10-06 22:35:56 +03:00
|
|
|
struct ColorAndWebName {
|
2020-05-30 17:06:59 +03:00
|
|
|
constexpr ColorAndWebName(RGBA32 c, const char* n)
|
|
|
|
: color(c)
|
|
|
|
, name(n)
|
|
|
|
{
|
|
|
|
}
|
2019-10-06 22:35:56 +03:00
|
|
|
RGBA32 color;
|
2020-05-30 17:06:59 +03:00
|
|
|
StringView name;
|
2019-10-06 22:35:56 +03:00
|
|
|
};
|
2019-08-03 12:32:37 +03:00
|
|
|
|
2020-05-30 17:06:59 +03:00
|
|
|
constexpr ColorAndWebName web_colors[] = {
|
2019-12-23 06:19:15 +03:00
|
|
|
// CSS Level 1
|
|
|
|
{ 0x000000, "black" },
|
|
|
|
{ 0xc0c0c0, "silver" },
|
|
|
|
{ 0x808080, "gray" },
|
|
|
|
{ 0xffffff, "white" },
|
|
|
|
{ 0x800000, "maroon" },
|
|
|
|
{ 0xff0000, "red" },
|
2019-10-06 22:35:56 +03:00
|
|
|
{ 0x800080, "purple" },
|
|
|
|
{ 0xff00ff, "fuchsia" },
|
|
|
|
{ 0x008000, "green" },
|
2019-12-23 06:19:15 +03:00
|
|
|
{ 0x00ff00, "lime" },
|
|
|
|
{ 0x808000, "olive" },
|
|
|
|
{ 0xffff00, "yellow" },
|
2019-10-06 22:35:56 +03:00
|
|
|
{ 0x000080, "navy" },
|
|
|
|
{ 0x0000ff, "blue" },
|
|
|
|
{ 0x008080, "teal" },
|
2019-12-23 06:19:15 +03:00
|
|
|
{ 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" },
|
2020-09-29 01:40:25 +03:00
|
|
|
{ 0xfafad2, "lightgoldenrodyellow" },
|
2019-12-23 06:19:15 +03:00
|
|
|
{ 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" },
|
2020-09-29 01:40:25 +03:00
|
|
|
{ 0x66cdaa, "mediumaquamarine" },
|
2019-12-23 06:19:15 +03:00
|
|
|
{ 0x0000cd, "mediumblue" },
|
|
|
|
{ 0xba55d3, "mediumorchid" },
|
|
|
|
{ 0x9370db, "mediumpurple" },
|
|
|
|
{ 0x3cb371, "mediumseagreen" },
|
|
|
|
{ 0x7b68ee, "mediumslateblue" },
|
2020-09-29 01:40:25 +03:00
|
|
|
{ 0x00fa9a, "mediumspringgreen" },
|
2019-12-23 06:19:15 +03:00
|
|
|
{ 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" },
|
2019-12-16 21:45:04 +03:00
|
|
|
{ 0xffc0cb, "pink" },
|
2019-12-23 06:19:15 +03:00
|
|
|
{ 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)
|
2019-10-06 22:35:56 +03:00
|
|
|
{ 0x000000, nullptr }
|
|
|
|
};
|
|
|
|
|
2021-10-23 15:05:38 +03:00
|
|
|
if (string == "transparent")
|
|
|
|
return Color::from_rgba(0x00000000);
|
|
|
|
|
2020-05-30 17:06:59 +03:00
|
|
|
for (size_t i = 0; !web_colors[i].name.is_null(); ++i) {
|
2019-10-06 22:35:56 +03:00
|
|
|
if (string == web_colors[i].name)
|
|
|
|
return Color::from_rgb(web_colors[i].color);
|
|
|
|
}
|
|
|
|
|
2020-03-21 21:06:38 +03:00
|
|
|
if (string.starts_with("rgb(") && string.ends_with(")"))
|
|
|
|
return parse_rgb_color(string);
|
|
|
|
|
2020-05-05 16:49:38 +03:00
|
|
|
if (string.starts_with("rgba(") && string.ends_with(")"))
|
|
|
|
return parse_rgba_color(string);
|
|
|
|
|
2019-10-06 22:35:56 +03:00
|
|
|
if (string[0] != '#')
|
2019-08-03 12:32:37 +03:00
|
|
|
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');
|
|
|
|
};
|
|
|
|
|
2019-10-06 22:35:56 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-01-06 21:29:49 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-10-06 22:35:56 +03:00
|
|
|
if (string.length() != 7 && string.length() != 9)
|
|
|
|
return {};
|
|
|
|
|
2019-08-03 12:32:37 +03:00
|
|
|
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());
|
|
|
|
}
|
2020-02-15 01:28:42 +03:00
|
|
|
|
2020-06-02 21:29:34 +03:00
|
|
|
}
|
2020-02-15 14:04:35 +03:00
|
|
|
|
2020-05-12 19:52:51 +03:00
|
|
|
bool IPC::encode(IPC::Encoder& encoder, const Color& color)
|
|
|
|
{
|
|
|
|
encoder << color.value();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-29 20:03:13 +03:00
|
|
|
bool IPC::decode(IPC::Decoder& decoder, Color& color)
|
2020-02-15 14:04:35 +03:00
|
|
|
{
|
2020-02-23 03:32:00 +03:00
|
|
|
u32 rgba = 0;
|
2020-03-29 20:03:13 +03:00
|
|
|
if (!decoder.decode(rgba))
|
2020-02-15 14:04:35 +03:00
|
|
|
return false;
|
|
|
|
color = Color::from_rgba(rgba);
|
|
|
|
return true;
|
|
|
|
}
|
2020-10-26 18:47:38 +03:00
|
|
|
|
2020-12-30 14:14:15 +03:00
|
|
|
void AK::Formatter<Gfx::Color>::format(FormatBuilder& builder, const Gfx::Color& value)
|
2020-10-26 18:47:38 +03:00
|
|
|
{
|
2020-12-30 14:14:15 +03:00
|
|
|
Formatter<StringView>::format(builder, value.to_string());
|
2020-10-26 18:47:38 +03:00
|
|
|
}
|