You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

424 lines
9.2 KiB

14 years ago
//
// color.cc
//
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#include "color.h"
#include <stdlib.h>
14 years ago
/*
* 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 0; \
14 years ago
} \
while (' ' == *str || ',' == *str) str++;
/*
* Named colors.
*/
static struct named_color {
const char *name;
uint32_t val;
} named_colors[] = {
14 years ago
{ "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", 0xfafad2ff }
14 years ago
, { "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", 0x00fa9aff }
14 years ago
, { "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 }
14 years ago
, { NULL, NULL }
14 years ago
};
/*
* 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 = (double) (rgba >> 24) / 255;
color.g = (double) ((rgba & 0x00ff0000) >> 16) / 255;
color.b = (double) ((rgba & 0x0000ff00) >> 8) / 255;
color.a = (double) (rgba & 0xff) / 255;
14 years ago
return color;
}
/*
* Return a string representation of the color.
*/
void
rgba_to_string(rgba_t rgba, char *buf) {
if (1 == rgba.a) {
sprintf(buf, "#%.2x%.2x%.2x"
, (int) (rgba.r * 255)
, (int) (rgba.g * 255)
, (int) (rgba.b * 255));
} else {
sprintf(buf, "rgba(%d, %d, %d, %.2f)"
, (int) (rgba.r * 255)
, (int) (rgba.g * 255)
, (int) (rgba.b * 255)
, rgba.a);
}
}
14 years ago
/*
* Return rgba from (r,g,b,a).
*/
static inline int32_t
14 years ago
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).
*/
static int32_t
14 years ago
rgba_from_rgb(uint8_t r, uint8_t g, uint8_t b) {
return rgba_from_rgba(r, g, b, 255);
14 years ago
}
/*
* Return rgb from "#RRGGBB".
*/
static int32_t
14 years ago
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"
*/
static int32_t
14 years ago
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()"
*/
static int32_t
rgba_from_rgb_string(const char *str, short *ok) {
14 years ago
if (str == strstr(str, "rgb(")) {
14 years ago
str += 4;
WHITESPACE;
uint8_t r = 0, g = 0, b = 0;
CHANNEL(r);
CHANNEL(g);
CHANNEL(b);
return *ok = 1, rgba_from_rgb(r, g, b);
14 years ago
}
return *ok = 0;
14 years ago
}
/*
* Return rgb from "rgba()"
*/
static int32_t
rgba_from_rgba_string(const char *str, short *ok) {
14 years ago
if (str == strstr(str, "rgba(")) {
14 years ago
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 *ok = 1, rgba_from_rgba(r, g, b, a * 255);
14 years ago
}
return *ok = 0;
14 years ago
}
/*
* Return rgb from:
*
* - "#RGB"
* - "#RRGGBB"
*
*/
static int32_t
rgba_from_hex_string(const char *str, short *ok) {
14 years ago
size_t len = strlen(str);
*ok = 1;
14 years ago
if (6 == len) return rgba_from_hex6_string(str);
if (3 == len) return rgba_from_hex3_string(str);
return *ok = 0;
14 years ago
}
/*
* Return named color value.
*/
static int32_t
rgba_from_name_string(const char *str, short *ok) {
14 years ago
int i = 0;
struct named_color color;
while ((color = named_colors[i++]).name) {
if (*str == *color.name && 0 == strcmp(str, color.name))
return *ok = 1, color.val;
14 years ago
}
return *ok = 0;
14 years ago
}
/*
* Return rgb from:
*
* - #RGB
* - #RRGGBB
* - rgb(r,g,b)
* - rgba(r,g,b,a)
* - name
*
*/
int32_t
rgba_from_string(const char *str, short *ok) {
14 years ago
if ('#' == str[0])
return rgba_from_hex_string(++str, ok);
14 years ago
if (str == strstr(str, "rgba"))
return rgba_from_rgba_string(str, ok);
14 years ago
if (str == strstr(str, "rgb"))
return rgba_from_rgb_string(str, ok);
return rgba_from_name_string(str, ok);
14 years ago
}
/*
* 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
);
}