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.
744 lines
16 KiB
744 lines
16 KiB
|
|
//
|
|
// color.cc
|
|
//
|
|
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
|
|
//
|
|
|
|
#include "color.h"
|
|
#include <stdlib.h>
|
|
#include <cmath>
|
|
#include <limits>
|
|
|
|
// Compatibility with Visual Studio versions prior to VS2015
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
/*
|
|
* Parse integer value
|
|
*/
|
|
|
|
template <typename parsed_t>
|
|
static bool
|
|
parse_integer(const char** pStr, parsed_t *pParsed) {
|
|
parsed_t& c = *pParsed;
|
|
const char*& str = *pStr;
|
|
int8_t sign=1;
|
|
|
|
c = 0;
|
|
if (*str == '-') {
|
|
sign=-1;
|
|
++str;
|
|
}
|
|
else if (*str == '+')
|
|
++str;
|
|
|
|
if (*str >= '0' && *str <= '9') {
|
|
do {
|
|
c *= 10;
|
|
c += *str++ - '0';
|
|
} while (*str >= '0' && *str <= '9');
|
|
} else {
|
|
return false;
|
|
}
|
|
if (sign<0)
|
|
c=-c;
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse CSS <number> value
|
|
* Adapted from http://crackprogramming.blogspot.co.il/2012/10/implement-atof.html
|
|
*/
|
|
|
|
template <typename parsed_t>
|
|
static bool
|
|
parse_css_number(const char** pStr, parsed_t *pParsed) {
|
|
parsed_t &parsed = *pParsed;
|
|
const char*& str = *pStr;
|
|
const char* startStr = str;
|
|
if (!str || !*str)
|
|
return false;
|
|
parsed_t integerPart = 0;
|
|
parsed_t fractionPart = 0;
|
|
int divisorForFraction = 1;
|
|
int sign = 1;
|
|
int exponent = 0;
|
|
int digits = 0;
|
|
bool inFraction = false;
|
|
|
|
if (*str == '-') {
|
|
++str;
|
|
sign = -1;
|
|
}
|
|
else if (*str == '+')
|
|
++str;
|
|
while (*str != '\0') {
|
|
if (*str >= '0' && *str <= '9') {
|
|
if (digits>=std::numeric_limits<parsed_t>::digits10) {
|
|
if (!inFraction)
|
|
return false;
|
|
}
|
|
else {
|
|
++digits;
|
|
|
|
if (inFraction) {
|
|
fractionPart = fractionPart*10 + (*str - '0');
|
|
divisorForFraction *= 10;
|
|
}
|
|
else {
|
|
integerPart = integerPart*10 + (*str - '0');
|
|
}
|
|
}
|
|
}
|
|
else if (*str == '.') {
|
|
if (inFraction)
|
|
break;
|
|
else
|
|
inFraction = true;
|
|
}
|
|
else if (*str == 'e') {
|
|
++str;
|
|
if (!parse_integer(&str, &exponent))
|
|
return false;
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
++str;
|
|
}
|
|
if (str != startStr) {
|
|
parsed = sign * (integerPart + fractionPart/divisorForFraction);
|
|
for (;exponent>0;--exponent)
|
|
parsed *= 10;
|
|
for (;exponent<0;++exponent)
|
|
parsed /= 10;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Clip value to the range [minValue, maxValue]
|
|
*/
|
|
|
|
template <typename T>
|
|
static T
|
|
clip(T value, T minValue, T maxValue) {
|
|
if (value > maxValue)
|
|
value = maxValue;
|
|
if (value < minValue)
|
|
value = minValue;
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* Wrap value to the range [0, limit]
|
|
*/
|
|
|
|
template <typename T>
|
|
static T
|
|
wrap_float(T value, T limit) {
|
|
return fmod(fmod(value, limit) + limit, limit);
|
|
}
|
|
|
|
/*
|
|
* Wrap value to the range [0, limit] - currently-unused integer version of wrap_float
|
|
*/
|
|
|
|
// template <typename T>
|
|
// static T wrap_int(T value, T limit) {
|
|
// return (value % limit + limit) % limit;
|
|
// }
|
|
|
|
/*
|
|
* Parse color channel value
|
|
*/
|
|
|
|
static bool
|
|
parse_rgb_channel(const char** pStr, uint8_t *pChannel) {
|
|
int channel;
|
|
if (parse_integer(pStr, &channel)) {
|
|
*pChannel = clip(channel, 0, 255);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Parse a value in degrees
|
|
*/
|
|
|
|
static bool
|
|
parse_degrees(const char** pStr, float *pDegrees) {
|
|
float degrees;
|
|
if (parse_css_number(pStr, °rees)) {
|
|
*pDegrees = wrap_float(degrees, 360.0f);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Parse and clip a percentage value. Returns a float in the range [0, 1].
|
|
*/
|
|
|
|
static bool
|
|
parse_clipped_percentage(const char** pStr, float *pFraction) {
|
|
float percentage;
|
|
bool result = parse_css_number(pStr,&percentage);
|
|
const char*& str = *pStr;
|
|
if (result) {
|
|
if (*str == '%') {
|
|
++str;
|
|
*pFraction = clip(percentage, 0.0f, 100.0f) / 100.0f;
|
|
return result;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Macros to help with parsing inside rgba_from_*_string
|
|
*/
|
|
|
|
#define WHITESPACE \
|
|
while (' ' == *str) ++str;
|
|
|
|
#define WHITESPACE_OR_COMMA \
|
|
while (' ' == *str || ',' == *str) ++str;
|
|
|
|
#define CHANNEL(NAME) \
|
|
if (!parse_rgb_channel(&str, &NAME)) \
|
|
return 0; \
|
|
|
|
#define HUE(NAME) \
|
|
if (!parse_degrees(&str, &NAME)) \
|
|
return 0;
|
|
|
|
#define SATURATION(NAME) \
|
|
if (!parse_clipped_percentage(&str, &NAME)) \
|
|
return 0;
|
|
|
|
#define LIGHTNESS(NAME) SATURATION(NAME)
|
|
|
|
#define ALPHA(NAME) \
|
|
if (*str >= '1' && *str <= '9') { \
|
|
NAME = 1; \
|
|
} else { \
|
|
if ('0' == *str) ++str; \
|
|
if ('.' == *str) { \
|
|
++str; \
|
|
float n = .1f; \
|
|
while (*str >= '0' && *str <= '9') { \
|
|
NAME += (*str++ - '0') * n; \
|
|
n *= .1f; \
|
|
} \
|
|
} \
|
|
} \
|
|
do {} while (0) // require trailing semicolon
|
|
|
|
/*
|
|
* 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 }
|
|
, { "darkgrey", 0xA9A9A9FF }
|
|
, { "darkkhaki", 0xBDB76BFF }
|
|
, { "darkmagenta", 0x8B008BFF }
|
|
, { "darkolivegreen", 0x556B2FFF }
|
|
, { "darkorange", 0xFF8C00FF }
|
|
, { "darkorchid", 0x9932CCFF }
|
|
, { "darkred", 0x8B0000FF }
|
|
, { "darksalmon", 0xE9967AFF }
|
|
, { "darkseagreen", 0x8FBC8FFF }
|
|
, { "darkslateblue", 0x483D8BFF }
|
|
, { "darkslategray", 0x2F4F4FFF }
|
|
, { "darkslategrey", 0x2F4F4FFF }
|
|
, { "darkturquoise", 0x00CED1FF }
|
|
, { "darkviolet", 0x9400D3FF }
|
|
, { "deeppink", 0xFF1493FF }
|
|
, { "deepskyblue", 0x00BFFFFF }
|
|
, { "dimgray", 0x696969FF }
|
|
, { "dimgrey", 0x696969FF }
|
|
, { "dodgerblue", 0x1E90FFFF }
|
|
, { "firebrick", 0xB22222FF }
|
|
, { "floralwhite", 0xFFFAF0FF }
|
|
, { "forestgreen", 0x228B22FF }
|
|
, { "fuchsia", 0xFF00FFFF }
|
|
, { "gainsboro", 0xDCDCDCFF }
|
|
, { "ghostwhite", 0xF8F8FFFF }
|
|
, { "gold", 0xFFD700FF }
|
|
, { "goldenrod", 0xDAA520FF }
|
|
, { "gray", 0x808080FF }
|
|
, { "green", 0x008000FF }
|
|
, { "greenyellow", 0xADFF2FFF }
|
|
, { "grey", 0x808080FF }
|
|
, { "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 }
|
|
, { "lightgray", 0xD3D3D3FF }
|
|
, { "lightgreen", 0x90EE90FF }
|
|
, { "lightgrey", 0xD3D3D3FF }
|
|
, { "lightpink", 0xFFB6C1FF }
|
|
, { "lightsalmon", 0xFFA07AFF }
|
|
, { "lightseagreen", 0x20B2AAFF }
|
|
, { "lightskyblue", 0x87CEFAFF }
|
|
, { "lightslategray", 0x778899FF }
|
|
, { "lightslategrey", 0x778899FF }
|
|
, { "lightsteelblue", 0xB0C4DEFF }
|
|
, { "lightyellow", 0xFFFFE0FF }
|
|
, { "lime", 0x00FF00FF }
|
|
, { "limegreen", 0x32CD32FF }
|
|
, { "linen", 0xFAF0E6FF }
|
|
, { "magenta", 0xFF00FFFF }
|
|
, { "maroon", 0x800000FF }
|
|
, { "mediumaquamarine", 0x66CDAAFF }
|
|
, { "mediumblue", 0x0000CDFF }
|
|
, { "mediumorchid", 0xBA55D3FF }
|
|
, { "mediumpurple", 0x9370DBFF }
|
|
, { "mediumseagreen", 0x3CB371FF }
|
|
, { "mediumslateblue", 0x7B68EEFF }
|
|
, { "mediumspringgreen", 0x00FA9AFF }
|
|
, { "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", 0xDB7093FF }
|
|
, { "papayawhip", 0xFFEFD5FF }
|
|
, { "peachpuff", 0xFFDAB9FF }
|
|
, { "peru", 0xCD853FFF }
|
|
, { "pink", 0xFFC0CBFF }
|
|
, { "plum", 0xDDA0DDFF }
|
|
, { "powderblue", 0xB0E0E6FF }
|
|
, { "purple", 0x800080FF }
|
|
, { "rebeccapurple", 0x663399FF } // Source: CSS Color Level 4 draft
|
|
, { "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 }
|
|
, { "slategrey", 0x708090FF }
|
|
, { "snow", 0xFFFAFAFF }
|
|
, { "springgreen", 0x00FF7FFF }
|
|
, { "steelblue", 0x4682B4FF }
|
|
, { "tan", 0xD2B48CFF }
|
|
, { "teal", 0x008080FF }
|
|
, { "thistle", 0xD8BFD8FF }
|
|
, { "tomato", 0xFF6347FF }
|
|
, { "turquoise", 0x40E0D0FF }
|
|
, { "violet", 0xEE82EEFF }
|
|
, { "wheat", 0xF5DEB3FF }
|
|
, { "white", 0xFFFFFFFF }
|
|
, { "whitesmoke", 0xF5F5F5FF }
|
|
, { "yellow", 0xFFFF00FF }
|
|
, { "yellowgreen", 0x9ACD32FF }
|
|
, { NULL, 0 }
|
|
};
|
|
|
|
/*
|
|
* 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 >> 16 & 0xff) / 255;
|
|
color.b = (double) (rgba >> 8 & 0xff) / 255;
|
|
color.a = (double) (rgba & 0xff) / 255;
|
|
return color;
|
|
}
|
|
|
|
/*
|
|
* Return a string representation of the color.
|
|
*/
|
|
|
|
void
|
|
rgba_to_string(rgba_t rgba, char *buf, size_t len) {
|
|
if (1 == rgba.a) {
|
|
snprintf(buf, len, "#%.2x%.2x%.2x"
|
|
, (int) (rgba.r * 255)
|
|
, (int) (rgba.g * 255)
|
|
, (int) (rgba.b * 255));
|
|
} else {
|
|
snprintf(buf, len, "rgba(%d, %d, %d, %.2f)"
|
|
, (int) (rgba.r * 255)
|
|
, (int) (rgba.g * 255)
|
|
, (int) (rgba.b * 255)
|
|
, rgba.a);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return rgba from (r,g,b,a).
|
|
*/
|
|
|
|
static 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;
|
|
}
|
|
|
|
/*
|
|
* Helper function used in rgba_from_hsla().
|
|
* Based on http://dev.w3.org/csswg/css-color-4/#hsl-to-rgb
|
|
*/
|
|
|
|
static float
|
|
hue_to_rgb(float t1, float t2, float hue) {
|
|
if (hue < 0)
|
|
hue += 6;
|
|
if (hue >= 6)
|
|
hue -= 6;
|
|
|
|
if (hue < 1)
|
|
return (t2 - t1) * hue + t1;
|
|
else if (hue < 3)
|
|
return t2;
|
|
else if (hue < 4)
|
|
return (t2 - t1) * (4 - hue) + t1;
|
|
else
|
|
return t1;
|
|
}
|
|
|
|
/*
|
|
* Return rgba from (h,s,l,a).
|
|
* Expects h values in the range [0, 360), and s, l, a in the range [0, 1].
|
|
* Adapted from http://dev.w3.org/csswg/css-color-4/#hsl-to-rgb
|
|
*/
|
|
|
|
static inline int32_t
|
|
rgba_from_hsla(float h_deg, float s, float l, float a) {
|
|
uint8_t r, g, b;
|
|
float h = (6 * h_deg) / 360.0f, m1, m2;
|
|
|
|
if (l<=0.5)
|
|
m2=l*(s+1);
|
|
else
|
|
m2=l+s-l*s;
|
|
m1 = l*2 - m2;
|
|
|
|
// Scale and round the RGB components
|
|
r = (uint8_t)floor(hue_to_rgb(m1, m2, h + 2) * 255 + 0.5);
|
|
g = (uint8_t)floor(hue_to_rgb(m1, m2, h ) * 255 + 0.5);
|
|
b = (uint8_t)floor(hue_to_rgb(m1, m2, h - 2) * 255 + 0.5);
|
|
|
|
return rgba_from_rgba(r, g, b, (uint8_t) (a * 255));
|
|
}
|
|
|
|
/*
|
|
* Return rgba from (h,s,l).
|
|
* Expects h values in the range [0, 360), and s, l in the range [0, 1].
|
|
*/
|
|
|
|
static inline int32_t
|
|
rgba_from_hsl(float h_deg, float s, float l) {
|
|
return rgba_from_hsla(h_deg, s, l, 1.0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return rgba from (r,g,b).
|
|
*/
|
|
|
|
static int32_t
|
|
rgba_from_rgb(uint8_t r, uint8_t g, uint8_t b) {
|
|
return rgba_from_rgba(r, g, b, 255);
|
|
}
|
|
|
|
/*
|
|
* Return rgb from "#RRGGBB".
|
|
*/
|
|
|
|
static 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"
|
|
*/
|
|
|
|
static 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()"
|
|
*/
|
|
|
|
static int32_t
|
|
rgba_from_rgb_string(const char *str, short *ok) {
|
|
if (str == strstr(str, "rgb(")) {
|
|
str += 4;
|
|
WHITESPACE;
|
|
uint8_t r = 0, g = 0, b = 0;
|
|
CHANNEL(r);
|
|
WHITESPACE_OR_COMMA;
|
|
CHANNEL(g);
|
|
WHITESPACE_OR_COMMA;
|
|
CHANNEL(b);
|
|
WHITESPACE;
|
|
return *ok = 1, rgba_from_rgb(r, g, b);
|
|
}
|
|
return *ok = 0;
|
|
}
|
|
|
|
/*
|
|
* Return rgb from "rgba()"
|
|
*/
|
|
|
|
static int32_t
|
|
rgba_from_rgba_string(const char *str, short *ok) {
|
|
if (str == strstr(str, "rgba(")) {
|
|
str += 5;
|
|
WHITESPACE;
|
|
uint8_t r = 0, g = 0, b = 0;
|
|
float a = 0;
|
|
CHANNEL(r);
|
|
WHITESPACE_OR_COMMA;
|
|
CHANNEL(g);
|
|
WHITESPACE_OR_COMMA;
|
|
CHANNEL(b);
|
|
WHITESPACE_OR_COMMA;
|
|
ALPHA(a);
|
|
WHITESPACE;
|
|
return *ok = 1, rgba_from_rgba(r, g, b, (int) (a * 255));
|
|
}
|
|
return *ok = 0;
|
|
}
|
|
|
|
/*
|
|
* Return rgb from "hsla()"
|
|
*/
|
|
|
|
static int32_t
|
|
rgba_from_hsla_string(const char *str, short *ok) {
|
|
if (str == strstr(str, "hsla(")) {
|
|
str += 5;
|
|
WHITESPACE;
|
|
float h_deg = 0;
|
|
float s = 0, l = 0;
|
|
float a = 0;
|
|
HUE(h_deg);
|
|
WHITESPACE_OR_COMMA;
|
|
SATURATION(s);
|
|
WHITESPACE_OR_COMMA;
|
|
LIGHTNESS(l);
|
|
WHITESPACE_OR_COMMA;
|
|
ALPHA(a);
|
|
WHITESPACE;
|
|
return *ok = 1, rgba_from_hsla(h_deg, s, l, a);
|
|
}
|
|
return *ok = 0;
|
|
}
|
|
|
|
/*
|
|
* Return rgb from "hsl()"
|
|
*/
|
|
|
|
static int32_t
|
|
rgba_from_hsl_string(const char *str, short *ok) {
|
|
if (str == strstr(str, "hsl(")) {
|
|
str += 4;
|
|
WHITESPACE;
|
|
float h_deg = 0;
|
|
float s = 0, l = 0;
|
|
HUE(h_deg);
|
|
WHITESPACE_OR_COMMA;
|
|
SATURATION(s);
|
|
WHITESPACE_OR_COMMA;
|
|
LIGHTNESS(l);
|
|
WHITESPACE;
|
|
return *ok = 1, rgba_from_hsl(h_deg, s, l);
|
|
}
|
|
return *ok = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return rgb from:
|
|
*
|
|
* - "#RGB"
|
|
* - "#RRGGBB"
|
|
*
|
|
*/
|
|
|
|
static int32_t
|
|
rgba_from_hex_string(const char *str, short *ok) {
|
|
size_t len = strlen(str);
|
|
*ok = 1;
|
|
if (6 == len) return rgba_from_hex6_string(str);
|
|
if (3 == len) return rgba_from_hex3_string(str);
|
|
return *ok = 0;
|
|
}
|
|
|
|
/*
|
|
* Return named color value.
|
|
*/
|
|
|
|
static int32_t
|
|
rgba_from_name_string(const char *str, short *ok) {
|
|
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;
|
|
}
|
|
return *ok = 0;
|
|
}
|
|
|
|
/*
|
|
* Return rgb from:
|
|
*
|
|
* - #RGB
|
|
* - #RRGGBB
|
|
* - rgb(r,g,b)
|
|
* - rgba(r,g,b,a)
|
|
* - hsl(h,s,l)
|
|
* - hsla(h,s,l,a)
|
|
* - name
|
|
*
|
|
*/
|
|
|
|
int32_t
|
|
rgba_from_string(const char *str, short *ok) {
|
|
if ('#' == str[0])
|
|
return rgba_from_hex_string(++str, ok);
|
|
if (str == strstr(str, "rgba"))
|
|
return rgba_from_rgba_string(str, ok);
|
|
if (str == strstr(str, "rgb"))
|
|
return rgba_from_rgb_string(str, ok);
|
|
if (str == strstr(str, "hsla"))
|
|
return rgba_from_hsla_string(str, ok);
|
|
if (str == strstr(str, "hsl"))
|
|
return rgba_from_hsl_string(str, ok);
|
|
return rgba_from_name_string(str, ok);
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
);
|
|
}
|
|
|