1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* |
10 | * @f color |
11 | * @t The color module |
12 | * @a Alex van Ballegooij |
13 | * @v 1.0 |
14 | * @* Introduction |
15 | * The color atom is a simple 32bit (24bit+zeros) encoding of a standard RGB |
16 | * color encoding, consisting of the red component in bits 16-23, |
17 | * the green component in bits 8-15, and the blue component in bits 0-7. |
18 | * |
19 | * The module contains a number of color conversion methods to construct |
20 | * colors in various colorspaces and extract relevant color channels. |
21 | * |
22 | * @enumerate |
23 | * @item rgb |
24 | * = (byte,byte,byte) colorspace r,g,b=[0..255] |
25 | * @item hsv |
26 | * = (flt,flt,flt) colorspace h=[0..360], s,v=[0..1] |
27 | * @item ycc |
28 | * = (byte,byte,byte) colorspace y,c,c=[0..255] |
29 | * @end enumerate |
30 | * |
31 | */ |
32 | |
33 | #include "monetdb_config.h" |
34 | #include "mal.h" |
35 | #include "mal_exception.h" |
36 | #include <math.h> |
37 | #include "color.h" |
38 | |
39 | /* |
40 | * @- Atom commands |
41 | */ |
42 | |
43 | static int |
44 | CLRhextoint(char h, char l) |
45 | { |
46 | int r = 0; |
47 | |
48 | if (isdigit((unsigned char) h)) |
49 | r = 16 * (int) (h - '0'); |
50 | else if (h >= 'a' && h <= 'f') |
51 | r = 16 * (int) (10 + h - 'a'); |
52 | else if (h >= 'A' && h <= 'F') |
53 | r = 16 * (int) (10 + h - 'A'); |
54 | else |
55 | return -1; |
56 | if (isdigit((unsigned char) l)) |
57 | r += (int) (l - '0'); |
58 | else if (l >= 'a' && l <= 'f') |
59 | r += (int) (10 + l - 'a'); |
60 | else if (l >= 'A' && l <= 'F') |
61 | r += (int) (10 + l - 'A'); |
62 | else |
63 | return -1; |
64 | return r; |
65 | } |
66 | |
67 | ssize_t |
68 | color_fromstr(const char *colorStr, size_t *len, color **c, bool external) |
69 | { |
70 | const char *p = colorStr; |
71 | |
72 | if (*len < sizeof(color) || *c == NULL) { |
73 | GDKfree(*c); |
74 | *c = GDKmalloc(sizeof(color)); |
75 | if( *c == NULL) |
76 | return -1; |
77 | *len = sizeof(color); |
78 | } |
79 | |
80 | if (GDK_STRNIL(colorStr)) { |
81 | **c = color_nil; |
82 | return 1; |
83 | } |
84 | |
85 | while (GDKisspace(*p)) |
86 | p++; |
87 | if (external && strncmp(p, "nil" , 3) == 0) { |
88 | **c = color_nil; |
89 | p += 3; |
90 | } else if (strncmp(p, "0x00" , 4) == 0) { |
91 | int r, g, b; |
92 | |
93 | if ((r = CLRhextoint(p[4], p[5])) == -1 || |
94 | (g = CLRhextoint(p[6], p[7])) == -1 || |
95 | (b = CLRhextoint(p[8], p[9])) == -1) { |
96 | **c = color_nil; |
97 | return 0; |
98 | } |
99 | **c = (color) (r << 16 | g << 8 | b); |
100 | p += 10; |
101 | } else { |
102 | **c = color_nil; |
103 | GDKerror("not a color\n" ); |
104 | return -1; |
105 | } |
106 | return (ssize_t) (p - colorStr); |
107 | } |
108 | |
109 | ssize_t |
110 | color_tostr(char **colorStr, size_t *len, const color *c, bool external) |
111 | { |
112 | color sc = *c; |
113 | |
114 | /* allocate and fill a new string */ |
115 | |
116 | if (*len < 11 || *colorStr == NULL) { |
117 | GDKfree(*colorStr); |
118 | *colorStr = GDKmalloc(11); |
119 | if( *colorStr == NULL) |
120 | return -1; |
121 | *len = 11; |
122 | } |
123 | |
124 | if (is_color_nil(sc)) { |
125 | if (external) { |
126 | strcpy(*colorStr, "nil" ); |
127 | return 3; |
128 | } |
129 | strcpy(*colorStr, str_nil); |
130 | return 1; |
131 | } |
132 | snprintf(*colorStr, *len, "0x%08X" , (unsigned int) sc); |
133 | |
134 | return (ssize_t) strlen(*colorStr); |
135 | } |
136 | |
137 | str |
138 | CLRstr(str *s, const color *c) |
139 | { |
140 | size_t len = 0; |
141 | str t = 0; |
142 | |
143 | if (color_tostr(&t, &len, c, false) < 0) |
144 | throw(MAL, "color.str" , GDK_EXCEPTION); |
145 | *s = t; |
146 | return MAL_SUCCEED; |
147 | } |
148 | |
149 | str |
150 | CLRrgb(color *rgb, const int *r, const int *g, const int *b) |
151 | { |
152 | *rgb = (color) (((*r & 0xFF) << 16) | ((*g & 0xFF) << 8) | (*b & 0xFF)); |
153 | return (MAL_SUCCEED); |
154 | } |
155 | |
156 | str |
157 | CLRred(int *r, const color *c) |
158 | { |
159 | *r = (int) ((*c >> 16) & 0xFF); |
160 | return (MAL_SUCCEED); |
161 | } |
162 | |
163 | str |
164 | CLRgreen(int *g, const color *c) |
165 | { |
166 | *g = (int) ((*c >> 8) & 0xFF); |
167 | return (MAL_SUCCEED); |
168 | } |
169 | |
170 | str |
171 | CLRblue(int *b, const color *c) |
172 | { |
173 | *b = (int) (*c & 0xFF); |
174 | return (MAL_SUCCEED); |
175 | } |
176 | |
177 | #define max2(a,b) ((a)>(b)?(a):(b)) |
178 | #define max3(a,b,c) max2(max2(a,b),(c)) |
179 | |
180 | #define min2(a,b) ((a)<(b)?(a):(b)) |
181 | #define min3(a,b,c) min2(min2(a,b),(c)) |
182 | |
183 | #define EPS 0.001f |
184 | |
185 | static void |
186 | color_rgb2hsv(float *h, float *s, float *v, int R, int G, int B) |
187 | { |
188 | register float H, S, V, max; |
189 | register float Rtmp = ((float) (R)) / 255.0f; |
190 | register float Gtmp = ((float) (G)) / 255.0f; |
191 | register float Btmp = ((float) (B)) / 255.0f; |
192 | |
193 | max = max3(Rtmp, Gtmp, Btmp); |
194 | V = max; |
195 | if (fabs(max) <= EPS) { |
196 | S = 0; |
197 | H = 0; |
198 | } else { |
199 | register float min, delta; |
200 | |
201 | min = min3(Rtmp, Gtmp, Btmp); |
202 | delta = max - min; |
203 | S = delta / max; |
204 | if (Rtmp == max) |
205 | H = (Gtmp - Btmp) / delta; |
206 | else if (Gtmp == max) |
207 | H = 2 + (Btmp - Rtmp) / delta; |
208 | else /* Btmp == max */ |
209 | H = 4 + (Rtmp - Gtmp) / delta; |
210 | H *= 60; |
211 | if (H < 0) |
212 | H += 360; |
213 | } |
214 | *h = H; |
215 | *s = S; |
216 | *v = V; |
217 | } |
218 | |
219 | str |
220 | CLRhsv(color *c, const flt *h, const flt *s, const flt *v) |
221 | { |
222 | int r, g, b; |
223 | float Rtmp, Gtmp, Btmp; |
224 | |
225 | if (fabs(*s) <= EPS) { |
226 | Rtmp = Gtmp = Btmp = (*v); |
227 | } else { |
228 | float Htmp = (*h) / 60; |
229 | float f = Htmp - ((int) Htmp); |
230 | float p = (*v) * (1 - (*s)); |
231 | float q = (*v) * (1 - (*s) * f); |
232 | float t = (*v) * (1 - (*s) * (1 - f)); |
233 | |
234 | switch ((int) floor(Htmp)) { |
235 | case 0: |
236 | Rtmp = *v; |
237 | Gtmp = t; |
238 | Btmp = p; |
239 | break; |
240 | case 1: |
241 | Rtmp = q; |
242 | Gtmp = *v; |
243 | Btmp = p; |
244 | break; |
245 | case 2: |
246 | Rtmp = p; |
247 | Gtmp = *v; |
248 | Btmp = t; |
249 | break; |
250 | case 3: |
251 | Rtmp = p; |
252 | Gtmp = q; |
253 | Btmp = *v; |
254 | break; |
255 | case 4: |
256 | Rtmp = t; |
257 | Gtmp = p; |
258 | Btmp = *v; |
259 | break; |
260 | default: /* case 5: */ |
261 | Rtmp = *v; |
262 | Gtmp = p; |
263 | Btmp = q; |
264 | break; |
265 | } |
266 | } |
267 | r = (int) ((Rtmp * 255.0f) + 0.5f); |
268 | g = (int) ((Gtmp * 255.0f) + 0.5f); |
269 | b = (int) ((Btmp * 255.0f) + 0.5f); |
270 | return CLRrgb(c, &r, &g, &b); |
271 | } |
272 | |
273 | str |
274 | CLRhue(flt *f, const color *c) |
275 | { |
276 | float h, s, v; |
277 | |
278 | color_rgb2hsv(&h, &s, &v, (*c >> 16) & 0xFF, (*c >> 8) & 0xFF, (*c) & 0xFF); |
279 | *f = h; |
280 | return (MAL_SUCCEED); |
281 | } |
282 | |
283 | str |
284 | CLRhueInt(int *f, const color *c) |
285 | { |
286 | float h, s, v; |
287 | |
288 | color_rgb2hsv(&h, &s, &v, (*c >> 16) & 0xFF, (*c >> 8) & 0xFF, (*c) & 0xFF); |
289 | *f = (int) h; |
290 | return (MAL_SUCCEED); |
291 | } |
292 | |
293 | str |
294 | CLRsaturation(flt *f, const color *c) |
295 | { |
296 | float h, s, v; |
297 | |
298 | color_rgb2hsv(&h, &s, &v, (*c >> 16) & 0xFF, (*c >> 8) & 0xFF, (*c) & 0xFF); |
299 | *f = s; |
300 | return (MAL_SUCCEED); |
301 | } |
302 | |
303 | str |
304 | CLRsaturationInt(int *f, const color *c) |
305 | { |
306 | float h, s, v; |
307 | |
308 | color_rgb2hsv(&h, &s, &v, (*c >> 16) & 0xFF, (*c >> 8) & 0xFF, (*c) & 0xFF); |
309 | *f = (int) s; |
310 | return (MAL_SUCCEED); |
311 | } |
312 | |
313 | str |
314 | CLRvalue(flt *f, const color *c) |
315 | { |
316 | float h, s, v; |
317 | |
318 | color_rgb2hsv(&h, &s, &v, (*c >> 16) & 0xFF, (*c >> 8) & 0xFF, (*c) & 0xFF); |
319 | *f = v; |
320 | return (MAL_SUCCEED); |
321 | } |
322 | |
323 | str |
324 | CLRvalueInt(int *f, const color *c) |
325 | { |
326 | float h, s, v; |
327 | |
328 | color_rgb2hsv(&h, &s, &v, (*c >> 16) & 0xFF, (*c >> 8) & 0xFF, (*c) & 0xFF); |
329 | *f = (int) v; |
330 | return (MAL_SUCCEED); |
331 | } |
332 | |
333 | |
334 | #ifndef CLIP |
335 | #define CLIP(X) ((unsigned char)(((X)&~0xFF)?(((X)<0x00)?0x00:0xFF):(X))) |
336 | #endif |
337 | |
338 | str |
339 | CLRycc(color *c, const int *y, const int *cr, const int *cb) |
340 | { |
341 | int r, g, b; |
342 | float Y = (float) *y; |
343 | float CR = (float) (*cr - 128); |
344 | float CB = (float) (*cb - 128); |
345 | |
346 | r = (int) (Y + CR * 1.4022f); |
347 | r = CLIP(r); |
348 | g = (int) (Y - CB * 0.3456f - CR * 0.7145f); |
349 | g = CLIP(g); |
350 | b = (int) (Y + CB * 1.7710f); |
351 | b = CLIP(b); |
352 | return CLRrgb(c, &r, &g, &b); |
353 | } |
354 | |
355 | str |
356 | CLRluminance(int *y, const color *c) |
357 | { |
358 | int r = (int) ((*c >> 16) & 0xFF); |
359 | int g = (int) ((*c >> 8) & 0xFF); |
360 | int b = (int) (*c & 0xFF); |
361 | |
362 | *y = (int) (0.2989f * (float) (r) + 0.5866f * (float) (g) + 0.1145f * (float) (b)); |
363 | *y = CLIP(*y); |
364 | return (MAL_SUCCEED); |
365 | } |
366 | |
367 | str |
368 | CLRcr(int *cr, const color *c) |
369 | { |
370 | int r = (int) ((*c >> 16) & 0xFF); |
371 | int g = (int) ((*c >> 8) & 0xFF); |
372 | int b = (int) (*c & 0xFF); |
373 | |
374 | *cr = (int) (0.5000f * (float) (r) - 0.4183f * (float) (g) - 0.0816f * (float) (b)) + 128; |
375 | return (MAL_SUCCEED); |
376 | } |
377 | |
378 | str |
379 | CLRcb(int *cb, const color *c) |
380 | { |
381 | int r = (int) ((*c >> 16) & 0xFF); |
382 | int g = (int) ((*c >> 8) & 0xFF); |
383 | int b = (int) (*c & 0xFF); |
384 | |
385 | *cb = (int) (-0.1687f * (float) (r) - 0.3312f * (float) (g) + 0.5000f * (float) (b)) + 128; |
386 | return (MAL_SUCCEED); |
387 | } |
388 | |
389 | str |
390 | CLRcolor(color *c, const char **val) |
391 | { |
392 | size_t len = sizeof(color); |
393 | |
394 | if (color_fromstr(*val, &len, &c, false) < 0) |
395 | throw(MAL, "color.color" , GDK_EXCEPTION); |
396 | return MAL_SUCCEED; |
397 | } |
398 | |