Serenity Operating System
at hosted 397 lines 12 kB view raw
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}