diff --git a/Color.cpp b/Color.cpp index d5dd238..778626b 100644 --- a/Color.cpp +++ b/Color.cpp @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include #include "Color.h" @@ -47,53 +50,132 @@ ColorTable colorTable; // -1: allow alpha but ignore - set to to 255 (0xff) Color::Color(std::string color, int alpha) { - if (color[0] != '#') { - int l = color.length(); - for (int i = 0; i < l; i++) - color[i] = tolower(color[i]); - if (colorTable.count(color) > 0) { - *this = colorTable[color]; - return; + size_t pos = color.find_first_of("+-"); + std::string basecolor = color.substr(0, pos); + std::string colormod; + if (pos != std::string::npos) + colormod = color.substr(pos); + do { + if (basecolor[0] != '#') { + int l = basecolor.length(); + for (int i = 0; i < l; i++) + basecolor[i] = tolower(basecolor[i]); + if (colorTable.count(basecolor) > 0) { + *this = colorTable[basecolor]; + break; + } + throw std::runtime_error(std::string("Symbolic color '") + color + "' not known, or color does not begin with #"); } - throw std::runtime_error(std::string("Symbolic color '") + color + "' not known, or color does not begin with #"); - } - if (std::string::npos != color.find_first_not_of("0123456789abcdefABCDEF",1)) { - throw std::runtime_error("Color value has invalid digits (expected: [0-9a-zA-Z])"); - } - if (alpha) { - if (color.length() != 4 && color.length() != 5 && color.length() != 7 && color.length() != 9) - throw std::runtime_error("Color not in the expected format (#xxx, #xxxx, #xxxxxx or #xxxxxxxx)"); - } - else { - if (color.length() == 5 || color.length() == 9) - throw std::runtime_error("Color not in the expected format (#xxx or #xxxxxx) - alpha not allowed"); - if (color.length() != 4 && color.length() != 7) - throw std::runtime_error("Color not in the expected format (#xxx or #xxxxxx)"); - } - unsigned col = strtoul(color.c_str() + 1, NULL, 16); - if (color.length() < 6) { - if (color.length() == 5) - a = (col >> 12) & 0x0f; - else - a = 0x0f; - a |= a << 4; - r = (col >> 8) & 0x0f; - r |= r << 4; - g = (col >> 4) & 0x0f; - g |= g << 4; - b = (col >> 0) & 0x0f; - b |= b << 4; - } - else { - if (color.length() == 9) - a = (col >> 24) & 0xff; - else + if (std::string::npos != basecolor.find_first_not_of("0123456789abcdefABCDEF",1)) { + throw std::runtime_error("Color value has invalid digits (expected: [0-9a-zA-Z])"); + } + if (alpha) { + if (basecolor.length() != 4 && basecolor.length() != 5 && basecolor.length() != 7 && basecolor.length() != 9) + throw std::runtime_error(std::string("Color '") + color + "' not in the expected format (#xxx, #xxxx, #xxxxxx or #xxxxxxxx)"); + } + else { + if (basecolor.length() == 5 || basecolor.length() == 9) + throw std::runtime_error(std::string("Color '") + color + "' not in the expected format (#xxx or #xxxxxx) - alpha not allowed"); + if (basecolor.length() != 4 && basecolor.length() != 7) + throw std::runtime_error(std::string("Color '") + color + "' not in the expected format (#xxx or #xxxxxx)"); + } + unsigned col = strtoul(basecolor.c_str() + 1, NULL, 16); + if (basecolor.length() < 6) { + if (basecolor.length() == 5) + a = (col >> 12) & 0x0f; + else + a = 0x0f; + a |= a << 4; + r = (col >> 8) & 0x0f; + r |= r << 4; + g = (col >> 4) & 0x0f; + g |= g << 4; + b = (col >> 0) & 0x0f; + b |= b << 4; + } + else { + if (basecolor.length() == 9) + a = (col >> 24) & 0xff; + else + a = 0xff; + r = (col >> 16) & 0xff; + g = (col >> 8) & 0xff; + b = (col >> 0) & 0xff; + } + if (alpha <= 0) a = 0xff; - r = (col >> 16) & 0xff; - g = (col >> 8) & 0xff; - b = (col >> 0) & 0xff; + } while(false); + if (colormod.length() == 0) + return; + std::istringstream iss(colormod); + iss >> std::ws; + while (!iss.eof()) { + double w; + char operation; + char ccode='w'; + iss >> operation; + iss >> ccode; + iss >> w >> std::ws; + if (w > 1) + w = w / 255; + if (!strchr("+-",operation) || !strchr("wkrgbcmy", ccode) || w < 0 || w > 1 || iss.fail()) + throw std::runtime_error(std::string("Invalid color modification(s) (") + color + "); expected one or more of [+-][wkrgbcmy] (n = 0..1)"); + double v; + if (operation == '+' || operation == '-') { + // Mix in a color + int mixr = r; + int mixg = g; + int mixb = b; + if (operation == '+') { + if (ccode == 'w') + mixr = mixg = mixb = 255; + else if (operation == 'k') + mixr = mixg = mixb = 0; + else if (ccode == 'r') + mixr = 255; + else if (ccode == 'g') + mixg = 255; + else if (ccode == 'b') + mixb = 255; + else if (ccode == 'c') + mixg = mixb = 255; + else if (ccode == 'm') + mixr = mixb = 255; + else if (ccode == 'y') + mixr = mixg = 255; + } + else { + if (ccode == 'w') + mixr = mixg = mixb = 0; + else if (operation == 'k') + mixr = mixg = mixb = 255; + else if (ccode == 'r') + mixr = 0; + else if (ccode == 'g') + mixg = 0; + else if (ccode == 'b') + mixb = 0; + else if (ccode == 'c') + mixg = mixb = 0; + else if (ccode == 'm') + mixr = mixb = 0; + else if (ccode == 'y') + mixr = mixg = 0; + } + + v = r * (1 - w) + mixr * w; + if (v < 0) v = 0; + else if (v > 255) v = 255; + r = int(v + 0.5); + v = g * (1 - w) + mixg * w; + if (v < 0) v = 0; + else if (v > 255) v = 255; + g = int(v + 0.5); + v = b * (1 - w) + mixb * w; + if (v < 0) v = 0; + else if (v > 255) v = 255; + b = int(v + 0.5); + } } - if (alpha <= 0) - a = 0xff; } diff --git a/mapper.cpp b/mapper.cpp index 2d290b2..dd9f244 100644 --- a/mapper.cpp +++ b/mapper.cpp @@ -113,6 +113,8 @@ void usage() "\t\tyellow, magenta, fuchsia, cyan, aqua,\n" "\t\torange, chartreuse, pink, violet, springgreen, azure\n" "\t\tbrown (= 50% orange)\n" + "\tAs well as [+-][wkrgbcmy], where n = 0.0..1.0 (or 1.00001 .. 255)\n" + "\t\t[+-][wkrgbcmy] mixes in or out white, black, red, green, blue, cyan, magenta, yellow\n" "Geometry formats:\n" "\tx[+|-+|-] (dimensions and corner)\n" "\t,++ (corner and dimensions)\n"