Vuo  2.0.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
32 
48 {
49  VuoColor color = {0,0,0,0};
50 
51  json_type t = json_object_get_type(js);
52  if (t == json_type_string)
53  {
54  const char *s = json_object_get_string(js);
55  if (s[0] == '#')
56  {
57  size_t len = strlen(s);
58  if (len == 4) // "#rgb"
59  {
60  long r = VuoInteger_makeFromHexByte(s[1]);
61  long g = VuoInteger_makeFromHexByte(s[2]);
62  long b = VuoInteger_makeFromHexByte(s[3]);
63  color.r = (float)r / 15;
64  color.g = (float)g / 15;
65  color.b = (float)b / 15;
66  color.a = 1;
67  }
68  else if (len == 5) // "#rgba"
69  {
70  long r = VuoInteger_makeFromHexByte(s[1]);
71  long g = VuoInteger_makeFromHexByte(s[2]);
72  long b = VuoInteger_makeFromHexByte(s[3]);
73  long a = VuoInteger_makeFromHexByte(s[4]);
74  color.r = (float)r / 15;
75  color.g = (float)g / 15;
76  color.b = (float)b / 15;
77  color.a = (float)a / 15;
78  }
79  else if (len == 7) // "#rrggbb"
80  {
81  long r = (VuoInteger_makeFromHexByte(s[1]) << 4) + VuoInteger_makeFromHexByte(s[2]);
82  long g = (VuoInteger_makeFromHexByte(s[3]) << 4) + VuoInteger_makeFromHexByte(s[4]);
83  long b = (VuoInteger_makeFromHexByte(s[5]) << 4) + VuoInteger_makeFromHexByte(s[6]);
84  color.r = (float)r / 255;
85  color.g = (float)g / 255;
86  color.b = (float)b / 255;
87  color.a = 1;
88  }
89  else if (len == 9) // "#rrggbbaa"
90  {
91  long r = (VuoInteger_makeFromHexByte(s[1]) << 4) + VuoInteger_makeFromHexByte(s[2]);
92  long g = (VuoInteger_makeFromHexByte(s[3]) << 4) + VuoInteger_makeFromHexByte(s[4]);
93  long b = (VuoInteger_makeFromHexByte(s[5]) << 4) + VuoInteger_makeFromHexByte(s[6]);
94  long a = (VuoInteger_makeFromHexByte(s[7]) << 4) + VuoInteger_makeFromHexByte(s[8]);
95  color.r = (float)r / 255;
96  color.g = (float)g / 255;
97  color.b = (float)b / 255;
98  color.a = (float)a / 255;
99  }
100  }
101  else
102  {
103  // "r,g,b" or "r,g,b,a"
104  color.a = 1;
105  sscanf(s, "%20g, %20g, %20g, %20g", &color.r, &color.g, &color.b, &color.a);
106  }
107  return color;
108  }
109  else if (t == json_type_array)
110  {
111  int len = json_object_array_length(js);
112  if (len >= 1)
113  color.r = json_object_get_double(json_object_array_get_idx(js, 0));
114  if (len >= 2)
115  color.g = json_object_get_double(json_object_array_get_idx(js, 1));
116  if (len >= 3)
117  color.b = json_object_get_double(json_object_array_get_idx(js, 2));
118  if (len >= 4)
119  color.a = json_object_get_double(json_object_array_get_idx(js, 3));
120  else
121  color.a = 1;
122  }
123 
124  json_object *o = NULL;
125 
126  if (json_object_object_get_ex(js, "r", &o))
127  color.r = VuoReal_makeFromJson(o);
128 
129  if (json_object_object_get_ex(js, "g", &o))
130  color.g = VuoReal_makeFromJson(o);
131 
132  if (json_object_object_get_ex(js, "b", &o))
133  color.b = VuoReal_makeFromJson(o);
134 
135  if (json_object_object_get_ex(js, "a", &o))
136  color.a = VuoReal_makeFromJson(o);
137 
138  return color;
139 }
140 
146 {
147  json_object *js = json_object_new_object();
148 
149  json_object_object_add(js, "r", VuoReal_getJson(value.r));
150 
151  json_object_object_add(js, "g", VuoReal_getJson(value.g));
152 
153  json_object_object_add(js, "b", VuoReal_getJson(value.b));
154 
155  json_object_object_add(js, "a", VuoReal_getJson(value.a));
156 
157  return js;
158 }
159 
164 {
165  return VuoText_format("<span style='background-color:#%02x%02x%02x;'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> %4.02f, %4.02f, %4.02f, %4.02f",
166  (unsigned int)(VuoReal_clamp(value.r,0,1) * 255),
167  (unsigned int)(VuoReal_clamp(value.g,0,1) * 255),
168  (unsigned int)(VuoReal_clamp(value.b,0,1) * 255),
169  value.r, value.g, value.b, value.a);
170 }
171 
175 char *VuoColor_getSummary(const VuoColor value)
176 {
177  const char *t = VUO_STRINGIFY(
178  <style>
179  th,td { text-align: right; }
180  td { font-weight: normal; }
181  .left { text-align: left; }
182  </style>
183  <table cellspacing=6>
184  <tr>
185  <td class='left'><span style='background-color:%s;'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></td>
186  <th>Red</th>
187  <th>Green</th>
188  <th>Blue</th>
189  <th>Alpha</th>
190  </tr>
191  <tr>
192  <th>Normalized:</th>
193  <td>%4.03f</td>
194  <td>%4.03f</td>
195  <td>%4.03f</td>
196  <td>%4.03f</td>
197  </tr>
198  <tr>
199  <th>8-bit:</th>
200  <td>%d</td>
201  <td>%d</td>
202  <td>%d</td>
203  <td>%d</td>
204  </tr>
205  <tr>
206  <th>Hex:</th>
207  <td class='left' colspan=4>&nbsp;%s</td>
208  </tr>
209  </table>
210  );
211 
212  VuoText hex = VuoColor_getHex(value, false);
213  VuoLocal(hex);
214  return VuoText_format(t,
215  hex,
216  value.r, value.g, value.b, value.a,
217  (unsigned int)(VuoReal_clamp(value.r,0,1) * 255),
218  (unsigned int)(VuoReal_clamp(value.g,0,1) * 255),
219  (unsigned int)(VuoReal_clamp(value.b,0,1) * 255),
220  (unsigned int)(VuoReal_clamp(value.a,0,1) * 255),
221  hex
222  );
223 }
224 
229 {
230  if (includeAlpha)
231  return VuoText_make(VuoText_format("#%02x%02x%02x%02x",
232  (unsigned int)(VuoReal_clamp(color.r,0,1) * 255),
233  (unsigned int)(VuoReal_clamp(color.g,0,1) * 255),
234  (unsigned int)(VuoReal_clamp(color.b,0,1) * 255),
235  (unsigned int)(VuoReal_clamp(color.a,0,1) * 255)
236  ));
237  else
238  return VuoText_make(VuoText_format("#%02x%02x%02x",
239  (unsigned int)(VuoReal_clamp(color.r,0,1) * 255),
240  (unsigned int)(VuoReal_clamp(color.g,0,1) * 255),
241  (unsigned int)(VuoReal_clamp(color.b,0,1) * 255)
242  ));
243 }
244 
253 VuoColor VuoColor_makeWithHSLA(VuoReal hue, VuoReal saturation, VuoReal luminosity, VuoReal alpha)
254 {
255  // http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
256 
257  float r, g, b;
258 
259  if (saturation < 0.00001)
260  r = g = b = luminosity;
261  else
262  {
263  VuoReal hueWrapped = fmod(hue, 1);
264  float (^hue2rgb)(float p, float q, float t) = ^(float p, float q, float t) {
265  if (t < 0.f) t += 1.f;
266  if (t > 1.f) t -= 1.f;
267  if (t < 1.f/6.f) return p + (q - p) * 6.f * t;
268  if (t < 1.f/2.f) return q;
269  if (t < 2.f/3.f) return p + (q - p) * (2.f/3.f - t) * 6.f;
270  return p;
271  };
272 
273  float l = VuoReal_clamp(luminosity, 0, 1);
274  float s = VuoReal_clamp(saturation, 0, 1);
275  float q = luminosity < 0.5f ? l * (1.f + s) : l + s - l * s;
276  float p = 2.f * l - q;
277  r = hue2rgb(p, q, hueWrapped + 1.f/3.f);
278  g = hue2rgb(p, q, hueWrapped);
279  b = hue2rgb(p, q, hueWrapped - 1.f/3.f);
280  }
281 
282  return VuoColor_makeWithRGBA(r, g, b, alpha);
283 }
284 
291 {
292  // http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
293 
294  VuoReal r, g, b;
295  VuoColor_getRGBA(color, &r, &g, &b, a);
296 
297  float max = fmax(r, fmax(g, b));
298  float min = fmin(r, fmin(g, b));
299  *h = *s = *l = (max + min) / 2.f;
300 
301  if (max == min)
302  {
303  *h = *s = 0;
304  }
305  else
306  {
307  float d = max - min;
308  *s = *l > 0.5f ? d / (2.f - max - min) : d / (max + min);
309  if (max == r)
310  *h = (g - b) / d + (g < b ? 6.f : 0.f);
311  else if (max == g)
312  *h = (b - r) / d + 2.f;
313  else
314  *h = (r - g) / d + 4.f;
315  *h /= 6.f;
316  }
317 }
318 
323 {
324  float max = fmax(color.r, fmax(color.g, color.b));
325  float min = fmin(color.r, fmin(color.g, color.b));
326  return (max + min) / 2.;
327 }
328 
333 {
334  // http://www.rapidtables.com/convert/color/cmyk-to-rgb.htm
335  return VuoColor_makeWithRGBA(
336  (1. - c) * (1. - k),
337  (1. - m) * (1. - k),
338  (1. - y) * (1. - k),
339  a);
340 }
341 
346 {
347  // http://www.rapidtables.com/convert/color/rgb-to-cmyk.htm
348 
349  double r = color.r,
350  g = color.g,
351  b = color.b;
352 
353  *k = 1. - MAX(MAX(r, g), b);
354  if (VuoReal_areEqual(*k, 1))
355  *c = *m = *y = 0;
356  else
357  {
358  *c = (1. - r - *k) / (1. - *k);
359  *m = (1. - g - *k) / (1. - *k);
360  *y = (1. - b - *k) / (1. - *k);
361  }
362  *a = color.a;
363 }
364 
371 {
372  VuoColor result = VuoColor_makeWithRGBA(0,0,0,0);
373  unsigned long colorCount = VuoListGetCount_VuoColor(colors);
374  for (unsigned long i = 1; i <= colorCount; ++i)
375  {
376  VuoColor color = VuoListGetValue_VuoColor(colors, i);
377  result.r += color.r * color.a;
378  result.g += color.g * color.a;
379  result.b += color.b * color.a;
380  result.a += color.a;
381  }
382 
383  if (result.a < 0.00001)
384  return VuoColor_makeWithRGBA(0,0,0,0);
385 
386  result.r /= result.a;
387  result.g /= result.a;
388  result.b /= result.a;
389  result.a /= colorCount;
390  return result;
391 }
392 
401 {
402  size_t itemCount = VuoListGetCount_VuoColor(colors);
403  if (itemCount == 0)
404  return false;
405 
406  VuoColor *itemData = VuoListGetData_VuoColor(colors);
407  for (size_t i = 0; i < itemCount; ++i)
408  if (!VuoColor_isOpaque(itemData[i]))
409  return false;
410 
411  return true;
412 }
413 
422 {
423  VuoColor c = VuoColor_premultiply(color);
424  if (type == VuoThresholdType_Rec601)
425  return c.r * .299 + c.g * .587 + c.b * .114;
426  else if (type == VuoThresholdType_Rec709)
427  return pow(
428  pow(c.r, 2.2) * .2126
429  + pow(c.g, 2.2) * .7152
430  + pow(c.b, 2.2) * .0722,
431  1./2.2);
432  else if (type == VuoThresholdType_Desaturate)
433  return (MAX(c.r, MAX(c.g, c.b)) + MIN(c.r, MIN(c.g, c.b))) / 2.;
434  else if (type == VuoThresholdType_RGBAverage)
435  return (c.r + c.g + c.b) / 3.;
436  else if (type == VuoThresholdType_RGBMaximum)
437  return MAX(c.r, MAX(c.g, c.b));
438  else if (type == VuoThresholdType_RGBMinimum)
439  return MIN(c.r, MIN(c.g, c.b));
440  else if (type == VuoThresholdType_Red)
441  return c.r;
442  else if (type == VuoThresholdType_Green)
443  return c.g;
444  else if (type == VuoThresholdType_Blue)
445  return c.b;
446  else // if (type == VuoThresholdType_Alpha)
447  return c.a;
448 }
449 
453 bool VuoColor_areEqual(const VuoColor value1, const VuoColor value2)
454 {
455  return VuoReal_areEqual(value1.r, value2.r)
456  && VuoReal_areEqual(value1.g, value2.g)
457  && VuoReal_areEqual(value1.b, value2.b)
458  && VuoReal_areEqual(value1.a, value2.a);
459 }
460 
464 bool VuoColor_areEqualWithinTolerance(const VuoColor a, const VuoColor b, const float tolerance)
465 {
466  return fabs(a.r - b.r) <= tolerance
467  && fabs(a.g - b.g) <= tolerance
468  && fabs(a.b - b.b) <= tolerance
469  && fabs(a.a - b.a) <= tolerance;
470 }
471 
475 bool VuoColor_isLessThan(const VuoColor a, const VuoColor b)
476 {
477  if (a.r < b.r) return true;
478  if (a.r > b.r) return false;
479 
480  if (a.g < b.g) return true;
481  if (a.g > b.g) return false;
482 
483  if (a.b < b.b) return true;
484  if (a.b > b.b) return false;
485 
486  if (a.a < b.a) return true;
487 // if (a.a > b.a) return false;
488 
489  return false;
490 }