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 
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 
177 char *VuoColor_getSummary(const VuoColor value)
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>
193  <tr>
194  <th>Normalized:</th>
195  <td>%4.03f</td>
196  <td>%4.03f</td>
197  <td>%4.03f</td>
198  <td>%4.03f</td>
199  </tr>
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>
207  <tr>
208  <th>Hex:</th>
209  <td class='left' colspan=4>&nbsp;%s</td>
210  </tr>
211  </table>
212  );
213 
214  VuoText hex = VuoColor_getHex(value, false);
215  VuoLocal(hex);
216  return VuoText_format(t,
217  hex,
218  value.r, value.g, value.b, value.a,
219  (unsigned int)(VuoReal_clamp(value.r,0,1) * 255),
220  (unsigned int)(VuoReal_clamp(value.g,0,1) * 255),
221  (unsigned int)(VuoReal_clamp(value.b,0,1) * 255),
222  (unsigned int)(VuoReal_clamp(value.a,0,1) * 255),
223  hex
224  );
225 }
226 
231 {
232  if (includeAlpha)
233  return VuoText_make(VuoText_format("#%02x%02x%02x%02x",
234  (unsigned int)(VuoReal_clamp(color.r,0,1) * 255),
235  (unsigned int)(VuoReal_clamp(color.g,0,1) * 255),
236  (unsigned int)(VuoReal_clamp(color.b,0,1) * 255),
237  (unsigned int)(VuoReal_clamp(color.a,0,1) * 255)
238  ));
239  else
240  return VuoText_make(VuoText_format("#%02x%02x%02x",
241  (unsigned int)(VuoReal_clamp(color.r,0,1) * 255),
242  (unsigned int)(VuoReal_clamp(color.g,0,1) * 255),
243  (unsigned int)(VuoReal_clamp(color.b,0,1) * 255)
244  ));
245 }
246 
255 VuoColor VuoColor_makeWithHSLA(VuoReal hue, VuoReal saturation, VuoReal luminosity, VuoReal alpha)
256 {
257  // http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
258 
259  float r, g, b;
260 
261  if (saturation < 0.00001)
262  r = g = b = luminosity;
263  else
264  {
265  VuoReal hueWrapped = fmod(hue, 1);
266  float (^hue2rgb)(float p, float q, float t) = ^(float p, float q, float t) {
267  if (t < 0.f) t += 1.f;
268  if (t > 1.f) t -= 1.f;
269  if (t < 1.f/6.f) return p + (q - p) * 6.f * t;
270  if (t < 1.f/2.f) return q;
271  if (t < 2.f/3.f) return p + (q - p) * (2.f/3.f - t) * 6.f;
272  return p;
273  };
274 
275  float l = VuoReal_clamp(luminosity, 0, 1);
276  float s = VuoReal_clamp(saturation, 0, 1);
277  float q = luminosity < 0.5f ? l * (1.f + s) : l + s - l * s;
278  float p = 2.f * l - q;
279  r = hue2rgb(p, q, hueWrapped + 1.f/3.f);
280  g = hue2rgb(p, q, hueWrapped);
281  b = hue2rgb(p, q, hueWrapped - 1.f/3.f);
282  }
283 
284  return VuoColor_makeWithRGBA(r, g, b, alpha);
285 }
286 
293 {
294  // http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
295 
296  VuoReal r, g, b;
297  VuoColor_getRGBA(color, &r, &g, &b, a);
298 
299  float max = fmax(r, fmax(g, b));
300  float min = fmin(r, fmin(g, b));
301  *h = *s = *l = (max + min) / 2.f;
302 
303  if (max == min)
304  {
305  *h = *s = 0;
306  }
307  else
308  {
309  float d = max - min;
310  *s = *l > 0.5f ? d / (2.f - max - min) : d / (max + min);
311  if (max == r)
312  *h = (g - b) / d + (g < b ? 6.f : 0.f);
313  else if (max == g)
314  *h = (b - r) / d + 2.f;
315  else
316  *h = (r - g) / d + 4.f;
317  *h /= 6.f;
318  }
319 }
320 
325 {
326  float max = fmax(color.r, fmax(color.g, color.b));
327  float min = fmin(color.r, fmin(color.g, color.b));
328  return (max + min) / 2.;
329 }
330 
335 {
336  // http://www.rapidtables.com/convert/color/cmyk-to-rgb.htm
337  return VuoColor_makeWithRGBA(
338  (1. - c) * (1. - k),
339  (1. - m) * (1. - k),
340  (1. - y) * (1. - k),
341  a);
342 }
343 
348 {
349  // http://www.rapidtables.com/convert/color/rgb-to-cmyk.htm
350 
351  double r = color.r,
352  g = color.g,
353  b = color.b;
354 
355  *k = 1. - MAX(MAX(r, g), b);
356  if (VuoReal_areEqual(*k, 1))
357  *c = *m = *y = 0;
358  else
359  {
360  *c = (1. - r - *k) / (1. - *k);
361  *m = (1. - g - *k) / (1. - *k);
362  *y = (1. - b - *k) / (1. - *k);
363  }
364  *a = color.a;
365 }
366 
373 {
374  VuoColor result = VuoColor_makeWithRGBA(0,0,0,0);
375  unsigned long colorCount = VuoListGetCount_VuoColor(colors);
376  for (unsigned long i = 1; i <= colorCount; ++i)
377  {
378  VuoColor color = VuoListGetValue_VuoColor(colors, i);
379  result.r += color.r * color.a;
380  result.g += color.g * color.a;
381  result.b += color.b * color.a;
382  result.a += color.a;
383  }
384 
385  if (result.a < 0.00001)
386  return VuoColor_makeWithRGBA(0,0,0,0);
387 
388  result.r /= result.a;
389  result.g /= result.a;
390  result.b /= result.a;
391  result.a /= colorCount;
392  return result;
393 }
394 
403 {
404  size_t itemCount = VuoListGetCount_VuoColor(colors);
405  if (itemCount == 0)
406  return false;
407 
408  VuoColor *itemData = VuoListGetData_VuoColor(colors);
409  for (size_t i = 0; i < itemCount; ++i)
410  if (!VuoColor_isOpaque(itemData[i]))
411  return false;
412 
413  return true;
414 }
415 
424 {
425  VuoColor c = VuoColor_premultiply(color);
426  if (type == VuoThresholdType_Rec601)
427  return c.r * .299 + c.g * .587 + c.b * .114;
428  else if (type == VuoThresholdType_Rec709)
429  return pow(
430  pow(c.r, 2.2) * .2126
431  + pow(c.g, 2.2) * .7152
432  + pow(c.b, 2.2) * .0722,
433  1./2.2);
434  else if (type == VuoThresholdType_Desaturate)
435  return (MAX(c.r, MAX(c.g, c.b)) + MIN(c.r, MIN(c.g, c.b))) / 2.;
436  else if (type == VuoThresholdType_RGBAverage)
437  return (c.r + c.g + c.b) / 3.;
438  else if (type == VuoThresholdType_RGBMaximum)
439  return MAX(c.r, MAX(c.g, c.b));
440  else if (type == VuoThresholdType_RGBMinimum)
441  return MIN(c.r, MIN(c.g, c.b));
442  else if (type == VuoThresholdType_Red)
443  return c.r;
444  else if (type == VuoThresholdType_Green)
445  return c.g;
446  else if (type == VuoThresholdType_Blue)
447  return c.b;
448  else // if (type == VuoThresholdType_Alpha)
449  return c.a;
450 }
451 
455 bool VuoColor_areEqual(const VuoColor value1, const VuoColor value2)
456 {
457  return VuoReal_areEqual(value1.r, value2.r)
458  && VuoReal_areEqual(value1.g, value2.g)
459  && VuoReal_areEqual(value1.b, value2.b)
460  && VuoReal_areEqual(value1.a, value2.a);
461 }
462 
466 bool VuoColor_areEqualWithinTolerance(const VuoColor a, const VuoColor b, const float tolerance)
467 {
468  return fabs(a.r - b.r) <= tolerance
469  && fabs(a.g - b.g) <= tolerance
470  && fabs(a.b - b.b) <= tolerance
471  && fabs(a.a - b.a) <= tolerance;
472 }
473 
477 bool VuoColor_isLessThan(const VuoColor a, const VuoColor b)
478 {
479  if (a.r < b.r) return true;
480  if (a.r > b.r) return false;
481 
482  if (a.g < b.g) return true;
483  if (a.g > b.g) return false;
484 
485  if (a.b < b.b) return true;
486  if (a.b > b.b) return false;
487 
488  if (a.a < b.a) return true;
489 // if (a.a > b.a) return false;
490 
491  return false;
492 }