diff --git a/src/color.cc b/src/color.cc new file mode 100644 index 0000000..7865c6c --- /dev/null +++ b/src/color.cc @@ -0,0 +1,401 @@ + +// +// color.cc +// +// Copyright (c) 2010 LearnBoost +// + +#include "color.h" + +/* + * Consume whitespace. + */ + +#define WHITESPACE \ + while (' ' == *str) ++str; + +/* + * Parse color channel value + */ + +#define CHANNEL(NAME) \ + if (*str >= '0' && *str <= '9') { \ + do { \ + NAME *= 10; \ + NAME += *str++ - '0'; \ + } while (*str >= '0' && *str <= '9'); \ + } else { \ + return -1; \ + } \ + while (' ' == *str || ',' == *str) str++; + +/* + * Named colors. + */ + +static struct named_color { + const char *name; + uint32_t val; +} named_colors[] = { + {"transparent", 0xffffff00} + , {"aliceblue", 0xf0f8ffff} + , {"antiquewhite", 0xfaebd7ff} + , {"aqua", 0x00ffffff} + , {"aquamarine", 0x7fffd4ff} + , {"azure", 0xf0ffffff} + , {"beige", 0xf5f5dcff} + , {"bisque", 0xffe4c4ff} + , {"black", 0x000000ff} + , {"blanchedalmond", 0xffebcdff} + , {"blue", 0x0000ffff} + , {"blueviolet", 0x8a2be2ff} + , {"brown", 0xa52a2aff} + , {"burlywood", 0xdeb887ff} + , {"cadetblue", 0x5f9ea0ff} + , {"chartreuse", 0x7fff00ff} + , {"chocolate", 0xd2691eff} + , {"coral", 0xff7f50ff} + , {"cornflowerblue", 0x6495edff} + , {"cornsilk", 0xfff8dcff} + , {"crimson", 0xdc143cff} + , {"cyan", 0x00ffffff} + , {"darkblue", 0x00008bff} + , {"darkcyan", 0x008b8bff} + , {"darkgoldenrod", 0xb8860bff} + , {"darkgray", 0xa9a9a9ff} + , {"darkgreen", 0x006400ff} + , {"darkkhaki", 0xbdb76bff} + , {"darkmagenta", 0x8b008bff} + , {"darkolivegreen", 0x556b2fff} + , {"darkorange", 0xff8c00ff} + , {"darkorchid", 0x9932ccff} + , {"darkred", 0x8b0000ff} + , {"darksalmon", 0xe9967aff} + , {"darkseagreen", 0x8fbc8fff} + , {"darkslateblue", 0x483d8bff} + , {"darkslategray", 0x2f4f4fff} + , {"darkturquoise", 0x00ced1ff} + , {"darkviolet", 0x9400d3ff} + , {"deeppink", 0xff1493ff} + , {"deepskyblue", 0x00bfffff} + , {"dimgray", 0x696969ff} + , {"dodgerblue", 0x1e90ffff} + , {"feldspar", 0xd19275ff} + , {"firebrick", 0xb22222ff} + , {"floralwhite", 0xfffaf0ff} + , {"forestgreen", 0x228b22ff} + , {"fuchsia", 0xff00ffff} + , {"gainsboro", 0xdcdcdcff} + , {"ghostwhite", 0xf8f8ffff} + , {"gold", 0xffd700ff} + , {"goldenrod", 0xdaa520ff} + , {"gray", 0x808080ff} + , {"green", 0x008000ff} + , {"greenyellow", 0xadff2fff} + , {"honeydew", 0xf0fff0ff} + , {"hotpink", 0xff69b4ff} + , {"indianred ", 0xcd5c5cff} + , {"indigo ", 0x4b0082ff} + , {"ivory", 0xfffff0ff} + , {"khaki", 0xf0e68cff} + , {"lavender", 0xe6e6faff} + , {"lavenderblush", 0xfff0f5ff} + , {"lawngreen", 0x7cfc00ff} + , {"lemonchiffon", 0xfffacdff} + , {"lightblue", 0xadd8e6ff} + , {"lightcoral", 0xf08080ff} + , {"lightcyan", 0xe0ffffff} + , {"lightgoldenrodyellow", 0xfafa} + , {"lightgrey", 0xd3d3d3ff} + , {"lightgreen", 0x90ee90ff} + , {"lightpink", 0xffb6c1ff} + , {"lightsalmon", 0xffa07aff} + , {"lightseagreen", 0x20b2aaff} + , {"lightskyblue", 0x87cefaff} + , {"lightslateblue", 0x8470ffff} + , {"lightslategray", 0x778899ff} + , {"lightsteelblue", 0xb0c4deff} + , {"lightyellow", 0xffffe0ff} + , {"lime", 0x00ff00ff} + , {"limegreen", 0x32cd32ff} + , {"linen", 0xfaf0e6ff} + , {"magenta", 0xff00ffff} + , {"maroon", 0x800000ff} + , {"mediumaquamarine", 0x66cdaaff} + , {"mediumblue", 0x0000cdff} + , {"mediumorchid", 0xba55d3ff} + , {"mediumpurple", 0x9370d8ff} + , {"mediumseagreen", 0x3cb371ff} + , {"mediumslateblue", 0x7b68eeff} + , {"mediumspringgreen", 0x00fa9af} + , {"mediumturquoise", 0x48d1ccff} + , {"mediumvioletred", 0xc71585ff} + , {"midnightblue", 0x191970ff} + , {"mintcream", 0xf5fffaff} + , {"mistyrose", 0xffe4e1ff} + , {"moccasin", 0xffe4b5ff} + , {"navajowhite", 0xffdeadff} + , {"navy", 0x000080ff} + , {"oldlace", 0xfdf5e6ff} + , {"olive", 0x808000ff} + , {"olivedrab", 0x6b8e23ff} + , {"orange", 0xffa500ff} + , {"orangered", 0xff4500ff} + , {"orchid", 0xda70d6ff} + , {"palegoldenrod", 0xeee8aaff} + , {"palegreen", 0x98fb98ff} + , {"paleturquoise", 0xafeeeeff} + , {"palevioletred", 0xd87093ff} + , {"papayawhip", 0xffefd5ff} + , {"peachpuff", 0xffdab9ff} + , {"peru", 0xcd853fff} + , {"pink", 0xffc0cbff} + , {"plum", 0xdda0ddff} + , {"powderblue", 0xb0e0e6ff} + , {"purple", 0x800080ff} + , {"red", 0xff0000ff} + , {"rosybrown", 0xbc8f8fff} + , {"royalblue", 0x4169e1ff} + , {"saddlebrown", 0x8b4513ff} + , {"salmon", 0xfa8072ff} + , {"sandybrown", 0xf4a460ff} + , {"seagreen", 0x2e8b57ff} + , {"seashell", 0xfff5eeff} + , {"sienna", 0xa0522dff} + , {"silver", 0xc0c0c0ff} + , {"skyblue", 0x87ceebff} + , {"slateblue", 0x6a5acdff} + , {"slategray", 0x708090ff} + , {"snow", 0xfffafaff} + , {"springgreen", 0x00ff7fff} + , {"steelblue", 0x4682b4ff} + , {"tan", 0xd2b48cff} + , {"teal", 0x008080ff} + , {"thistle", 0xd8bfd8ff} + , {"tomato", 0xff6347ff} + , {"turquoise", 0x40e0d0ff} + , {"violet", 0xee82eeff} + , {"violetred", 0xd02090ff} + , {"wheat", 0xf5deb3ff} + , {"white", 0xffffffff} + , {"whitesmoke", 0xf5f5f5ff} + , {"yellow", 0xffff00ff} + , {"yellowgreen", 0x9acd32ff} + , {NULL, NULL} +}; + +/* + * Hex digit int val. + */ + +static int +h(char c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return c - '0'; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return (c - 'a') + 10; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return (c - 'A') + 10; + } + return 0; +} + +/* + * Return rgba_t from rgba. + */ + +rgba_t +rgba_create(uint32_t rgba) { + rgba_t color; + color.r = rgba >> 24; + color.g = rgba >> 16; + color.b = rgba >> 8; + color.a = rgba; + return color; +} + +/* + * Return rgba from (r,g,b,a). + */ + +inline int32_t +rgba_from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return + r << 24 + | g << 16 + | b << 8 + | a; +} + +/* + * Return rgba from (r,g,b). + */ + +int32_t +rgba_from_rgb(uint8_t r, uint8_t g, uint8_t b) { + return rgba_from_rgba(r, g, b, 0); +} + +/* + * Return rgb from "#RRGGBB". + */ + +int32_t +rgba_from_hex6_string(const char *str) { + return rgba_from_rgb( + (h(str[0]) << 4) + h(str[1]) + , (h(str[2]) << 4) + h(str[3]) + , (h(str[4]) << 4) + h(str[5]) + ); +} + +/* + * Return rgb from "#RGB" + */ + +int32_t +rgba_from_hex3_string(const char *str) { + return rgba_from_rgb( + (h(str[0]) << 4) + h(str[0]) + , (h(str[1]) << 4) + h(str[1]) + , (h(str[2]) << 4) + h(str[2]) + ); +} + +/* + * Return rgb from "rgb()" + */ + +int32_t +rgba_from_rgb_string(const char *str) { + if (str == strnstr(str, "rgb(", 4)) { + str += 4; + WHITESPACE; + uint8_t r = 0, g = 0, b = 0; + CHANNEL(r); + CHANNEL(g); + CHANNEL(b); + return rgba_from_rgb(r, g, b); + } + return -1; +} + +/* + * Return rgb from "rgba()" + */ + +int32_t +rgba_from_rgba_string(const char *str) { + if (str == strnstr(str, "rgba(", 5)) { + str += 5; + WHITESPACE; + uint8_t r = 0, g = 0, b = 0; + float a = 0; + CHANNEL(r); + CHANNEL(g); + CHANNEL(b); + // TODO: less strict + if ('1' == *str) { + a = 1; + } else { + if ('0' == *str) a = 0, ++str; + if ('.' == *str) { + ++str; + float n = .1; + while (*str >= '0' && *str <= '9') { + a += (*str++ - '0') * n; + n *= .1; + } + } + } + return rgba_from_rgba(r, g, b, a * 255); + } + return -1; +} + +/* + * Return rgb from: + * + * - "#RGB" + * - "#RRGGBB" + * + */ + +int32_t +rgba_from_hex_string(const char *str) { + size_t len = strlen(str); + if (6 == len) return rgba_from_hex6_string(str); + if (3 == len) return rgba_from_hex3_string(str); + return -1; +} + +/* + * Return named color value. + */ + +int32_t +rgba_from_name_string(const char *str) { + int i = 0; + struct named_color color; + while ((color = named_colors[i++]).name) { + if (*str == *color.name && 0 == strcmp(str, color.name)) + return color.val; + } + return -1; +} + +/* + * Return rgb from: + * + * - #RGB + * - #RRGGBB + * - rgb(r,g,b) + * - rgba(r,g,b,a) + * - name + * + */ + +int32_t +rgba_from_string(const char *str) { + if ('#' == str[0]) + return rgba_from_hex_string(++str); + if (str == strnstr(str, "rgba", 4)) + return rgba_from_rgba_string(str); + if (str == strnstr(str, "rgb", 3)) + return rgba_from_rgb_string(str); + return rgba_from_name_string(str); +} + +/* + * Inspect the given rgba color. + */ + +void +rgba_inspect(int32_t rgba) { + printf("rgba(%d,%d,%d,%d)\n" + , rgba >> 24 & 0xFF + , rgba >> 16 & 0xFF + , rgba >> 8 & 0xFF + , rgba & 0xFF + ); +} diff --git a/src/color.h b/src/color.h index 03dbe7c..60eaf54 100644 --- a/src/color.h +++ b/src/color.h @@ -8,6 +8,7 @@ #ifndef __COLOR_PARSER_H__ #define __COLOR_PARSER_H__ +#include #include #include @@ -20,396 +21,40 @@ typedef struct { } rgba_t; /* - * Consume whitespace. - */ - -#define WHITESPACE \ - while (' ' == *str) ++str; - -/* - * Parse color channel value - */ - -#define CHANNEL(NAME) \ - if (*str >= '0' && *str <= '9') { \ - do { \ - NAME *= 10; \ - NAME += *str++ - '0'; \ - } while (*str >= '0' && *str <= '9'); \ - } else { \ - return -1; \ - } \ - while (' ' == *str || ',' == *str) str++; - -/* - * Named colors. - */ - -static struct named_color { - const char *name; - uint32_t val; -} named_colors[] = { - {"transparent", 0xffffff00} - , {"aliceblue", 0xf0f8ffff} - , {"antiquewhite", 0xfaebd7ff} - , {"aqua", 0x00ffffff} - , {"aquamarine", 0x7fffd4ff} - , {"azure", 0xf0ffffff} - , {"beige", 0xf5f5dcff} - , {"bisque", 0xffe4c4ff} - , {"black", 0x000000ff} - , {"blanchedalmond", 0xffebcdff} - , {"blue", 0x0000ffff} - , {"blueviolet", 0x8a2be2ff} - , {"brown", 0xa52a2aff} - , {"burlywood", 0xdeb887ff} - , {"cadetblue", 0x5f9ea0ff} - , {"chartreuse", 0x7fff00ff} - , {"chocolate", 0xd2691eff} - , {"coral", 0xff7f50ff} - , {"cornflowerblue", 0x6495edff} - , {"cornsilk", 0xfff8dcff} - , {"crimson", 0xdc143cff} - , {"cyan", 0x00ffffff} - , {"darkblue", 0x00008bff} - , {"darkcyan", 0x008b8bff} - , {"darkgoldenrod", 0xb8860bff} - , {"darkgray", 0xa9a9a9ff} - , {"darkgreen", 0x006400ff} - , {"darkkhaki", 0xbdb76bff} - , {"darkmagenta", 0x8b008bff} - , {"darkolivegreen", 0x556b2fff} - , {"darkorange", 0xff8c00ff} - , {"darkorchid", 0x9932ccff} - , {"darkred", 0x8b0000ff} - , {"darksalmon", 0xe9967aff} - , {"darkseagreen", 0x8fbc8fff} - , {"darkslateblue", 0x483d8bff} - , {"darkslategray", 0x2f4f4fff} - , {"darkturquoise", 0x00ced1ff} - , {"darkviolet", 0x9400d3ff} - , {"deeppink", 0xff1493ff} - , {"deepskyblue", 0x00bfffff} - , {"dimgray", 0x696969ff} - , {"dodgerblue", 0x1e90ffff} - , {"feldspar", 0xd19275ff} - , {"firebrick", 0xb22222ff} - , {"floralwhite", 0xfffaf0ff} - , {"forestgreen", 0x228b22ff} - , {"fuchsia", 0xff00ffff} - , {"gainsboro", 0xdcdcdcff} - , {"ghostwhite", 0xf8f8ffff} - , {"gold", 0xffd700ff} - , {"goldenrod", 0xdaa520ff} - , {"gray", 0x808080ff} - , {"green", 0x008000ff} - , {"greenyellow", 0xadff2fff} - , {"honeydew", 0xf0fff0ff} - , {"hotpink", 0xff69b4ff} - , {"indianred ", 0xcd5c5cff} - , {"indigo ", 0x4b0082ff} - , {"ivory", 0xfffff0ff} - , {"khaki", 0xf0e68cff} - , {"lavender", 0xe6e6faff} - , {"lavenderblush", 0xfff0f5ff} - , {"lawngreen", 0x7cfc00ff} - , {"lemonchiffon", 0xfffacdff} - , {"lightblue", 0xadd8e6ff} - , {"lightcoral", 0xf08080ff} - , {"lightcyan", 0xe0ffffff} - , {"lightgoldenrodyellow", 0xfafa} - , {"lightgrey", 0xd3d3d3ff} - , {"lightgreen", 0x90ee90ff} - , {"lightpink", 0xffb6c1ff} - , {"lightsalmon", 0xffa07aff} - , {"lightseagreen", 0x20b2aaff} - , {"lightskyblue", 0x87cefaff} - , {"lightslateblue", 0x8470ffff} - , {"lightslategray", 0x778899ff} - , {"lightsteelblue", 0xb0c4deff} - , {"lightyellow", 0xffffe0ff} - , {"lime", 0x00ff00ff} - , {"limegreen", 0x32cd32ff} - , {"linen", 0xfaf0e6ff} - , {"magenta", 0xff00ffff} - , {"maroon", 0x800000ff} - , {"mediumaquamarine", 0x66cdaaff} - , {"mediumblue", 0x0000cdff} - , {"mediumorchid", 0xba55d3ff} - , {"mediumpurple", 0x9370d8ff} - , {"mediumseagreen", 0x3cb371ff} - , {"mediumslateblue", 0x7b68eeff} - , {"mediumspringgreen", 0x00fa9af} - , {"mediumturquoise", 0x48d1ccff} - , {"mediumvioletred", 0xc71585ff} - , {"midnightblue", 0x191970ff} - , {"mintcream", 0xf5fffaff} - , {"mistyrose", 0xffe4e1ff} - , {"moccasin", 0xffe4b5ff} - , {"navajowhite", 0xffdeadff} - , {"navy", 0x000080ff} - , {"oldlace", 0xfdf5e6ff} - , {"olive", 0x808000ff} - , {"olivedrab", 0x6b8e23ff} - , {"orange", 0xffa500ff} - , {"orangered", 0xff4500ff} - , {"orchid", 0xda70d6ff} - , {"palegoldenrod", 0xeee8aaff} - , {"palegreen", 0x98fb98ff} - , {"paleturquoise", 0xafeeeeff} - , {"palevioletred", 0xd87093ff} - , {"papayawhip", 0xffefd5ff} - , {"peachpuff", 0xffdab9ff} - , {"peru", 0xcd853fff} - , {"pink", 0xffc0cbff} - , {"plum", 0xdda0ddff} - , {"powderblue", 0xb0e0e6ff} - , {"purple", 0x800080ff} - , {"red", 0xff0000ff} - , {"rosybrown", 0xbc8f8fff} - , {"royalblue", 0x4169e1ff} - , {"saddlebrown", 0x8b4513ff} - , {"salmon", 0xfa8072ff} - , {"sandybrown", 0xf4a460ff} - , {"seagreen", 0x2e8b57ff} - , {"seashell", 0xfff5eeff} - , {"sienna", 0xa0522dff} - , {"silver", 0xc0c0c0ff} - , {"skyblue", 0x87ceebff} - , {"slateblue", 0x6a5acdff} - , {"slategray", 0x708090ff} - , {"snow", 0xfffafaff} - , {"springgreen", 0x00ff7fff} - , {"steelblue", 0x4682b4ff} - , {"tan", 0xd2b48cff} - , {"teal", 0x008080ff} - , {"thistle", 0xd8bfd8ff} - , {"tomato", 0xff6347ff} - , {"turquoise", 0x40e0d0ff} - , {"violet", 0xee82eeff} - , {"violetred", 0xd02090ff} - , {"wheat", 0xf5deb3ff} - , {"white", 0xffffffff} - , {"whitesmoke", 0xf5f5f5ff} - , {"yellow", 0xffff00ff} - , {"yellowgreen", 0x9acd32ff} - , {NULL, NULL} -}; - -/* - * Hex digit int val. - */ - -static int -h(char c) { - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return c - '0'; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - return (c - 'a') + 10; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - return (c - 'A') + 10; - } - return 0; -} - -/* - * Return rgba_t from rgba. + * Prototypes. */ rgba_t -rgba_create(uint32_t rgba) { - rgba_t color; - color.r = rgba >> 24; - color.g = rgba >> 16; - color.b = rgba >> 8; - color.a = rgba; - return color; -} - -/* - * Return rgba from (r,g,b,a). - */ +rgba_create(uint32_t rgba); inline int32_t -rgba_from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - return - r << 24 - | g << 16 - | b << 8 - | a; -} - -/* - * Return rgba from (r,g,b). - */ +rgba_from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); int32_t -rgba_from_rgb(uint8_t r, uint8_t g, uint8_t b) { - return rgba_from_rgba(r, g, b, 0); -} - -/* - * Return rgb from "#RRGGBB". - */ +rgba_from_rgb(uint8_t r, uint8_t g, uint8_t b); int32_t -rgba_from_hex6_string(const char *str) { - return rgba_from_rgb( - (h(str[0]) << 4) + h(str[1]) - , (h(str[2]) << 4) + h(str[3]) - , (h(str[4]) << 4) + h(str[5]) - ); -} - -/* - * Return rgb from "#RGB" - */ +rgba_from_hex6_string(const char *str); int32_t -rgba_from_hex3_string(const char *str) { - return rgba_from_rgb( - (h(str[0]) << 4) + h(str[0]) - , (h(str[1]) << 4) + h(str[1]) - , (h(str[2]) << 4) + h(str[2]) - ); -} - -/* - * Return rgb from "rgb()" - */ +rgba_from_hex3_string(const char *str); int32_t -rgba_from_rgb_string(const char *str) { - if (str == strnstr(str, "rgb(", 4)) { - str += 4; - WHITESPACE; - uint8_t r = 0, g = 0, b = 0; - CHANNEL(r); - CHANNEL(g); - CHANNEL(b); - return rgba_from_rgb(r, g, b); - } - return -1; -} - -/* - * Return rgb from "rgba()" - */ +rgba_from_rgb_string(const char *str); int32_t -rgba_from_rgba_string(const char *str) { - if (str == strnstr(str, "rgba(", 5)) { - str += 5; - WHITESPACE; - uint8_t r = 0, g = 0, b = 0; - float a = 0; - CHANNEL(r); - CHANNEL(g); - CHANNEL(b); - // TODO: less strict - if ('1' == *str) { - a = 1; - } else { - if ('0' == *str) a = 0, ++str; - if ('.' == *str) { - ++str; - float n = .1; - while (*str >= '0' && *str <= '9') { - a += (*str++ - '0') * n; - n *= .1; - } - } - } - return rgba_from_rgba(r, g, b, a * 255); - } - return -1; -} - -/* - * Return rgb from: - * - * - "#RGB" - * - "#RRGGBB" - * - */ +rgba_from_rgba_string(const char *str); int32_t -rgba_from_hex_string(const char *str) { - size_t len = strlen(str); - if (6 == len) return rgba_from_hex6_string(str); - if (3 == len) return rgba_from_hex3_string(str); - return -1; -} - -/* - * Return named color value. - */ +rgba_from_hex_string(const char *str); int32_t -rgba_from_name_string(const char *str) { - int i = 0; - struct named_color color; - while ((color = named_colors[i++]).name) { - if (*str == *color.name && 0 == strcmp(str, color.name)) - return color.val; - } - return -1; -} - -/* - * Return rgb from: - * - * - #RGB - * - #RRGGBB - * - rgb(r,g,b) - * - rgba(r,g,b,a) - * - name - * - */ +rgba_from_name_string(const char *str); int32_t -rgba_from_string(const char *str) { - if ('#' == str[0]) - return rgba_from_hex_string(++str); - if (str == strnstr(str, "rgba", 4)) - return rgba_from_rgba_string(str); - if (str == strnstr(str, "rgb", 3)) - return rgba_from_rgb_string(str); - return rgba_from_name_string(str); -} - -/* - * Inspect the given rgba color. - */ +rgba_from_string(const char *str); void -rgba_inspect(int32_t rgba) { - printf("rgba(%d,%d,%d,%d)\n" - , rgba >> 24 & 0xFF - , rgba >> 16 & 0xFF - , rgba >> 8 & 0xFF - , rgba & 0xFF - ); -} +rgba_inspect(int32_t rgba); #endif /* __COLOR_PARSER_H__ */