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
43static int
44CLRhextoint(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
67ssize_t
68color_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
109ssize_t
110color_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
137str
138CLRstr(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
149str
150CLRrgb(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
156str
157CLRred(int *r, const color *c)
158{
159 *r = (int) ((*c >> 16) & 0xFF);
160 return (MAL_SUCCEED);
161}
162
163str
164CLRgreen(int *g, const color *c)
165{
166 *g = (int) ((*c >> 8) & 0xFF);
167 return (MAL_SUCCEED);
168}
169
170str
171CLRblue(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
185static void
186color_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
219str
220CLRhsv(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
273str
274CLRhue(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
283str
284CLRhueInt(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
293str
294CLRsaturation(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
303str
304CLRsaturationInt(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
313str
314CLRvalue(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
323str
324CLRvalueInt(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
338str
339CLRycc(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
355str
356CLRluminance(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
367str
368CLRcr(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
378str
379CLRcb(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
389str
390CLRcolor(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