GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/dye/convertDye.c Lines: 0 166 0.0 %
Date: 2017-05-26 Branches: 0 94 0.0 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
25
#include "dye.h"
26
27
/*
28
** values in these matrices were copied from:
29
** from http://www.cs.rit.edu/~ncs/color/t_convert.html
30
**
31
** from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
32
** these look to be specific to sRGB with D65 white
33
**
34
** NB: for a long time this matrices were wrong, because they
35
** never got transposed at the same time that matrices in Teem
36
** were switched from column-major to row-major ordering
37
*/
38
float dyeRGBtoXYZMatx[9] = {
39
  0.412453f, 0.357580f, 0.180423f,
40
  0.212671f, 0.715160f, 0.072169f,
41
  0.019334f, 0.119193f, 0.950227f};
42
float dyeXYZtoRGBMatx[9] = {
43
  3.240479f, -1.537150f, -0.498535f,
44
  -0.969256f, 1.875992f, 0.041556f,
45
  0.055648f, -0.204043f, 1.057311f};
46
47
/* summing the rows of the RGBtoXYZ matrix to get X_n, Y_n, Z_n */
48
float dyeWhiteXYZ_n[3] = {0.950456f, 1.0f, 1.088754f};
49
/* so  x = X/(X+Y+Z) = 0.312731268 and
50
   and y = Y/(X+Y+Z) = 0..32903287;
51
   then http://en.wikipedia.org/wiki/Illuminant_D65
52
   confirms that this is D65 white */
