From 813c088c1c66df45731c9015248b22d55e694c76 Mon Sep 17 00:00:00 2001 From: Craig Robbins Date: Wed, 29 Oct 2014 16:40:55 +1000 Subject: [PATCH] Added names colours and refactored parseColorString() --- doc/lua_api.txt | 6 + src/util/string.cpp | 321 ++++++++++++++++++++++++++++++++++++-------- src/util/string.h | 9 +- 3 files changed, 274 insertions(+), 62 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ff2143cc8..698efbe8a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1265,6 +1265,12 @@ ColorString #RRGGBBAA ^ defines a color in hexadecimal format and alpha channel +Named colors are also supported and are equivalent to "CSS Color Module Level 4" +(http://dev.w3.org/csswg/css-color/#named-colors). To specify the value of the +alpha channel, append #AA to the end of the color name (e.g. colorname#08). For +named colors the hexadecimal string representing the alpha value must (always) +be two hexadecimal digits. + Vector helpers --------------- vector.new([x[, y, z]]) -> vector diff --git a/src/util/string.cpp b/src/util/string.cpp index 57545aa20..c590e7e57 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -24,12 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include #include "../sha1.h" #include "../base64.h" #include "../hex.h" #include "../porting.h" +static bool parseHexColorString(const std::string &value, video::SColor &color); +static bool parseNamedColorString(const std::string &value, video::SColor &color); + #ifdef __ANDROID__ const wchar_t* wide_chars = L" !\"#$%&'()*+,-./0123456789:;<=>?@" @@ -307,62 +311,269 @@ u64 read_seed(const char *str) bool parseColorString(const std::string &value, video::SColor &color, bool quiet) { - const char *hexpattern = NULL; - video::SColor outcolor(255, 255, 255, 255); + bool success; - if (value[0] == '#') { - if (value.size() == 9) - hexpattern = "#RRGGBBAA"; - else if (value.size() == 7) - hexpattern = "#RRGGBB"; - else if (value.size() == 5) - hexpattern = "#RGBA"; - else if (value.size() == 4) - hexpattern = "#RGB"; - } + if (value[0] == '#') + success = parseHexColorString(value, color); + else + success = parseNamedColorString(value, color); - if (!hexpattern) - goto fail; - - assert(strlen(hexpattern) == value.size()); - for (size_t pos = 0; pos < value.size(); ++pos) { - // '#' in the pattern means skip that character - if (hexpattern[pos] == '#') - continue; - - // Else assume hexpattern[pos] is one of 'R' 'G' 'B' 'A' - // Read one or two digits, depending on hexpattern - unsigned char c1, c2; - if (hexpattern[pos+1] == hexpattern[pos]) { - // Two digits, e.g. hexpattern == "#RRGGBB" - if (!hex_digit_decode(value[pos], c1) || - !hex_digit_decode(value[pos+1], c2)) - goto fail; - ++pos; - } else { - // One digit, e.g. hexpattern == "#RGB" - if (!hex_digit_decode(value[pos], c1)) - goto fail; - c2 = c1; - } - u32 colorpart = ((c1 & 0x0f) << 4) | (c2 & 0x0f); - - // Update outcolor with newly read color part - if (hexpattern[pos] == 'R') - outcolor.setRed(colorpart); - else if (hexpattern[pos] == 'G') - outcolor.setGreen(colorpart); - else if (hexpattern[pos] == 'B') - outcolor.setBlue(colorpart); - else if (hexpattern[pos] == 'A') - outcolor.setAlpha(colorpart); - } - - color = outcolor; - return true; - -fail: - if (!quiet) + if (!success && !quiet) errorstream << "Invalid color: \"" << value << "\"" << std::endl; - return false; + + return success; +} + +static bool parseHexColorString(const std::string &value, video::SColor &color) +{ + unsigned char components[] = { 0x00, 0x00, 0x00, 0xff }; // R,G,B,A + + if (value[0] != '#') + return false; + + size_t len = value.size(); + bool short_form; + + if (len == 9 || len == 7) // #RRGGBBAA or #RRGGBB + short_form = false; + else if (len == 5 || len == 4) // #RGBA or #RGB + short_form = true; + else + return false; + + bool success = true; + + for (size_t pos = 1, cc = 0; pos < len; pos++, cc++) { + assert(cc < sizeof components / sizeof components[0]); + if (short_form) { + unsigned char d; + if (!hex_digit_decode(value[pos], d)) { + success = false; + break; + } + components[cc] = (d & 0xf) << 4 | (d & 0xf); + } else { + unsigned char d1, d2; + if (!hex_digit_decode(value[pos], d1) || + !hex_digit_decode(value[pos+1], d2)) { + success = false; + break; + } + components[cc] = (d1 & 0xf) << 4 | (d2 & 0xf); + pos++; // skip the second digit -- it's already used + } + } + + if (success) { + color.setRed(components[0]); + color.setGreen(components[1]); + color.setBlue(components[2]); + color.setAlpha(components[3]); + } + + return success; +} + +struct ColorContainer { + ColorContainer(); + std::map colors; +}; + +ColorContainer::ColorContainer() +{ + colors["aliceblue"] = 0xf0f8ff; + colors["antiquewhite"] = 0xfaebd7; + colors["aqua"] = 0x00ffff; + colors["aquamarine"] = 0x7fffd4; + colors["azure"] = 0xf0ffff; + colors["beige"] = 0xf5f5dc; + colors["bisque"] = 0xffe4c4; + colors["black"] = 00000000; + colors["blanchedalmond"] = 0xffebcd; + colors["blue"] = 0x0000ff; + colors["blueviolet"] = 0x8a2be2; + colors["brown"] = 0xa52a2a; + colors["burlywood"] = 0xdeb887; + colors["cadetblue"] = 0x5f9ea0; + colors["chartreuse"] = 0x7fff00; + colors["chocolate"] = 0xd2691e; + colors["coral"] = 0xff7f50; + colors["cornflowerblue"] = 0x6495ed; + colors["cornsilk"] = 0xfff8dc; + colors["crimson"] = 0xdc143c; + colors["cyan"] = 0x00ffff; + colors["darkblue"] = 0x00008b; + colors["darkcyan"] = 0x008b8b; + colors["darkgoldenrod"] = 0xb8860b; + colors["darkgray"] = 0xa9a9a9; + colors["darkgreen"] = 0x006400; + colors["darkkhaki"] = 0xbdb76b; + colors["darkmagenta"] = 0x8b008b; + colors["darkolivegreen"] = 0x556b2f; + colors["darkorange"] = 0xff8c00; + colors["darkorchid"] = 0x9932cc; + colors["darkred"] = 0x8b0000; + colors["darksalmon"] = 0xe9967a; + colors["darkseagreen"] = 0x8fbc8f; + colors["darkslateblue"] = 0x483d8b; + colors["darkslategray"] = 0x2f4f4f; + colors["darkturquoise"] = 0x00ced1; + colors["darkviolet"] = 0x9400d3; + colors["deeppink"] = 0xff1493; + colors["deepskyblue"] = 0x00bfff; + colors["dimgray"] = 0x696969; + colors["dodgerblue"] = 0x1e90ff; + colors["firebrick"] = 0xb22222; + colors["floralwhite"] = 0xfffaf0; + colors["forestgreen"] = 0x228b22; + colors["fuchsia"] = 0xff00ff; + colors["gainsboro"] = 0xdcdcdc; + colors["ghostwhite"] = 0xf8f8ff; + colors["gold"] = 0xffd700; + colors["goldenrod"] = 0xdaa520; + colors["gray"] = 0x808080; + colors["green"] = 0x008000; + colors["greenyellow"] = 0xadff2f; + colors["honeydew"] = 0xf0fff0; + colors["hotpink"] = 0xff69b4; + colors["indianred "] = 0xcd5c5c; + colors["indigo "] = 0x4b0082; + colors["ivory"] = 0xfffff0; + colors["khaki"] = 0xf0e68c; + colors["lavender"] = 0xe6e6fa; + colors["lavenderblush"] = 0xfff0f5; + colors["lawngreen"] = 0x7cfc00; + colors["lemonchiffon"] = 0xfffacd; + colors["lightblue"] = 0xadd8e6; + colors["lightcoral"] = 0xf08080; + colors["lightcyan"] = 0xe0ffff; + colors["lightgoldenrodyellow"] = 0xfafad2; + colors["lightgray"] = 0xd3d3d3; + colors["lightgreen"] = 0x90ee90; + colors["lightpink"] = 0xffb6c1; + colors["lightsalmon"] = 0xffa07a; + colors["lightseagreen"] = 0x20b2aa; + colors["lightskyblue"] = 0x87cefa; + colors["lightslategray"] = 0x778899; + colors["lightsteelblue"] = 0xb0c4de; + colors["lightyellow"] = 0xffffe0; + colors["lime"] = 0x00ff00; + colors["limegreen"] = 0x32cd32; + colors["linen"] = 0xfaf0e6; + colors["magenta"] = 0xff00ff; + colors["maroon"] = 0x800000; + colors["mediumaquamarine"] = 0x66cdaa; + colors["mediumblue"] = 0x0000cd; + colors["mediumorchid"] = 0xba55d3; + colors["mediumpurple"] = 0x9370db; + colors["mediumseagreen"] = 0x3cb371; + colors["mediumslateblue"] = 0x7b68ee; + colors["mediumspringgreen"] = 0x00fa9a; + colors["mediumturquoise"] = 0x48d1cc; + colors["mediumvioletred"] = 0xc71585; + colors["midnightblue"] = 0x191970; + colors["mintcream"] = 0xf5fffa; + colors["mistyrose"] = 0xffe4e1; + colors["moccasin"] = 0xffe4b5; + colors["navajowhite"] = 0xffdead; + colors["navy"] = 0x000080; + colors["oldlace"] = 0xfdf5e6; + colors["olive"] = 0x808000; + colors["olivedrab"] = 0x6b8e23; + colors["orange"] = 0xffa500; + colors["orangered"] = 0xff4500; + colors["orchid"] = 0xda70d6; + colors["palegoldenrod"] = 0xeee8aa; + colors["palegreen"] = 0x98fb98; + colors["paleturquoise"] = 0xafeeee; + colors["palevioletred"] = 0xdb7093; + colors["papayawhip"] = 0xffefd5; + colors["peachpuff"] = 0xffdab9; + colors["peru"] = 0xcd853f; + colors["pink"] = 0xffc0cb; + colors["plum"] = 0xdda0dd; + colors["powderblue"] = 0xb0e0e6; + colors["purple"] = 0x800080; + colors["red"] = 0xff0000; + colors["rosybrown"] = 0xbc8f8f; + colors["royalblue"] = 0x4169e1; + colors["saddlebrown"] = 0x8b4513; + colors["salmon"] = 0xfa8072; + colors["sandybrown"] = 0xf4a460; + colors["seagreen"] = 0x2e8b57; + colors["seashell"] = 0xfff5ee; + colors["sienna"] = 0xa0522d; + colors["silver"] = 0xc0c0c0; + colors["skyblue"] = 0x87ceeb; + colors["slateblue"] = 0x6a5acd; + colors["slategray"] = 0x708090; + colors["snow"] = 0xfffafa; + colors["springgreen"] = 0x00ff7f; + colors["steelblue"] = 0x4682b4; + colors["tan"] = 0xd2b48c; + colors["teal"] = 0x008080; + colors["thistle"] = 0xd8bfd8; + colors["tomato"] = 0xff6347; + colors["turquoise"] = 0x40e0d0; + colors["violet"] = 0xee82ee; + colors["wheat"] = 0xf5deb3; + colors["white"] = 0xffffff; + colors["whitesmoke"] = 0xf5f5f5; + colors["yellow"] = 0xffff00; + colors["yellowgreen"] = 0x9acd32; + +} + +static const ColorContainer named_colors; + +static bool parseNamedColorString(const std::string &value, video::SColor &color) +{ + std::string color_name; + std::string alpha_string; + + /* If the string has a # in it, assume this is the start of a specified + * alpha value (if it isn't the string is invalid and the error will be + * caught later on, either because the color name won't be found or the + * alpha value will fail conversion) + */ + size_t alpha_pos = value.find('#'); + if (alpha_pos != std::string::npos) { + color_name = value.substr(0, alpha_pos); + alpha_string = value.substr(alpha_pos + 1); + } else { + color_name = value; + } + + color_name = lowercase(value); + + std::map::const_iterator it; + it = named_colors.colors.find(color_name); + if (it == named_colors.colors.end()) + return false; + + u32 color_temp = it->second; + + /* An empty string for alpha is ok (none of the color table entries + * have an alpha value either). Color strings without an alpha specified + * are interpreted as fully opaque + * + * For named colors the supplied alpha string (representing a hex value) + * must be exactly two digits. For example: colorname#08 + */ + if (!alpha_string.empty()) { + if (alpha_string.length() != 2) + return false; + + unsigned char d1, d2; + if (!hex_digit_decode(alpha_string.at(0), d1) + || !hex_digit_decode(alpha_string.at(1), d2)) + return false; + color_temp |= ((d1 & 0xf) << 4 | (d2 & 0xf)) << 24; + } else { + color_temp |= 0xff << 24; // Fully opaque + } + + color = video::SColor(color_temp); + + return true; } diff --git a/src/util/string.h b/src/util/string.h index e46fbf4e9..9f7b6673d 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -126,13 +126,8 @@ inline std::vector str_split(const std::string &str, char delimiter inline std::string lowercase(const std::string &s) { std::string s2; - for(size_t i=0; i= 'A' && c <= 'Z') - c -= 'A' - 'a'; - s2 += c; - } + for(size_t i = 0; i < s.size(); i++) + s2[i] = tolower(s.at(i)); return s2; }