Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/Assertions.h>
28#include <AK/BufferStream.h>
29#include <AK/Optional.h>
30#include <AK/String.h>
31#include <AK/Vector.h>
32#include <LibGfx/Color.h>
33#include <LibGfx/SystemTheme.h>
34#include <LibIPC/Decoder.h>
35#include <ctype.h>
36#include <stdio.h>
37
38namespace Gfx {
39
40Color::Color(NamedColor named)
41{
42 struct {
43 u8 r;
44 u8 g;
45 u8 b;
46 } rgb;
47
48 switch (named) {
49 case Black:
50 rgb = { 0, 0, 0 };
51 break;
52 case White:
53 rgb = { 255, 255, 255 };
54 break;
55 case Red:
56 rgb = { 255, 0, 0 };
57 break;
58 case Green:
59 rgb = { 0, 255, 0 };
60 break;
61 case Cyan:
62 rgb = { 0, 255, 255 };
63 break;
64 case DarkCyan:
65 rgb = { 0, 127, 127 };
66 break;
67 case MidCyan:
68 rgb = { 0, 192, 192 };
69 break;
70 case Blue:
71 rgb = { 0, 0, 255 };
72 break;
73 case Yellow:
74 rgb = { 255, 255, 0 };
75 break;
76 case Magenta:
77 rgb = { 255, 0, 255 };
78 break;
79 case DarkGray:
80 rgb = { 64, 64, 64 };
81 break;
82 case MidGray:
83 rgb = { 127, 127, 127 };
84 break;
85 case LightGray:
86 rgb = { 192, 192, 192 };
87 break;
88 case MidGreen:
89 rgb = { 0, 192, 0 };
90 break;
91 case MidBlue:
92 rgb = { 0, 0, 192 };
93 break;
94 case MidRed:
95 rgb = { 192, 0, 0 };
96 break;
97 case MidMagenta:
98 rgb = { 192, 0, 192 };
99 break;
100 case DarkGreen:
101 rgb = { 0, 128, 0 };
102 break;
103 case DarkBlue:
104 rgb = { 0, 0, 128 };
105 break;
106 case DarkRed:
107 rgb = { 128, 0, 0 };
108 break;
109 case WarmGray:
110 rgb = { 212, 208, 200 };
111 break;
112 default:
113 ASSERT_NOT_REACHED();
114 break;
115 }
116
117 m_value = 0xff000000 | (rgb.r << 16) | (rgb.g << 8) | rgb.b;
118}
119
120String Color::to_string() const
121{
122 return String::format("#%b%b%b%b", red(), green(), blue(), alpha());
123}
124
125static Optional<Color> parse_rgb_color(const StringView& string)
126{
127 ASSERT(string.starts_with("rgb("));
128 ASSERT(string.ends_with(")"));
129
130 auto substring = string.substring_view(4, string.length() - 5);
131 auto parts = substring.split_view(',');
132
133 if (parts.size() != 3)
134 return {};
135
136 bool ok;
137 auto r = parts[0].to_int(ok);
138 if (!ok)
139 return {};
140 auto g = parts[1].to_int(ok);
141 if (!ok)
142 return {};
143 auto b = parts[2].to_int(ok);
144 if (!ok)
145 return {};
146
147 if (r < 0 || r > 255)
148 return {};
149 if (g < 0 || g > 255)
150 return {};
151 if (b < 0 || b > 255)
152 return {};
153
154 return Color(r, g, b);
155}
156
157Optional<Color> Color::from_string(const StringView& string)
158{
159 if (string.is_empty())
160 return {};
161
162 struct ColorAndWebName {
163 RGBA32 color;
164 const char* name;
165 };
166
167 const ColorAndWebName web_colors[] = {
168 // CSS Level 1
169 { 0x000000, "black" },
170 { 0xc0c0c0, "silver" },
171 { 0x808080, "gray" },
172 { 0xffffff, "white" },
173 { 0x800000, "maroon" },
174 { 0xff0000, "red" },
175 { 0x800080, "purple" },
176 { 0xff00ff, "fuchsia" },
177 { 0x008000, "green" },
178 { 0x00ff00, "lime" },
179 { 0x808000, "olive" },
180 { 0xffff00, "yellow" },
181 { 0x000080, "navy" },
182 { 0x0000ff, "blue" },
183 { 0x008080, "teal" },
184 { 0x00ffff, "aqua" },
185 // CSS Level 2 (Revision 1)
186 { 0xffa500, "orange" },
187 // CSS Color Module Level 3
188 { 0xf0f8ff, "aliceblue" },
189 { 0xfaebd7, "antiquewhite" },
190 { 0x7fffd4, "aquamarine" },
191 { 0xf0ffff, "azure" },
192 { 0xf5f5dc, "beige" },
193 { 0xffe4c4, "bisque" },
194 { 0xffebcd, "blanchedalmond" },
195 { 0x8a2be2, "blueviolet" },
196 { 0xa52a2a, "brown" },
197 { 0xdeb887, "burlywood" },
198 { 0x5f9ea0, "cadetblue" },
199 { 0x7fff00, "chartreuse" },
200 { 0xd2691e, "chocolate" },
201 { 0xff7f50, "coral" },
202 { 0x6495ed, "cornflowerblue" },
203 { 0xfff8dc, "cornsilk" },
204 { 0xdc143c, "crimson" },
205 { 0x00ffff, "cyan" },
206 { 0x00008b, "darkblue" },
207 { 0x008b8b, "darkcyan" },
208 { 0xb8860b, "darkgoldenrod" },
209 { 0xa9a9a9, "darkgray" },
210 { 0x006400, "darkgreen" },
211 { 0xa9a9a9, "darkgrey" },
212 { 0xbdb76b, "darkkhaki" },
213 { 0x8b008b, "darkmagenta" },
214 { 0x556b2f, "darkolivegreen" },
215 { 0xff8c00, "darkorange" },
216 { 0x9932cc, "darkorchid" },
217 { 0x8b0000, "darkred" },
218 { 0xe9967a, "darksalmon" },
219 { 0x8fbc8f, "darkseagreen" },
220 { 0x483d8b, "darkslateblue" },
221 { 0x2f4f4f, "darkslategray" },
222 { 0x2f4f4f, "darkslategrey" },
223 { 0x00ced1, "darkturquoise" },
224 { 0x9400d3, "darkviolet" },
225 { 0xff1493, "deeppink" },
226 { 0x00bfff, "deepskyblue" },
227 { 0x696969, "dimgray" },
228 { 0x696969, "dimgrey" },
229 { 0x1e90ff, "dodgerblue" },
230 { 0xb22222, "firebrick" },
231 { 0xfffaf0, "floralwhite" },
232 { 0x228b22, "forestgreen" },
233 { 0xdcdcdc, "gainsboro" },
234 { 0xf8f8ff, "ghostwhite" },
235 { 0xffd700, "gold" },
236 { 0xdaa520, "goldenrod" },
237 { 0xadff2f, "greenyellow" },
238 { 0x808080, "grey" },
239 { 0xf0fff0, "honeydew" },
240 { 0xff69b4, "hotpink" },
241 { 0xcd5c5c, "indianred" },
242 { 0x4b0082, "indigo" },
243 { 0xfffff0, "ivory" },
244 { 0xf0e68c, "khaki" },
245 { 0xe6e6fa, "lavender" },
246 { 0xfff0f5, "lavenderblush" },
247 { 0x7cfc00, "lawngreen" },
248 { 0xfffacd, "lemonchiffon" },
249 { 0xadd8e6, "lightblue" },
250 { 0xf08080, "lightcoral" },
251 { 0xe0ffff, "lightcyan" },
252 { 0xfafad2, "lightgoldenrody" },
253 { 0xd3d3d3, "lightgray" },
254 { 0x90ee90, "lightgreen" },
255 { 0xd3d3d3, "lightgrey" },
256 { 0xffb6c1, "lightpink" },
257 { 0xffa07a, "lightsalmon" },
258 { 0x20b2aa, "lightseagreen" },
259 { 0x87cefa, "lightskyblue" },
260 { 0x778899, "lightslategray" },
261 { 0x778899, "lightslategrey" },
262 { 0xb0c4de, "lightsteelblue" },
263 { 0xffffe0, "lightyellow" },
264 { 0x32cd32, "limegreen" },
265 { 0xfaf0e6, "linen" },
266 { 0xff00ff, "magenta" },
267 { 0x66cdaa, "mediumaquamarin" },
268 { 0x0000cd, "mediumblue" },
269 { 0xba55d3, "mediumorchid" },
270 { 0x9370db, "mediumpurple" },
271 { 0x3cb371, "mediumseagreen" },
272 { 0x7b68ee, "mediumslateblue" },
273 { 0x00fa9a, "mediumspringgre" },
274 { 0x48d1cc, "mediumturquoise" },
275 { 0xc71585, "mediumvioletred" },
276 { 0x191970, "midnightblue" },
277 { 0xf5fffa, "mintcream" },
278 { 0xffe4e1, "mistyrose" },
279 { 0xffe4b5, "moccasin" },
280 { 0xffdead, "navajowhite" },
281 { 0xfdf5e6, "oldlace" },
282 { 0x6b8e23, "olivedrab" },
283 { 0xff4500, "orangered" },
284 { 0xda70d6, "orchid" },
285 { 0xeee8aa, "palegoldenrod" },
286 { 0x98fb98, "palegreen" },
287 { 0xafeeee, "paleturquoise" },
288 { 0xdb7093, "palevioletred" },
289 { 0xffefd5, "papayawhip" },
290 { 0xffdab9, "peachpuff" },
291 { 0xcd853f, "peru" },
292 { 0xffc0cb, "pink" },
293 { 0xdda0dd, "plum" },
294 { 0xb0e0e6, "powderblue" },
295 { 0xbc8f8f, "rosybrown" },
296 { 0x4169e1, "royalblue" },
297 { 0x8b4513, "saddlebrown" },
298 { 0xfa8072, "salmon" },
299 { 0xf4a460, "sandybrown" },
300 { 0x2e8b57, "seagreen" },
301 { 0xfff5ee, "seashell" },
302 { 0xa0522d, "sienna" },
303 { 0x87ceeb, "skyblue" },
304 { 0x6a5acd, "slateblue" },
305 { 0x708090, "slategray" },
306 { 0x708090, "slategrey" },
307 { 0xfffafa, "snow" },
308 { 0x00ff7f, "springgreen" },
309 { 0x4682b4, "steelblue" },
310 { 0xd2b48c, "tan" },
311 { 0xd8bfd8, "thistle" },
312 { 0xff6347, "tomato" },
313 { 0x40e0d0, "turquoise" },
314 { 0xee82ee, "violet" },
315 { 0xf5deb3, "wheat" },
316 { 0xf5f5f5, "whitesmoke" },
317 { 0x9acd32, "yellowgreen" },
318 // CSS Color Module Level 4
319 { 0x663399, "rebeccapurple" },
320 // (Fallback)
321 { 0x000000, nullptr }
322 };
323
324 for (size_t i = 0; web_colors[i].name; ++i) {
325 if (string == web_colors[i].name)
326 return Color::from_rgb(web_colors[i].color);
327 }
328
329 if (string.starts_with("rgb(") && string.ends_with(")"))
330 return parse_rgb_color(string);
331
332 if (string[0] != '#')
333 return {};
334
335 auto hex_nibble_to_u8 = [](char nibble) -> Optional<u8> {
336 if (!isxdigit(nibble))
337 return {};
338 if (nibble >= '0' && nibble <= '9')
339 return nibble - '0';
340 return 10 + (tolower(nibble) - 'a');
341 };
342
343 if (string.length() == 4) {
344 Optional<u8> r = hex_nibble_to_u8(string[1]);
345 Optional<u8> g = hex_nibble_to_u8(string[2]);
346 Optional<u8> b = hex_nibble_to_u8(string[3]);
347 if (!r.has_value() || !g.has_value() || !b.has_value())
348 return {};
349 return Color(r.value() * 17, g.value() * 17, b.value() * 17);
350 }
351
352 if (string.length() == 5) {
353 Optional<u8> r = hex_nibble_to_u8(string[1]);
354 Optional<u8> g = hex_nibble_to_u8(string[2]);
355 Optional<u8> b = hex_nibble_to_u8(string[3]);
356 Optional<u8> a = hex_nibble_to_u8(string[4]);
357 if (!r.has_value() || !g.has_value() || !b.has_value() || !a.has_value())
358 return {};
359 return Color(r.value() * 17, g.value() * 17, b.value() * 17, a.value() * 17);
360 }
361
362 if (string.length() != 7 && string.length() != 9)
363 return {};
364
365 auto to_hex = [&](char c1, char c2) -> Optional<u8> {
366 auto nib1 = hex_nibble_to_u8(c1);
367 auto nib2 = hex_nibble_to_u8(c2);
368 if (!nib1.has_value() || !nib2.has_value())
369 return {};
370 return nib1.value() << 4 | nib2.value();
371 };
372
373 Optional<u8> r = to_hex(string[1], string[2]);
374 Optional<u8> g = to_hex(string[3], string[4]);
375 Optional<u8> b = to_hex(string[5], string[6]);
376 Optional<u8> a = string.length() == 9 ? to_hex(string[7], string[8]) : Optional<u8>(255);
377
378 if (!r.has_value() || !g.has_value() || !b.has_value() || !a.has_value())
379 return {};
380
381 return Color(r.value(), g.value(), b.value(), a.value());
382}
383}
384
385const LogStream& operator<<(const LogStream& stream, Color value)
386{
387 return stream << value.to_string();
388}
389
390bool IPC::decode(IPC::Decoder& decoder, Color& color)
391{
392 u32 rgba = 0;
393 if (!decoder.decode(rgba))
394 return false;
395 color = Color::from_rgba(rgba);
396 return true;
397}