Added names colours and refactored parseColorString()
authorCraig Robbins <kde.psych@gmail.com>
Wed, 29 Oct 2014 06:40:55 +0000 (16:40 +1000)
committerkwolekr <kwolekr@minetest.net>
Wed, 29 Oct 2014 07:10:59 +0000 (03:10 -0400)
doc/lua_api.txt
src/util/string.cpp
src/util/string.h

index ff2143cc841ba07b6fd93075201317bae1e05cae..698efbe8af2d0ad98e7b8485df392ddd55ecc79c 100644 (file)
@@ -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
index 57545aa20a710cc5db4547c30c3fc244ff60ca75..c590e7e577a484654861f2d4f94f8587c9399c8d 100644 (file)
@@ -24,12 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include <sstream>
 #include <iomanip>
+#include <map>
 
 #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);
-
-       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";
-       }
+       bool success;
 
-       if (!hexpattern)
-               goto fail;
+       if (value[0] == '#')
+               success = parseHexColorString(value, color);
+       else
+               success = parseNamedColorString(value, color);
 
-       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;
+       if (!success && !quiet)
+               errorstream << "Invalid color: \"" << value << "\"" << std::endl;
+
+       return success;
+}
+
+static bool parseHexColorString(const std::string &value, video::SColor &color)
+{
+       unsigned char components[] = { 0x00, 0x00, 0x00, 0xff }; // R,G,B,A
 
-               // 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;
+       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 {
-                       // One digit, e.g. hexpattern == "#RGB"
-                       if (!hex_digit_decode(value[pos], c1))
-                               goto fail;
-                       c2 = c1;
+                       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
                }
-               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;
+       if (success) {
+               color.setRed(components[0]);
+               color.setGreen(components[1]);
+               color.setBlue(components[2]);
+               color.setAlpha(components[3]);
+       }
 
-fail:
-       if (!quiet)
-               errorstream << "Invalid color: \"" << value << "\"" << std::endl;
-       return false;
+       return success;
+}
+
+struct ColorContainer {
+       ColorContainer();
+       std::map<const std::string, u32> 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 std::string, unsigned>::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;
 }
index e46fbf4e9b2a48e3682eee3a317927290079cc13..9f7b6673dd0869b1ce6c624aff41cb460f99826b 100644 (file)
@@ -126,13 +126,8 @@ inline std::vector<std::string> 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<s.size(); i++)
-       {
-               char c = s[i];
-               if(c >= '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;
 }