53
54
/* the u'_n and v'_n which appear in the XYZ -> LUV conversion;
55
   u'_n = 4X_n / (X_n + 15Y_n + 3Z_n)
56
   v'_n = 9Y_n / (X_n + 15Y_n + 3Z_n)
57
*/
58
float dyeWhiteuvp_n[2] = {0.197839f, 0.468342f};
59
60
void
61
dyeRGBtoHSV(float *H, float *S, float *V,
62
            float  R, float  G, float  B) {
63
  float max, min, delta;
64
65
  max = AIR_MAX(R,G);
66
  max = AIR_MAX(B,max);
67
  min = AIR_MIN(R,G);
68
  min = AIR_MIN(B,min);
69
70
  *V = max;
71
  if (max != 0)
72
    *S = (max - min)/max;
73
  else
74
    *S = 0;
75
  if (0 == *S) {
76
    *H = 0;
77
    return;
78
  }
79
  /* else there is hue */
80
  delta = max - min;
81
  if (R == max)
82
    *H = (G - B)/delta;
83
  else if (G == max)
84
    *H = 2 + (B - R)/delta;
85
  else
86
    *H = 4 + (R - G)/delta;
87
  *H /= 6;
88
  if (*H < 0)
89
    *H += 1;
90
  return;
91
}
92
93
/*
94
******** dyeHSVtoRGB()
95
**
96
** conversion from HSV single hexcone to RGB
97
**
98
** input and ouput are all floats in interval [0,1]
99
** DOES NO RANGE CHECKING WHATSOEVER
100
**
101
** From Foley + vanDam, 2nd Ed., p. 593
102
*/
103
void
104
dyeHSVtoRGB(float *R, float *G, float *B,
105
            float  H, float  S, float  V) {
106
  float min, fract, vsf, mid1, mid2;
107
  int sextant;
108
109
  if (0 == S) {
110
    *R = *G = *B = V;
111
    return;
112
  }
113
  /* else there is hue */
114
  if (1 == H)
115
    H = 0;
116
  H *= 6;
117
  sextant = (int) floor(H);
118
  fract = H - sextant;
119
  vsf = V*S*fract;
120
  min = V*(1 - S);
121
  mid1 = min + vsf;
122
  mid2 = V - vsf;
123
  switch (sextant) {
124
  case 0: { *R = V;    *G = mid1; *B = min;  break; }
125
  case 1: { *R = mid2; *G = V;    *B = min;  break; }
126
  case 2: { *R = min;  *G = V;    *B = mid1; break; }
127
  case 3: { *R = min;  *G = mid2; *B = V;    break; }
128
  case 4: { *R = mid1; *G = min;  *B = V;    break; }
129
  case 5: { *R = V;    *G = min;  *B = mid2; break; }
130
  }
131
}
132
133
/*
134
******** dyeRGBtoHSL()
135
**
136
** converts from RGB to HSL double hexcone
137
** L: "lightness" = (max(R,G,B)+min(R,G,B))/2
138
** note that saturation (S) is different than the saturation in HSV
139
** hue (H) is the same in HSL and HSV
140
**
141
** r,g,b input and *h,*s,*l output are all floats in [0, 1]
142
** DOES NO RANGE CHECKING WHATSOEVER
143
**
144
** From Foley + vanDam, 2nd Ed., p. 595
145
*/
146
void
147
dyeRGBtoHSL(float *H, float *S, float *L,
148
            float  R, float  G, float  B) {
149
  float min, max, lev, delta;
150
151
  max = AIR_MAX(R,G);
152
  max = AIR_MAX(max,B);
153
  min = AIR_MIN(R,G);
154
  min = AIR_MIN(min,B);
155
156
  *L = lev = (max + min)/2.0f;
157
  if (max == min) {
158
    *S = 0;
159
    *H = 0;  /* actually, undefined */
160
    return;
161
  }
162
  /* else there is hue */
163
  delta = max - min;
164
  if (lev <= 0.5)
165
    *S = delta/(max + min);
166
  else
167
    *S = delta/(2-(max + min));
168
  if (R == max)
169
    *H = (G - B)/delta;
170
  else if (G == max)
171
    *H = 2 + (B - R)/delta;
172
  else
173
    *H = 4 + (R - G)/delta;
174
  *H /= 6;
175
  if (*H < 0)
176
    *H += 1;
177
  return;
178
}
179
180
/*
181
******** dyeHSLtoRGB()
182
**
183
** converts from HSL double hexcone back to RGB
184
**
185
** input and ouput are all floats in interval [0,1]
186
** DOES NO RANGE CHECKING WHATSOEVER
187
**
188
** From Foley + vanDam, 2nd Ed., p. 596
189
*/
190
void
191
dyeHSLtoRGB(float *R, float *G, float *B,
192
            float  H, float  S, float  L) {
193
  float m1, m2, fract, mid1, mid2;
194
  int sextant;
195
196
  if (S == 0) {
197
    *R = *G = *B = L;
198
    return;
199
  }
200
  /* else there is hue */
201
  if (L <= 0.5)
202
    m2 = L*(1+S);  /* the book says L*(L+S) which is ?? wrong ?? */
203
  else
204
    m2 = L + S - L*S;
205
  m1 = 2*L - m2;
206
  if (1 == H)
207
    H = 0;
208
  H *= 6;
209
  sextant = (int) floor(H);
210
  fract = H - sextant;
211
  mid1 = m1 + fract*(m2 - m1);
212
  mid2 = m2 + fract*(m1 - m2);
213
  /* compared to HSVtoRGB: V -> m2, min -> m1 */
214
  switch (sextant) {
215
  case 0: { *R = m2;   *G = mid1; *B = m1;   break; }
216
  case 1: { *R = mid2; *G = m2;   *B = m1;   break; }
217
  case 2: { *R = m1;   *G = m2;   *B = mid1; break; }
218
  case 3: { *R = m1;   *G = mid2; *B = m2;   break; }
219
  case 4: { *R = mid1; *G = m1;   *B = m2;   break; }
220
  case 5: { *R = m2;   *G = m1;   *B = mid2; break; }
221
  }
222
}
223
224
void
225
dyeRGBtoXYZ(float *X, float *Y, float *Z,
226
            float  R, float  G, float  B) {
227
  float in[3], out[3];
228
229
  ELL_3V_SET(in, R, G, B);
230
  ELL_3MV_MUL(out, dyeRGBtoXYZMatx, in);
231
  ELL_3V_GET(*X, *Y, *Z, out);
232
  return;
233
}
234
235
void
236
dyeXYZtoRGB(float *R, float *G, float *B,
237
            float  X, float  Y, float  Z) {
238
  float in[3], out[3];
239
240
  ELL_3V_SET(in, X, Y, Z);
241
  ELL_3MV_MUL(out, dyeXYZtoRGBMatx, in);
242
  ELL_3V_GET(*R, *G, *B, out);
243
  return;
244
}
245
246
float
247
dyeLcbrt(float t) {
248
  return AIR_CAST(float, (t > 0.008856
249
                          ? airCbrt(t)
250
                          : 7.787*t + 16.0/116.0));
251
}
252
253
float
254
dyeLcubed(float t) {
255
  return(t > 0.206893 ? t*t*t : (t - 16.0f/116.0f)/7.787f);
256
}
257
258
void
259
dyeXYZtoLAB(float *L, float *A, float *B,
260
            float  X, float  Y, float  Z) {
261
  float Xnorm, Ynorm, Znorm;
262
263
  Xnorm = X/dyeWhiteXYZ_n[0];
264
  Ynorm = Y/dyeWhiteXYZ_n[1];
265
  Znorm = Z/dyeWhiteXYZ_n[2];
266
  *L = 116.0f*dyeLcbrt(Ynorm) - 16.0f;
267
  *A = 500.0f*(dyeLcbrt(Xnorm) - dyeLcbrt(Ynorm));
268
  *B = 200.0f*(dyeLcbrt(Ynorm) - dyeLcbrt(Znorm));
269
}
270
271
void
272
dyeXYZtoLUV(float *L, float *U, float *V,
273
            float  X, float  Y, float  Z) {
274
  float Ynorm, up, vp;
275
276
  Ynorm = Y/dyeWhiteXYZ_n[1];
277
  *L = 116.0f*dyeLcbrt(Ynorm) - 16.0f;
278
  up = 4.0f*X/(X + 15.0f*Y + 3.0f*Z);
279
  vp = 9.0f*Y/(X + 15.0f*Y + 3.0f*Z);
280
  *U = 13.0f*(*L)*(up - dyeWhiteuvp_n[0]);
281
  *V = 13.0f*(*L)*(vp - dyeWhiteuvp_n[1]);
282
}
283
284
void
285
dyeLABtoXYZ(float *X, float *Y, float *Z,
286
            float  L, float  A, float  B) {
287
  float YnormCbrt;
288
289
  YnormCbrt = (16 + L)/116;
290
  *X = dyeWhiteXYZ_n[0]*dyeLcubed(A/500 + YnormCbrt);
291
  *Y = dyeWhiteXYZ_n[1]*dyeLcubed(YnormCbrt);
292
  *Z = dyeWhiteXYZ_n[2]*dyeLcubed(YnormCbrt - B/200);
293
  return;
294
}
295
296
void
297
dyeLUVtoXYZ(float *X, float *Y, float *Z,
298
            float  L, float  U, float  V) {
299
  float up, vp, YnormCbrt;
300
301
  YnormCbrt = (16 + L)/116;
302
  up = U/(13*L) + dyeWhiteuvp_n[0];
303
  vp = V/(13*L) + dyeWhiteuvp_n[1];
304
  *Y = dyeWhiteXYZ_n[1]*dyeLcubed(YnormCbrt);
305
  *X = -9*(*Y)*up/((up - 4)*vp - up*vp);
306
  *Z = (9*(*Y) - 15*vp*(*Y) - vp*(*X))/(3*vp);
307
  return;
308
}
309
310
void
311
dyeLABtoLCH(float *Lp, float *C, float *H,
312
            float  L, float  A, float  B) {
313
314
  *Lp = L;
315
  *C = sqrt(A*A + B*B);
316
  *H = atan2(B, A)/(2*AIR_PI) + 0.5;
317
}
318
319
void
320
dyeLCHtoLAB(float *Lp, float *A, float *B,
321
            float  L, float  C, float  H) {
322
  float phi = (H*2 - 1)*AIR_PI;
323
  *Lp = L;
324
  *A = C*cos(phi);
325
  *B = C*sin(phi);
326
}
327
328
void
329
dyeXYZtoLCH(float *_L, float *C, float *H,
330
            float  X, float  Y, float  Z) {
331
  float L, A, B;
332
  dyeXYZtoLAB(&L, &A, &B, X, Y, Z);
333
  dyeLABtoLCH(_L, C, H, L, A, B);
334
}
335
void
336
dyeLCHtoXYZ(float *X, float *Y, float *Z,
337
            float _L, float  C, float  H) {
338
  float L, A, B;
339
  dyeLCHtoLAB(&L, &A, &B, _L, C, H);
340
  dyeLABtoXYZ(X, Y, Z, L, A, B);
341
}
342
343
void
344
dyeIdentity(float *A, float *B, float *C,
345
            float  a, float  b, float  c) {
346
  *A = a;
347
  *B = b;
348
  *C = c;
349
  return;
350
}
351
352
dyeConverter dyeSimpleConvert[DYE_MAX_SPACE+1][DYE_MAX_SPACE+1] =
353
{
354
  {NULL,          NULL,          NULL,          NULL,          NULL,          NULL,          NULL,          NULL},
355
  {NULL,          dyeIdentity,   NULL,          dyeHSVtoRGB,   NULL,          NULL,          NULL,          NULL},
356
  {NULL,          NULL,          dyeIdentity,   dyeHSLtoRGB,   NULL,          NULL,          NULL,          NULL},
357
  {NULL,          dyeRGBtoHSV,   dyeRGBtoHSL,   dyeIdentity,   dyeRGBtoXYZ,   NULL,          NULL,          NULL},
358
  {NULL,          NULL,          NULL,          dyeXYZtoRGB,   dyeIdentity,   dyeXYZtoLAB,   dyeXYZtoLUV,   dyeXYZtoLCH},
359
  {NULL,          NULL,          NULL,          NULL,          dyeLABtoXYZ,   dyeIdentity,   NULL,          dyeLABtoLCH},
360
  {NULL,          NULL,          NULL,          NULL,          dyeLUVtoXYZ,   NULL,          dyeIdentity,   NULL},
361
  {NULL,          NULL,          NULL,          NULL,          dyeLCHtoXYZ,   dyeLCHtoLAB,   NULL,          dyeIdentity},
362
  };
