| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* colouring.h - Pure C colour math library (header-only, C89 compatible) |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* No Perl/XS dependencies. All functions are static. |
|
5
|
|
|
|
|
|
|
* |
|
6
|
|
|
|
|
|
|
* Usage from XS: |
|
7
|
|
|
|
|
|
|
* #define COLOURING_FATAL(msg) croak("%s", (msg)) |
|
8
|
|
|
|
|
|
|
* #include "colouring.h" |
|
9
|
|
|
|
|
|
|
* |
|
10
|
|
|
|
|
|
|
* Usage from plain C: |
|
11
|
|
|
|
|
|
|
* #include "colouring.h" |
|
12
|
|
|
|
|
|
|
* // COLOURING_FATAL defaults to fprintf+abort |
|
13
|
|
|
|
|
|
|
*/ |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
#ifndef COLOURING_H |
|
16
|
|
|
|
|
|
|
#define COLOURING_H |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
#include |
|
19
|
|
|
|
|
|
|
#include |
|
20
|
|
|
|
|
|
|
#include |
|
21
|
|
|
|
|
|
|
#include |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
/* ── Error handling ────────────────────────────────────────────── */ |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
#ifndef COLOURING_FATAL |
|
26
|
|
|
|
|
|
|
#define COLOURING_FATAL(msg) do { fprintf(stderr, "colouring: %s\n", (msg)); abort(); } while(0) |
|
27
|
|
|
|
|
|
|
#endif |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
/* ── Structs ───────────────────────────────────────────────────── */ |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
typedef struct { |
|
32
|
|
|
|
|
|
|
double r; /* 0-255 */ |
|
33
|
|
|
|
|
|
|
double g; |
|
34
|
|
|
|
|
|
|
double b; |
|
35
|
|
|
|
|
|
|
double a; /* 0.0-1.0 */ |
|
36
|
|
|
|
|
|
|
} colouring_rgba_t; |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
typedef struct { |
|
39
|
|
|
|
|
|
|
int h; /* 0-360 */ |
|
40
|
|
|
|
|
|
|
double s; /* 0.0-1.0 */ |
|
41
|
|
|
|
|
|
|
double l; /* 0.0-1.0 */ |
|
42
|
|
|
|
|
|
|
double a; /* 0.0-1.0 */ |
|
43
|
|
|
|
|
|
|
} colouring_hsl_t; |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
typedef struct { |
|
46
|
|
|
|
|
|
|
double h; /* 0-360 */ |
|
47
|
|
|
|
|
|
|
double s; /* 0.0-1.0 */ |
|
48
|
|
|
|
|
|
|
double v; /* 0.0-1.0 */ |
|
49
|
|
|
|
|
|
|
} colouring_hsv_t; |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
/* ── Scalar utilities ──────────────────────────────────────────── */ |
|
52
|
|
|
|
|
|
|
|
|
53
|
30451
|
|
|
|
|
|
static double colouring_min(double a, double b) { |
|
54
|
30451
|
100
|
|
|
|
|
return a < b ? a : b; |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
|
|
57
|
30451
|
|
|
|
|
|
static double colouring_max(double a, double b) { |
|
58
|
30451
|
100
|
|
|
|
|
return a > b ? a : b; |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
30279
|
|
|
|
|
|
static double colouring_clamp(double val, double upper) { |
|
62
|
30279
|
|
|
|
|
|
return colouring_min(colouring_max(val, 0), upper); |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
|
static double colouring_round(double val, int dp) { |
|
66
|
0
|
|
|
|
|
|
double factor = pow(10.0, dp); |
|
67
|
0
|
|
|
|
|
|
return floor(val * factor + 0.5) / factor; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
65
|
|
|
|
|
|
static double colouring_depercent(const char *str) { |
|
71
|
65
|
|
|
|
|
|
return atof(str) / 100.0; |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
/* Format 0.0-1.0 as "NN%". buf must be >= 8 bytes. */ |
|
75
|
40
|
|
|
|
|
|
static void colouring_percent_buf(double num, char *buf, size_t bufsz) { |
|
76
|
40
|
|
|
|
|
|
snprintf(buf, bufsz, "%.0f%%", num * 100.0); |
|
77
|
40
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
/* ── Hex parsing ───────────────────────────────────────────────── */ |
|
80
|
|
|
|
|
|
|
|
|
81
|
145
|
|
|
|
|
|
static int colouring_hex2int(const char *hex) { |
|
82
|
145
|
|
|
|
|
|
int val = 0; |
|
83
|
433
|
100
|
|
|
|
|
while (*hex) { |
|
84
|
289
|
|
|
|
|
|
int byte = *hex++; |
|
85
|
289
|
50
|
|
|
|
|
if (byte >= '0' && byte <= '9') byte = byte - '0'; |
|
|
|
100
|
|
|
|
|
|
|
86
|
217
|
50
|
|
|
|
|
else if (byte >= 'a' && byte <= 'f') byte = byte - 'a' + 10; |
|
|
|
100
|
|
|
|
|
|
|
87
|
1
|
50
|
|
|
|
|
else if (byte >= 'A' && byte <= 'F') byte = byte - 'A' + 10; |
|
|
|
50
|
|
|
|
|
|
|
88
|
1
|
|
|
|
|
|
else { COLOURING_FATAL("Invalid hex character"); return 0; } |
|
89
|
288
|
|
|
|
|
|
val = (val << 4) | (byte & 0xF); |
|
90
|
|
|
|
|
|
|
} |
|
91
|
144
|
|
|
|
|
|
return val; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
49
|
|
|
|
|
|
static colouring_rgba_t colouring_hex2rgb(const char *hex) { |
|
95
|
|
|
|
|
|
|
colouring_rgba_t c; |
|
96
|
|
|
|
|
|
|
int l; |
|
97
|
49
|
|
|
|
|
|
c.a = 1.0; |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
/* skip leading '#' if present */ |
|
100
|
49
|
50
|
|
|
|
|
if (hex[0] == '#') hex++; |
|
101
|
49
|
|
|
|
|
|
l = (int)strlen(hex); |
|
102
|
|
|
|
|
|
|
|
|
103
|
49
|
100
|
|
|
|
|
if (l == 3) { |
|
104
|
|
|
|
|
|
|
char h[3]; |
|
105
|
2
|
|
|
|
|
|
h[2] = '\0'; |
|
106
|
2
|
|
|
|
|
|
h[0] = hex[0]; h[1] = hex[0]; c.r = colouring_hex2int(h); |
|
107
|
1
|
|
|
|
|
|
h[0] = hex[1]; h[1] = hex[1]; c.g = colouring_hex2int(h); |
|
108
|
1
|
|
|
|
|
|
h[0] = hex[2]; h[1] = hex[2]; c.b = colouring_hex2int(h); |
|
109
|
47
|
50
|
|
|
|
|
} else if (l == 6) { |
|
110
|
|
|
|
|
|
|
char h[3]; |
|
111
|
47
|
|
|
|
|
|
h[2] = '\0'; |
|
112
|
47
|
|
|
|
|
|
h[0] = hex[0]; h[1] = hex[1]; c.r = colouring_hex2int(h); |
|
113
|
47
|
|
|
|
|
|
h[0] = hex[2]; h[1] = hex[3]; c.g = colouring_hex2int(h); |
|
114
|
47
|
|
|
|
|
|
h[0] = hex[4]; h[1] = hex[5]; c.b = colouring_hex2int(h); |
|
115
|
|
|
|
|
|
|
} else { |
|
116
|
0
|
|
|
|
|
|
COLOURING_FATAL("hex length must be 3 or 6"); |
|
117
|
|
|
|
|
|
|
c.r = c.g = c.b = 0; |
|
118
|
|
|
|
|
|
|
} |
|
119
|
48
|
|
|
|
|
|
return c; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
/* ── Number parsing from colour strings ────────────────────────── */ |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
/* Parse numeric values from strings like "rgb(255,0,128)" or "hsl(120,50%,50%)". |
|
125
|
|
|
|
|
|
|
Writes up to max_out doubles, returns count written. */ |
|
126
|
10021
|
|
|
|
|
|
static int colouring_parse_numbers(const char *str, double *out, int max_out) { |
|
127
|
10021
|
|
|
|
|
|
int count = 0; |
|
128
|
|
|
|
|
|
|
char temp[32]; |
|
129
|
10021
|
|
|
|
|
|
int ti = 0; |
|
130
|
|
|
|
|
|
|
int i; |
|
131
|
10021
|
|
|
|
|
|
int len = (int)strlen(str); |
|
132
|
|
|
|
|
|
|
|
|
133
|
229310
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
134
|
219289
|
100
|
|
|
|
|
if ((str[i] >= '0' && str[i] <= '9') || str[i] == '.') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
135
|
99046
|
50
|
|
|
|
|
if (ti < 31) temp[ti++] = str[i]; |
|
136
|
120243
|
100
|
|
|
|
|
} else if (ti > 0) { |
|
137
|
40070
|
|
|
|
|
|
temp[ti] = '\0'; |
|
138
|
40070
|
50
|
|
|
|
|
if (count < max_out) { |
|
139
|
40070
|
|
|
|
|
|
out[count++] = atof(temp); |
|
140
|
|
|
|
|
|
|
} |
|
141
|
40070
|
|
|
|
|
|
ti = 0; |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
/* flush trailing number */ |
|
145
|
10021
|
50
|
|
|
|
|
if (ti > 0 && count < max_out) { |
|
|
|
0
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
|
temp[ti] = '\0'; |
|
147
|
0
|
|
|
|
|
|
out[count++] = atof(temp); |
|
148
|
|
|
|
|
|
|
} |
|
149
|
10021
|
|
|
|
|
|
return count; |
|
150
|
|
|
|
|
|
|
} |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
/* ── HSL helper ────────────────────────────────────────────────── */ |
|
153
|
|
|
|
|
|
|
|
|
154
|
30210
|
|
|
|
|
|
static double colouring__hue(double h, double m1, double m2) { |
|
155
|
30210
|
100
|
|
|
|
|
h = h < 0 ? h + 1 : h > 1 ? h - 1 : h; |
|
|
|
100
|
|
|
|
|
|
|
156
|
30210
|
100
|
|
|
|
|
if (h * 6.0 < 1.0) return m1 + (m2 - m1) * h * 6.0; |
|
157
|
25151
|
100
|
|
|
|
|
if (h * 2.0 < 1.0) return m2; |
|
158
|
15081
|
100
|
|
|
|
|
if (h * 3.0 < 2.0) return m1 + (m2 - m1) * ((2.0 / 3.0) - h) * 6.0; |
|
159
|
10098
|
|
|
|
|
|
return m1; |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
/* ── Colour space conversions ──────────────────────────────────── */ |
|
163
|
|
|
|
|
|
|
|
|
164
|
10070
|
|
|
|
|
|
static colouring_rgba_t colouring_hsl2rgb(double h, double s, double l, double a) { |
|
165
|
|
|
|
|
|
|
colouring_rgba_t c; |
|
166
|
|
|
|
|
|
|
double m2, m1; |
|
167
|
10070
|
|
|
|
|
|
int hi = (int)h; |
|
168
|
|
|
|
|
|
|
|
|
169
|
10070
|
|
|
|
|
|
h = (hi % 360) / 360.0; |
|
170
|
10070
|
100
|
|
|
|
|
if (s > 1 || l > 1) { |
|
|
|
50
|
|
|
|
|
|
|
171
|
10001
|
|
|
|
|
|
s = s / 100.0; |
|
172
|
10001
|
|
|
|
|
|
l = l / 100.0; |
|
173
|
|
|
|
|
|
|
} |
|
174
|
10070
|
100
|
|
|
|
|
m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; |
|
175
|
10070
|
|
|
|
|
|
m1 = l * 2 - m2; |
|
176
|
|
|
|
|
|
|
|
|
177
|
10070
|
|
|
|
|
|
c.r = (int)(colouring_clamp(colouring__hue(h + (1.0 / 3.0), m1, m2), 1) * 255); |
|
178
|
10070
|
|
|
|
|
|
c.g = (int)(colouring_clamp(colouring__hue(h, m1, m2), 1) * 255); |
|
179
|
10070
|
|
|
|
|
|
c.b = (int)(colouring_clamp(colouring__hue(h - (1.0 / 3.0), m1, m2), 1) * 255); |
|
180
|
10070
|
|
|
|
|
|
c.a = a; |
|
181
|
10070
|
|
|
|
|
|
return c; |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
78
|
|
|
|
|
|
static colouring_hsl_t colouring_rgb2hsl(double r, double g, double b, double a) { |
|
185
|
|
|
|
|
|
|
colouring_hsl_t hsl; |
|
186
|
78
|
|
|
|
|
|
double rn = r / 255.0; |
|
187
|
78
|
|
|
|
|
|
double gn = g / 255.0; |
|
188
|
78
|
|
|
|
|
|
double bn = b / 255.0; |
|
189
|
78
|
|
|
|
|
|
double mx = colouring_max(colouring_max(rn, gn), bn); |
|
190
|
78
|
|
|
|
|
|
double mn = colouring_min(colouring_min(rn, gn), bn); |
|
191
|
78
|
|
|
|
|
|
double d = mx - mn; |
|
192
|
78
|
|
|
|
|
|
double h = 0; |
|
193
|
78
|
|
|
|
|
|
double s = 0; |
|
194
|
78
|
|
|
|
|
|
double l = (mx + mn) / 2.0; |
|
195
|
|
|
|
|
|
|
|
|
196
|
78
|
100
|
|
|
|
|
if (mx != mn) { |
|
197
|
10
|
100
|
|
|
|
|
s = l > 0.5 ? (d / (2.0 - mx - mn)) : (d / (mx + mn)); |
|
198
|
10
|
100
|
|
|
|
|
if (mx == rn) |
|
199
|
4
|
100
|
|
|
|
|
h = (gn - bn) / d + (gn < bn ? 6 : 0); |
|
200
|
6
|
100
|
|
|
|
|
else if (mx == gn) |
|
201
|
5
|
|
|
|
|
|
h = (bn - rn) / d + 2; |
|
202
|
|
|
|
|
|
|
else |
|
203
|
1
|
|
|
|
|
|
h = (rn - gn) / d + 4; |
|
204
|
10
|
|
|
|
|
|
h = h / 6.0; |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
|
|
207
|
78
|
|
|
|
|
|
hsl.h = (int)(h * 360); |
|
208
|
78
|
|
|
|
|
|
hsl.s = s; |
|
209
|
78
|
|
|
|
|
|
hsl.l = l; |
|
210
|
78
|
|
|
|
|
|
hsl.a = a; |
|
211
|
78
|
|
|
|
|
|
return hsl; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
8
|
|
|
|
|
|
static colouring_hsv_t colouring_rgb2hsv(double r, double g, double b) { |
|
215
|
|
|
|
|
|
|
colouring_hsv_t hsv; |
|
216
|
8
|
|
|
|
|
|
double rn = r / 255.0; |
|
217
|
8
|
|
|
|
|
|
double gn = g / 255.0; |
|
218
|
8
|
|
|
|
|
|
double bn = b / 255.0; |
|
219
|
8
|
|
|
|
|
|
double mx = colouring_max(colouring_max(rn, gn), bn); |
|
220
|
8
|
|
|
|
|
|
double mn = colouring_min(colouring_min(rn, gn), bn); |
|
221
|
8
|
|
|
|
|
|
double d = mx - mn; |
|
222
|
8
|
|
|
|
|
|
double h = 0; |
|
223
|
|
|
|
|
|
|
|
|
224
|
8
|
100
|
|
|
|
|
hsv.s = (mx == 0) ? 0 : d / mx; |
|
225
|
8
|
|
|
|
|
|
hsv.v = mx; |
|
226
|
|
|
|
|
|
|
|
|
227
|
8
|
100
|
|
|
|
|
if (mx != mn) { |
|
228
|
6
|
100
|
|
|
|
|
if (mx == rn) |
|
229
|
4
|
100
|
|
|
|
|
h = (gn - bn) / d + (gn < bn ? 6 : 0); |
|
230
|
2
|
100
|
|
|
|
|
else if (mx == gn) |
|
231
|
1
|
|
|
|
|
|
h = (bn - rn) / d + 2; |
|
232
|
|
|
|
|
|
|
else |
|
233
|
1
|
|
|
|
|
|
h = (rn - gn) / d + 4; |
|
234
|
6
|
|
|
|
|
|
h = h / 6.0; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
|
|
237
|
8
|
|
|
|
|
|
hsv.h = h * 360.0; |
|
238
|
8
|
|
|
|
|
|
return hsv; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
/* ── Colour manipulation ───────────────────────────────────────── */ |
|
242
|
|
|
|
|
|
|
|
|
243
|
11
|
|
|
|
|
|
static colouring_rgba_t colouring_lighten(double r, double g, double b, double a, |
|
244
|
|
|
|
|
|
|
double amount, int relative) { |
|
245
|
11
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
246
|
11
|
100
|
|
|
|
|
if (relative) { |
|
247
|
|
|
|
|
|
|
/* Original C code: double l = hsl.l || 1.; -- C logical OR |
|
248
|
|
|
|
|
|
|
always yields 1 when hsl.l is nonzero, 1 when zero. |
|
249
|
|
|
|
|
|
|
So relative lighten behaves same as absolute. */ |
|
250
|
2
|
|
|
|
|
|
hsl.l = hsl.l + colouring_clamp(amount, 1); |
|
251
|
|
|
|
|
|
|
} else { |
|
252
|
9
|
|
|
|
|
|
hsl.l = hsl.l + colouring_clamp(amount, 1); |
|
253
|
|
|
|
|
|
|
} |
|
254
|
11
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
11
|
|
|
|
|
|
static colouring_rgba_t colouring_darken(double r, double g, double b, double a, |
|
258
|
|
|
|
|
|
|
double amount, int relative) { |
|
259
|
11
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
260
|
11
|
100
|
|
|
|
|
if (relative) { |
|
261
|
1
|
|
|
|
|
|
hsl.l = hsl.l - colouring_clamp(hsl.l * amount, 1); |
|
262
|
|
|
|
|
|
|
} else { |
|
263
|
10
|
|
|
|
|
|
hsl.l = hsl.l - colouring_clamp(amount, 1); |
|
264
|
|
|
|
|
|
|
} |
|
265
|
11
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
12
|
|
|
|
|
|
static colouring_rgba_t colouring_fade(double r, double g, double b, double a, |
|
269
|
|
|
|
|
|
|
double amount) { |
|
270
|
12
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
271
|
12
|
|
|
|
|
|
hsl.a = amount; |
|
272
|
12
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
|
|
275
|
14
|
|
|
|
|
|
static colouring_rgba_t colouring_fadeout(double r, double g, double b, double a, |
|
276
|
|
|
|
|
|
|
double amount, int relative) { |
|
277
|
14
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
278
|
14
|
100
|
|
|
|
|
hsl.a -= colouring_clamp(relative ? hsl.a * amount : amount, 1); |
|
279
|
14
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
14
|
|
|
|
|
|
static colouring_rgba_t colouring_fadein(double r, double g, double b, double a, |
|
283
|
|
|
|
|
|
|
double amount, int relative) { |
|
284
|
14
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
285
|
14
|
100
|
|
|
|
|
hsl.a += colouring_clamp(relative ? hsl.a * amount : amount, 1); |
|
286
|
14
|
|
|
|
|
|
hsl.a = colouring_clamp(hsl.a, 1); |
|
287
|
14
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
1
|
|
|
|
|
|
static colouring_rgba_t colouring_saturate(double r, double g, double b, double a, |
|
291
|
|
|
|
|
|
|
double amount, int relative) { |
|
292
|
1
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
293
|
1
|
50
|
|
|
|
|
hsl.s += colouring_clamp(relative ? hsl.s * amount : amount, 1); |
|
294
|
1
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
2
|
|
|
|
|
|
static colouring_rgba_t colouring_desaturate(double r, double g, double b, double a, |
|
298
|
|
|
|
|
|
|
double amount, int relative) { |
|
299
|
2
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
300
|
2
|
50
|
|
|
|
|
hsl.s -= colouring_clamp(relative ? hsl.s * amount : amount, 1); |
|
301
|
2
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
1
|
|
|
|
|
|
static colouring_rgba_t colouring_greyscale(double r, double g, double b, double a) { |
|
305
|
1
|
|
|
|
|
|
colouring_hsl_t hsl = colouring_rgb2hsl(r, g, b, a); |
|
306
|
1
|
|
|
|
|
|
hsl.s -= 1.0; |
|
307
|
1
|
|
|
|
|
|
return colouring_hsl2rgb(hsl.h, hsl.s, hsl.l, hsl.a); |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
|
|
310
|
3
|
|
|
|
|
|
static colouring_rgba_t colouring_mix(colouring_rgba_t c1, colouring_rgba_t c2, |
|
311
|
|
|
|
|
|
|
int weight) { |
|
312
|
|
|
|
|
|
|
colouring_rgba_t out; |
|
313
|
3
|
|
|
|
|
|
double w = weight / 100.0; |
|
314
|
3
|
|
|
|
|
|
double a = c1.a - c2.a; |
|
315
|
|
|
|
|
|
|
double w1, w2; |
|
316
|
|
|
|
|
|
|
|
|
317
|
3
|
|
|
|
|
|
w = (w * 2) - 1; |
|
318
|
3
|
50
|
|
|
|
|
w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; |
|
319
|
3
|
|
|
|
|
|
w2 = 1 - w1; |
|
320
|
|
|
|
|
|
|
|
|
321
|
3
|
|
|
|
|
|
out.r = (c1.r * w1) + (c2.r * w2); |
|
322
|
3
|
|
|
|
|
|
out.g = (c1.g * w1) + (c2.g * w2); |
|
323
|
3
|
|
|
|
|
|
out.b = (c1.b * w1) + (c2.b * w2); |
|
324
|
3
|
|
|
|
|
|
out.a = (c1.a * w) + (c2.a * (1 - w)); |
|
325
|
3
|
|
|
|
|
|
return out; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
1
|
|
|
|
|
|
static colouring_rgba_t colouring_tint(colouring_rgba_t c, int weight) { |
|
329
|
|
|
|
|
|
|
colouring_rgba_t white; |
|
330
|
1
|
|
|
|
|
|
white.r = 255; white.g = 255; white.b = 255; white.a = 1.0; |
|
331
|
1
|
|
|
|
|
|
return colouring_mix(white, c, weight); |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
1
|
|
|
|
|
|
static colouring_rgba_t colouring_shade(colouring_rgba_t c, int weight) { |
|
335
|
|
|
|
|
|
|
colouring_rgba_t black; |
|
336
|
1
|
|
|
|
|
|
black.r = 0; black.g = 0; black.b = 0; black.a = 1.0; |
|
337
|
1
|
|
|
|
|
|
return colouring_mix(black, c, weight); |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
/* ── Formatting ────────────────────────────────────────────────── */ |
|
341
|
|
|
|
|
|
|
|
|
342
|
27
|
|
|
|
|
|
static void colouring_fmt_hex(colouring_rgba_t c, char *buf, size_t bufsz, |
|
343
|
|
|
|
|
|
|
int force_long) { |
|
344
|
27
|
|
|
|
|
|
int r = (int)c.r, g = (int)c.g, b = (int)c.b; |
|
345
|
27
|
|
|
|
|
|
snprintf(buf, bufsz, "#%02x%02x%02x", r, g, b); |
|
346
|
27
|
100
|
|
|
|
|
if (!force_long) { |
|
347
|
|
|
|
|
|
|
/* shorten "#aabbcc" to "#abc" if possible */ |
|
348
|
22
|
100
|
|
|
|
|
if (buf[1] == buf[2] && buf[3] == buf[4] && buf[5] == buf[6]) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
349
|
21
|
|
|
|
|
|
buf[2] = buf[3]; |
|
350
|
21
|
|
|
|
|
|
buf[3] = buf[5]; |
|
351
|
21
|
|
|
|
|
|
buf[4] = '\0'; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
} |
|
354
|
27
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
|
|
356
|
9
|
|
|
|
|
|
static void colouring_fmt_rgb(colouring_rgba_t c, char *buf, size_t bufsz) { |
|
357
|
9
|
|
|
|
|
|
snprintf(buf, bufsz, "rgb(%d,%d,%d)", (int)c.r, (int)c.g, (int)c.b); |
|
358
|
9
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
45
|
|
|
|
|
|
static void colouring_fmt_rgba(colouring_rgba_t c, char *buf, size_t bufsz) { |
|
361
|
45
|
|
|
|
|
|
snprintf(buf, bufsz, "rgba(%d,%d,%d,%.2g)", (int)c.r, (int)c.g, (int)c.b, c.a); |
|
362
|
45
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
|
|
364
|
12
|
|
|
|
|
|
static void colouring_fmt_hsl(colouring_hsl_t hsl, char *buf, size_t bufsz) { |
|
365
|
|
|
|
|
|
|
char ps[8], pl[8]; |
|
366
|
12
|
|
|
|
|
|
colouring_percent_buf(hsl.s, ps, sizeof(ps)); |
|
367
|
12
|
|
|
|
|
|
colouring_percent_buf(hsl.l, pl, sizeof(pl)); |
|
368
|
12
|
|
|
|
|
|
snprintf(buf, bufsz, "hsl(%d,%s,%s)", hsl.h, ps, pl); |
|
369
|
12
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
|
|
371
|
8
|
|
|
|
|
|
static void colouring_fmt_hsv(colouring_hsv_t hsv, char *buf, size_t bufsz) { |
|
372
|
|
|
|
|
|
|
char ps[8], pv[8]; |
|
373
|
8
|
|
|
|
|
|
colouring_percent_buf(hsv.s, ps, sizeof(ps)); |
|
374
|
8
|
|
|
|
|
|
colouring_percent_buf(hsv.v, pv, sizeof(pv)); |
|
375
|
8
|
|
|
|
|
|
snprintf(buf, bufsz, "hsv(%.0f,%s,%s)", hsv.h, ps, pv); |
|
376
|
8
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
5
|
|
|
|
|
|
static void colouring_fmt_term(colouring_rgba_t c, char *buf, size_t bufsz) { |
|
379
|
5
|
|
|
|
|
|
snprintf(buf, bufsz, "r%dg%db%d", (int)c.r, (int)c.g, (int)c.b); |
|
380
|
5
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
|
|
382
|
5
|
|
|
|
|
|
static void colouring_fmt_on_term(colouring_rgba_t c, char *buf, size_t bufsz) { |
|
383
|
5
|
|
|
|
|
|
snprintf(buf, bufsz, "on_r%dg%db%d", (int)c.r, (int)c.g, (int)c.b); |
|
384
|
5
|
|
|
|
|
|
} |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
/* ── Colour string conversion (dispatch) ──────────────────────── */ |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
/* Parse any supported colour string into RGBA. |
|
389
|
|
|
|
|
|
|
Returns 1 on success, 0 on failure. */ |
|
390
|
10070
|
|
|
|
|
|
static int colouring_parse(const char *str, colouring_rgba_t *out) { |
|
391
|
10070
|
100
|
|
|
|
|
if (str[0] == '#') { |
|
392
|
49
|
|
|
|
|
|
*out = colouring_hex2rgb(str); |
|
393
|
48
|
|
|
|
|
|
return 1; |
|
394
|
10021
|
100
|
|
|
|
|
} else if (str[0] == 'r' && str[1] == 'g' && str[2] == 'b') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
double nums[4]; |
|
396
|
17
|
|
|
|
|
|
int n = colouring_parse_numbers(str, nums, 4); |
|
397
|
17
|
100
|
|
|
|
|
if (n < 3) return 0; |
|
398
|
16
|
|
|
|
|
|
out->r = nums[0]; |
|
399
|
16
|
|
|
|
|
|
out->g = nums[1]; |
|
400
|
16
|
|
|
|
|
|
out->b = nums[2]; |
|
401
|
16
|
100
|
|
|
|
|
out->a = n >= 4 ? nums[3] : 1.0; |
|
402
|
16
|
|
|
|
|
|
return 1; |
|
403
|
10004
|
50
|
|
|
|
|
} else if (str[0] == 'h' && str[1] == 's' && str[2] == 'l') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
double nums[4]; |
|
405
|
10004
|
|
|
|
|
|
int n = colouring_parse_numbers(str, nums, 4); |
|
406
|
10004
|
100
|
|
|
|
|
if (n < 3) return 0; |
|
407
|
10003
|
100
|
|
|
|
|
*out = colouring_hsl2rgb(nums[0], nums[1], nums[2], |
|
408
|
|
|
|
|
|
|
n >= 4 ? nums[3] : 1.0); |
|
409
|
10003
|
|
|
|
|
|
return 1; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
0
|
|
|
|
|
|
return 0; |
|
412
|
|
|
|
|
|
|
} |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
#endif /* COLOURING_H */ |