Vuo  2.4.0
VuoColor.c
Go to the documentation of this file.
1
10#include <math.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include "type.h"
15
16#include "VuoThresholdType.h"
17
19#ifdef VUO_COMPILER
21 "title" : "Color",
22 "description" : "A color, with component values specifying red, green, blue, and alpha (opacity).",
23 "keywords" : [ "rgba" ],
24 "version" : "1.0.0",
25 "dependencies" : [
26 "VuoList_VuoColor",
27 "VuoReal",
28 "VuoText"
29 ]
30 });
31#endif
33
50{
51 VuoColor color = {0,0,0,1};
52
53 json_type t = json_object_get_type(js);
54 if (t == json_type_string)
55 {
56 const char *s = json_object_get_string(js);
57 if (s[0] == '#')
58 {
59 size_t len = strlen(s);
60 if (len == 4) // "#rgb"
61 {
62 long r = VuoInteger_makeFromHexByte(s[1]);
63 long g = VuoInteger_makeFromHexByte(s[2]);
64 long b = VuoInteger_makeFromHexByte(s[3]);
65 color.r = (float)r / 15;
66 color.g = (float)g / 15;
67 color.b = (float)b / 15;
68 color.a = 1;
69 }
70 else if (len == 5) // "#rgba"
71 {
72 long r = VuoInteger_makeFromHexByte(s[1]);
73 long g = VuoInteger_makeFromHexByte(s[2]);
74 long b = VuoInteger_makeFromHexByte(s[3]);
75 long a = VuoInteger_makeFromHexByte(s[4]);
76 color.r = (float)r / 15;
77 color.g = (float)g / 15;
78 color.b = (float)b / 15;
79 color.a = (float)a / 15;
80 }
81 else if (len == 7) // "#rrggbb"
82 {
83 long r = (VuoInteger_makeFromHexByte(s[1]) << 4) + VuoInteger_makeFromHexByte(s[2]);
84 long g = (VuoInteger_makeFromHexByte(s[3]) << 4) + VuoInteger_makeFromHexByte(s[4]);
85 long b = (VuoInteger_makeFromHexByte(s[5]) << 4) + VuoInteger_makeFromHexByte(s[6]);
86 color.r = (float)r / 255;
87 color.g = (float)g / 255;
88 color.b = (float)b / 255;
89 color.a = 1;
90 }
91 else if (len == 9) // "#rrggbbaa"
92 {
93 long r = (VuoInteger_makeFromHexByte(s[1]) << 4) + VuoInteger_makeFromHexByte(s[2]);
94 long g = (VuoInteger_makeFromHexByte(s[3]) << 4) + VuoInteger_makeFromHexByte(s[4]);
95 long b = (VuoInteger_makeFromHexByte(s[5]) << 4) + VuoInteger_makeFromHexByte(s[6]);
96 long a = (VuoInteger_makeFromHexByte(s[7]) << 4) + VuoInteger_makeFromHexByte(s[8]);
97 color.r = (float)r / 255;
98 color.g = (float)g / 255;
99 color.b = (float)b / 255;
100 color.a = (float)a / 255;
101 }
102 }
103 else
104 {
105 // "r,g,b" or "r,g,b,a"
106 color.a = 1;
107 sscanf(s, "%20g, %20g, %20g, %20g", &color.r, &color.g, &color.b, &color.a);
108 }
109 return color;
110 }
111 else if (t == json_type_array)
112 {
113 int len = json_object_array_length(js);
114 if (len >= 1)
115 color.r = json_object_get_double(json_object_array_get_idx(js, 0));
116 if (len >= 2)
117 color.g = json_object_get_double(json_object_array_get_idx(js, 1));
118 if (len >= 3)
119 color.b = json_object_get_double(json_object_array_get_idx(js, 2));
120 if (len >= 4)
121 color.a = json_object_get_double(json_object_array_get_idx(js, 3));
122 else
123 color.a = 1;
124 }
125
126 json_object *o = NULL;
127
128 if (json_object_object_get_ex(js, "r", &o))
129 color.r = VuoReal_makeFromJson(o);
130
131 if (json_object_object_get_ex(js, "g", &o))
132 color.g = VuoReal_makeFromJson(o);
133
134 if (json_object_object_get_ex(js, "b", &o))
135 color.b = VuoReal_makeFromJson(o);
136
137 if (json_object_object_get_ex(js, "a", &o))
138 color.a = VuoReal_makeFromJson(o);
139
140 return color;
141}
142
148{
149 json_object *js = json_object_new_object();
150
151 json_object_object_add(js, "r", VuoReal_getJson(value.r));
152
153 json_object_object_add(js, "g", VuoReal_getJson(value.g));
154
155 json_object_object_add(js, "b", VuoReal_getJson(value.b));
156
157 json_object_object_add(js, "a", VuoReal_getJson(value.a));
158
159 return js;
160}
161
166{
167 return VuoText_format("<span style='background-color:#%02x%02x%02x;'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> %4.02f, %4.02f, %4.02f, %4.02f",
168 (unsigned int)(VuoReal_clamp(value.r,0,1) * 255),
169 (unsigned int)(VuoReal_clamp(value.g,0,1) * 255),
170 (unsigned int)(VuoReal_clamp(value.b,0,1) * 255),
171 value.r, value.g, value.b, value.a);
172}
173
178{
179 const char *t = VUO_STRINGIFY(
180 <style>
181 th,td { text-align: right; }
182 td { font-weight: normal; }
183 .left { text-align: left; }
184 </style>
185 <table cellspacing=6>
186 <tr>
187 <td class='left'><span style='background-color:%s;'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></td>
188 <th>Red</th>
189 <th>Green</th>
190 <th>Blue</th>
191 <th>Alpha</th>
192 </tr>\n
193 <tr>
194 <th>Normalized:</th>
195 <td width=40>%4.03f</td>
196 <td width=40>%4.03f</td>
197 <td width=40>%4.03f</td>
198 <td width=40>%4.03f</td>
199 </tr>\n
200 <tr>
201 <th>8-bit:</th>
202 <td>%d</td>
203 <td>%d</td>
204 <td>%d</td>
205 <td>%d</td>
206 </tr>\n
207 <tr>
208 <th>Hex:</th>
209 <td class='left' colspan=4>&nbsp;<code>%s</code></td>
210 </tr>
211 </table>
212 );
213
214 VuoText hex = VuoColor_getHex(value, false);
215 VuoLocal(hex);
216
217 VuoText hexWithOptionalAlpha = hex;
218 if (value.a < 1)
219 hexWithOptionalAlpha = VuoColor_getHex(value, true);
220 VuoLocal(hexWithOptionalAlpha);
221
222 return VuoText_format(t,
223 hex,
224 value.r, value.g, value.b, value.a,
225 (unsigned int)(VuoReal_clamp(value.r,0,1) * 255),
226 (unsigned int)(VuoReal_clamp(value.g,0,1) * 255),
227 (unsigned int)(VuoReal_clamp(value.b,0,1) * 255),
228 (unsigned int)(VuoReal_clamp(value.a,0,1) * 255),
229 hexWithOptionalAlpha
230 );
231}
232
237{
238 if (includeAlpha)
239 return VuoText_make(VuoText_format("#%02x%02x%02x%02x",
240 (unsigned int)(VuoReal_clamp(color.r,0,1) * 255),
241 (unsigned int)(VuoReal_clamp(color.g,0,1) * 255),
242 (unsigned int)(VuoReal_clamp(color.b,0,1) * 255),
243 (unsigned int)(VuoReal_clamp(color.a,0,1) * 255)
244 ));
245 else
246 return VuoText_make(VuoText_format("#%02x%02x%02x",
247 (unsigned int)(VuoReal_clamp(color.r,0,1) * 255),
248 (unsigned int)(VuoReal_clamp(color.g,0,1) * 255),
249 (unsigned int)(VuoReal_clamp(color.b,0,1) * 255)
250 ));
251}
252
261VuoColor VuoColor_makeWithHSLA(VuoReal hue, VuoReal saturation, VuoReal luminosity, VuoReal alpha)
262{
263 // http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
264
265 float r, g, b;
266
267 if (saturation < 0.00001)
268 r = g = b = luminosity;
269 else
270 {
271 VuoReal hueWrapped = fmod(hue, 1);
272 float (^hue2rgb)(float p, float q, float t) = ^(float p, float q, float t) {
273 if (t < 0.f) t += 1.f;
274 if (t > 1.f) t -= 1.f;
275 if (t < 1.f/6.f) return p + (q - p) * 6.f * t;
276 if (t < 1.f/2.f) return q;
277 if (t < 2.f/3.f) return p + (q - p) * (2.f/3.f - t) * 6.f;
278 return p;
279 };
280
281 float l = VuoReal_clamp(luminosity, 0, 1);
282 float s = VuoReal_clamp(saturation, 0, 1);
283 float q = luminosity < 0.5f ? l * (1.f + s) : l + s - l * s;
284 float p = 2.f * l - q;
285 r = hue2rgb(p, q, hueWrapped + 1.f/3.f);
286 g = hue2rgb(p, q, hueWrapped);
287 b = hue2rgb(p, q, hueWrapped - 1.f/3.f);
288 }
289
290 return VuoColor_makeWithRGBA(r, g, b, alpha);
291}
292
299{
300 // http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
301
302 VuoReal r, g, b;
303 VuoColor_getRGBA(color, &r, &g, &b, a);
304
305 float max = fmax(r, fmax(g, b));
306 float min = fmin(r, fmin(g, b));
307 *h = *s = *l = (max + min) / 2.f;
308
309 if (max == min)
310 {
311 *h = *s = 0;
312 }
313 else
314 {
315 float d = max - min;
316 *s = *l > 0.5f ? d / (2.f - max - min) : d / (max + min);
317 if (max == r)
318 *h = (g - b) / d + (g < b ? 6.f : 0.f);
319 else if (max == g)
320 *h = (b - r) / d + 2.f;
321 else
322 *h = (r - g) / d + 4.f;
323 *h /= 6.f;
324 }
325}
326
331{
332 float max = fmax(color.r, fmax(color.g, color.b));
333 float min = fmin(color.r, fmin(color.g, color.b));
334 return (max + min) / 2.;
335}
336
343{
344 VuoColor result = VuoColor_makeWithRGBA(0,0,0,0);
345 unsigned long colorCount = VuoListGetCount_VuoColor(colors);
346 for (unsigned long i = 1; i <= colorCount; ++i)
347 {
348 VuoColor color = VuoListGetValue_VuoColor(colors, i);
349 result.r += color.r * color.a;
350 result.g += color.g * color.a;
351 result.b += color.b * color.a;
352 result.a += color.a;
353 }
354
355 if (result.a < 0.00001)
356 return VuoColor_makeWithRGBA(0,0,0,0);
357
358 result.r /= result.a;
359 result.g /= result.a;
360 result.b /= result.a;
361 result.a /= colorCount;
362 return result;
363}
364
373{
374 size_t itemCount = VuoListGetCount_VuoColor(colors);
375 if (itemCount == 0)
376 return false;
377
378 VuoColor *itemData = VuoListGetData_VuoColor(colors);
379 for (size_t i = 0; i < itemCount; ++i)
380 if (!VuoColor_isOpaque(itemData[i]))
381 return false;
382
383 return true;
384}
385
394{
396 if (type == VuoThresholdType_Rec601)
397 return c.r * .299 + c.g * .587 + c.b * .114;
398 else if (type == VuoThresholdType_Rec709)
399 return pow(
400 pow(c.r, 2.2) * .2126
401 + pow(c.g, 2.2) * .7152
402 + pow(c.b, 2.2) * .0722,
403 1./2.2);
404 else if (type == VuoThresholdType_Desaturate)
405 return (MAX(c.r, MAX(c.g, c.b)) + MIN(c.r, MIN(c.g, c.b))) / 2.;
406 else if (type == VuoThresholdType_RGBAverage)
407 return (c.r + c.g + c.b) / 3.;
408 else if (type == VuoThresholdType_RGBMaximum)
409 return MAX(c.r, MAX(c.g, c.b));
410 else if (type == VuoThresholdType_RGBMinimum)
411 return MIN(c.r, MIN(c.g, c.b));
412 else if (type == VuoThresholdType_Red)
413 return c.r;
414 else if (type == VuoThresholdType_Green)
415 return c.g;
416 else if (type == VuoThresholdType_Blue)
417 return c.b;
418 else // if (type == VuoThresholdType_Alpha)
419 return c.a;
420}
421
425bool VuoColor_areEqual(const VuoColor value1, const VuoColor value2)
426{
427 return VuoReal_areEqual(value1.r, value2.r)
428 && VuoReal_areEqual(value1.g, value2.g)
429 && VuoReal_areEqual(value1.b, value2.b)
430 && VuoReal_areEqual(value1.a, value2.a);
431}
432
436bool VuoColor_areEqualWithinTolerance(const VuoColor a, const VuoColor b, const float tolerance)
437{
438 return fabs(a.r - b.r) <= tolerance
439 && fabs(a.g - b.g) <= tolerance
440 && fabs(a.b - b.b) <= tolerance
441 && fabs(a.a - b.a) <= tolerance;
442}
443
448{
449 if (a.r < b.r) return true;
450 if (a.r > b.r) return false;
451
452 if (a.g < b.g) return true;
453 if (a.g > b.g) return false;
454
455 if (a.b < b.b) return true;
456 if (a.b > b.b) return false;
457
458 if (a.a < b.a) return true;
459// if (a.a > b.a) return false;
460
461 return false;
462}