363
364
/*
365
******** dyeConvert()
366
**
367
** master color conversion function.  Can convert between any two
368
** types by recursive calls and calls to the simple converters above.
369
*/
370
int
371
dyeConvert(dyeColor *col, int outSpace) {
372
  static const char me[] = "dyeConvert";
373
  float i0, i1, i2, o0, o1, o2;
374
  dyeConverter simple;
375
  int inSpace, E;
376
377
  E = 0;
378
  if (!col) {
379
    biffAddf(DYE, "%s: got NULL pointer", me);
380
    return 1;
381
  }
382
  inSpace = dyeColorGet(&i0, &i1, &i2, col);
383
  if (!DYE_VALID_SPACE(inSpace)) {
384
    biffAddf(DYE, "%s: invalid input space #%d\n", me, inSpace);
385
    return 1;
386
  }
387
  if (!DYE_VALID_SPACE(outSpace)) {
388
    biffAddf(DYE, "%s: invalid output space #%d\n", me, outSpace);
389
    return 1;
390
  }
391
392
  if ( (simple = dyeSimpleConvert[inSpace][outSpace]) ) {
393
    (*simple)(&o0, &o1, &o2, i0, i1, i2);
394
    dyeColorSet(col, outSpace, o0, o1, o2);
395
  }
396
  else {
397
    /* we have some work to do . . . */
398
    if (inSpace < dyeSpaceRGB && outSpace < dyeSpaceRGB) {
399
      /* its an easy HSV <-- RGB --> HSL conversion */
400
      if (!E) E |= dyeConvert(col, dyeSpaceRGB);
401
      if (!E) E |= dyeConvert(col, outSpace);
402
    }
403
    else if (inSpace > dyeSpaceXYZ && outSpace > dyeSpaceXYZ) {
404
      /* its an easy conversion among XYZ, LAB, LUV, LCH */
405
      if (!E) E |= dyeConvert(col, dyeSpaceXYZ);
406
      if (!E) E |= dyeConvert(col, outSpace);
407
    }
408
    else {
409
      /* the start and end spaces are at different stages */
410
      if (inSpace < outSpace) {
411
        /* we are going towards higher stages */
412
        if (inSpace < dyeSpaceRGB) {
413
          if (!E) E |= dyeConvert(col, dyeSpaceRGB);
414
          if (!E) E |= dyeConvert(col, outSpace);
415
        }
416
        else if (inSpace == dyeSpaceRGB) {
417
          if (!E) E |= dyeConvert(col, dyeSpaceXYZ);
418
          if (!E) E |= dyeConvert(col, outSpace);
419
        }
420
        else {
421
          biffAddf(DYE, "%s: CONFUSED! can't go %s -> %s\n",
422
                   me, dyeSpaceToStr[inSpace], dyeSpaceToStr[outSpace]);
423
          E = 1;
424
        }
425
      }
426
      else {
427
        /* we are going towards lower stages */
428
        if (outSpace < dyeSpaceRGB) {
429
          if (!E) E |= dyeConvert(col, dyeSpaceRGB);
430
          if (!E) E |= dyeConvert(col, outSpace);
431
        }
432
        else if (outSpace == dyeSpaceRGB) {
433
          if (!E) E |= dyeConvert(col, dyeSpaceXYZ);
434
          if (!E) E |= dyeConvert(col, dyeSpaceRGB);
435
        }
436
        else {
437
          biffAddf(DYE, "%s: CONFUSED! can't go %s -> %s\n",
438
                   me, dyeSpaceToStr[inSpace], dyeSpaceToStr[outSpace]);
439
          E = 1;
440
        }
441
      }
442
    }
443
  }
444
445
  return E;
446
}