1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.color;
18
19 public final class ColorConversions {
20
21
22
23
24
25
26 private static final double REF_Y = 100.000;
27
28
29
30
31
32 private static final double XYZ_m = 7.787037;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 public static ColorDin99Lab convertCieLabToDin99oLab(final ColorCieLab cie) {
130 return convertCieLabToDin99oLab(cie.l, cie.a, cie.b);
131 }
132
133
134
135
136
137
138
139
140
141
142
143 public static ColorDin99Lab convertCieLabToDin99oLab(final double l, final double a, final double b) {
144 final double kE = 1.0;
145 final double kCH = 1.0;
146 final double fac1 = 100.0 / Math.log(139.0 / 100.0);
147 final double ang = Math.toRadians(26.0);
148
149 final double l99o = fac1 / kE * Math.log(1 + 0.0039 * l);
150 double a99o = 0.0;
151 double b99o = 0.0;
152 if (a != 0.0 || b != 0.0) {
153 final double eo = a * Math.cos(ang) + b * Math.sin(ang);
154 final double fo = 0.83 * (b * Math.cos(ang) - a * Math.sin(ang));
155 final double Go = Math.sqrt(eo * eo + fo * fo);
156 final double C99o = Math.log(1.0 + 0.075 * Go) / (0.0435 * kCH * kE);
157 final double heofo = Math.atan2(fo, eo);
158 final double h99o = heofo + ang;
159 a99o = C99o * Math.cos(h99o);
160 b99o = C99o * Math.sin(h99o);
161 }
162 return new ColorDin99Lab(l99o, a99o, b99o);
163 }
164
165 public static ColorXyz convertCieLabToXyz(final ColorCieLab cielab) {
166 return convertCieLabToXyz(cielab.l, cielab.a, cielab.b);
167 }
168
169 public static ColorXyz convertCieLabToXyz(final double l, final double a, final double b) {
170 double varY = (l + 16) / 116.0;
171 double varX = a / 500 + varY;
172 double varZ = varY - b / 200.0;
173
174 varY = unPivotXyz(varY);
175 varX = unPivotXyz(varX);
176 varZ = unPivotXyz(varZ);
177
178 final double x = REF_X * varX;
179
180 final double y = REF_Y * varY;
181 final double z = REF_Z * varZ;
182
183 return new ColorXyz(x, y, z);
184 }
185
186 public static ColorCieLab convertCieLchToCieLab(final ColorCieLch cielch) {
187 return convertCieLchToCieLab(cielch.l, cielch.c, cielch.h);
188 }
189
190 public static ColorCieLab convertCieLchToCieLab(final double l, final double c, final double h) {
191
192
193
194 final double a = Math.cos(degree2radian(h)) * c;
195 final double b = Math.sin(degree2radian(h)) * c;
196
197 return new ColorCieLab(l, a, b);
198 }
199
200 public static ColorXyz convertCieLuvToXyz(final ColorCieLuv cielch) {
201 return convertCieLuvToXyz(cielch.l, cielch.u, cielch.v);
202 }
203
204 public static ColorXyz convertCieLuvToXyz(final double l, final double u, final double v) {
205
206
207 double varY = (l + 16) / 116.0;
208 varY = unPivotXyz(varY);
209
210 final double refU = 4 * REF_X / (REF_X + 15 * REF_Y + 3 * REF_Z);
211 final double refV = 9 * REF_Y / (REF_X + 15 * REF_Y + 3 * REF_Z);
212 final double varU = u / (13 * l) + refU;
213 final double varV = v / (13 * l) + refV;
214
215 final double y = varY * 100;
216 final double x = -(9 * y * varU) / ((varU - 4) * varV - varU * varV);
217 final double z = (9 * y - 15 * varV * y - varV * x) / (3 * varV);
218
219 return new ColorXyz(x, y, z);
220 }
221
222 public static ColorCmy convertCmykToCmy(final ColorCmyk cmyk) {
223 return convertCmykToCmy(cmyk.c, cmyk.m, cmyk.y, cmyk.k);
224 }
225
226 public static ColorCmy convertCmykToCmy(double c, double m, double y, final double k) {
227
228
229 c = c * (1 - k) + k;
230 m = m * (1 - k) + k;
231 y = y * (1 - k) + k;
232
233 return new ColorCmy(c, m, y);
234 }
235
236 public static int convertCmykToRgb(final int c, final int m, final int y, final int k) {
237 final double C = c / 255.0;
238 final double M = m / 255.0;
239 final double Y = y / 255.0;
240 final double K = k / 255.0;
241
242 return convertCmyToRgb(convertCmykToCmy(C, M, Y, K));
243 }
244
245 public static int convertCmykToRgbAdobe(final int sc, final int sm, final int sy, final int sk) {
246 final int red = 255 - (sc + sk);
247 final int green = 255 - (sm + sk);
248 final int blue = 255 - (sy + sk);
249
250 return convertRgbToRgb(red, green, blue);
251 }
252
253 public static ColorCmyk convertCmyToCmyk(final ColorCmy cmy) {
254
255
256 double c = cmy.c;
257 double m = cmy.m;
258 double y = cmy.y;
259
260 double varK = 1.0;
261
262 if (c < varK) {
263 varK = c;
264 }
265 if (m < varK) {
266 varK = m;
267 }
268 if (y < varK) {
269 varK = y;
270 }
271 if (varK == 1) {
272 c = 0;
273 m = 0;
274 y = 0;
275 } else {
276 c = (c - varK) / (1 - varK);
277 m = (m - varK) / (1 - varK);
278 y = (y - varK) / (1 - varK);
279 }
280 return new ColorCmyk(c, m, y, varK);
281 }
282
283 public static int convertCmyToRgb(final ColorCmy cmy) {
284
285
286
287
288
289
290
291
292
293 final double r = (1 - cmy.c) * 255.0;
294 final double g = (1 - cmy.m) * 255.0;
295 final double b = (1 - cmy.y) * 255.0;
296
297 return convertRgbToRgb(r, g, b);
298 }
299
300 public static ColorCieLab convertDin99bLabToCieLab(final ColorDin99Lab dinb) {
301 return convertDin99bLabToCieLab(dinb.l99, dinb.a99, dinb.b99);
302 }
303
304 public static ColorCieLab convertDin99bLabToCieLab(final double L99b, final double a99b, final double b99b) {
305 final double kE = 1.0;
306 final double kCH = 1.0;
307 final double fac1 = 100.0 / Math.log(129.0 / 50.0);
308 final double ang = Math.toRadians(16.0);
309
310 final double hef = Math.atan2(b99b, a99b);
311 final double c = Math.sqrt(a99b * a99b + b99b * b99b);
312 final double g = (Math.exp(0.045 * c * kCH * kE) - 1.0) / 0.045;
313 final double e = g * Math.cos(hef);
314 final double f = g * Math.sin(hef) / 0.7;
315
316 final double l = (Math.exp(L99b * kE / fac1) - 1.) / 0.0158;
317 final double a = e * Math.cos(ang) - f * Math.sin(ang);
318 final double b = e * Math.sin(ang) + f * Math.cos(ang);
319 return new ColorCieLab(l, a, b);
320 }
321
322
323
324
325
326
327
328
329
330 public static ColorCieLab convertDin99oLabToCieLab(final ColorDin99Lab dino) {
331 return convertDin99oLabToCieLab(dino.l99, dino.a99, dino.b99);
332 }
333
334
335
336
337
338
339
340
341
342
343
344 public static ColorCieLab convertDin99oLabToCieLab(final double l99o, final double a99o, final double b99o) {
345 final double kE = 1.0;
346 final double kCH = 1.0;
347 final double fac1 = 100.0 / Math.log(139.0 / 100.0);
348 final double ang = Math.toRadians(26.0);
349
350 final double l = (Math.exp(l99o * kE / fac1) - 1.0) / 0.0039;
351
352 final double h99ef = Math.atan2(b99o, a99o);
353
354 final double heofo = h99ef - ang;
355
356 final double c99 = Math.sqrt(a99o * a99o + b99o * b99o);
357 final double g = (Math.exp(0.0435 * kE * kCH * c99) - 1.0) / 0.075;
358 final double e = g * Math.cos(heofo);
359 final double f = g * Math.sin(heofo);
360
361 final double a = e * Math.cos(ang) - f / 0.83 * Math.sin(ang);
362 final double b = e * Math.sin(ang) + f / 0.83 * Math.cos(ang);
363
364 return new ColorCieLab(l, a, b);
365 }
366
367 public static int convertHslToRgb(final ColorHsl hsl) {
368 return convertHslToRgb(hsl.h, hsl.s, hsl.l);
369 }
370
371 public static int convertHslToRgb(final double h, final double s, final double l) {
372 double r, g, b;
373
374 if (s == 0) {
375
376 r = l * 255;
377 g = l * 255;
378 b = l * 255;
379 } else {
380 double var2;
381
382 if (l < 0.5) {
383 var2 = l * (1 + s);
384 } else {
385 var2 = l + s - s * l;
386 }
387
388 final double var1 = 2 * l - var2;
389
390 r = 255 * convertHueToRgb(var1, var2, h + 1 / 3.0);
391 g = 255 * convertHueToRgb(var1, var2, h);
392 b = 255 * convertHueToRgb(var1, var2, h - 1 / 3.0);
393 }
394
395 return convertRgbToRgb(r, g, b);
396 }
397
398 public static int convertHsvToRgb(final ColorHsv HSV) {
399 return convertHsvToRgb(HSV.h, HSV.s, HSV.v);
400 }
401
402 public static int convertHsvToRgb(final double h, final double s, final double v) {
403 double r, g, b;
404
405 if (s == 0) {
406
407 r = v * 255;
408 g = v * 255;
409 b = v * 255;
410 } else {
411 double varH = h * 6;
412 if (varH == 6) {
413 varH = 0;
414 }
415 final double varI = Math.floor(varH);
416 final double var1 = v * (1 - s);
417 final double var2 = v * (1 - s * (varH - varI));
418 final double var3 = v * (1 - s * (1 - (varH - varI)));
419
420 double varR, varG, varB;
421
422 if (varI == 0) {
423 varR = v;
424 varG = var3;
425 varB = var1;
426 } else if (varI == 1) {
427 varR = var2;
428 varG = v;
429 varB = var1;
430 } else if (varI == 2) {
431 varR = var1;
432 varG = v;
433 varB = var3;
434 } else if (varI == 3) {
435 varR = var1;
436 varG = var2;
437 varB = v;
438 } else if (varI == 4) {
439 varR = var3;
440 varG = var1;
441 varB = v;
442 } else {
443 varR = v;
444 varG = var1;
445 varB = var2;
446 }
447
448 r = varR * 255;
449 g = varG * 255;
450 b = varB * 255;
451 }
452
453 return convertRgbToRgb(r, g, b);
454 }
455
456 private static double convertHueToRgb(final double v1, final double v2, double vH) {
457 if (vH < 0) {
458 vH += 1;
459 }
460 if (vH > 1) {
461 vH -= 1;
462 }
463 if (6 * vH < 1) {
464 return v1 + (v2 - v1) * 6 * vH;
465 }
466 if (2 * vH < 1) {
467 return v2;
468 }
469 if (3 * vH < 2) {
470 return v1 + (v2 - v1) * (2 / 3.0 - vH) * 6;
471 }
472 return v1;
473 }
474
475 public static ColorXyz convertHunterLabToXyz(final ColorHunterLab cielab) {
476 return convertHunterLabToXyz(cielab.l, cielab.a, cielab.b);
477 }
478
479 public static ColorXyz convertHunterLabToXyz(final double l, final double a, final double b) {
480 final double varY = l / 10;
481 final double varX = a / 17.5 * l / 10;
482 final double varZ = b / 7 * l / 10;
483
484 final double y = Math.pow(varY, 2);
485 final double x = (varX + y) / 1.02;
486 final double z = -(varZ - y) / 0.847;
487
488 return new ColorXyz(x, y, z);
489 }
490
491 public static ColorCmy convertRgbToCmy(final int rgb) {
492 final int r = 0xff & rgb >> 16;
493 final int g = 0xff & rgb >> 8;
494 final int b = 0xff & rgb >> 0;
495
496
497
498
499 final double c = 1 - r / 255.0;
500 final double m = 1 - g / 255.0;
501 final double y = 1 - b / 255.0;
502
503 return new ColorCmy(c, m, y);
504 }
505
506 public static ColorHsl convertRgbToHsl(final int rgb) {
507
508 final int r = 0xff & rgb >> 16;
509 final int g = 0xff & rgb >> 8;
510 final int b = 0xff & rgb >> 0;
511
512 final double varR = r / 255.0;
513 final double varG = g / 255.0;
514 final double varB = b / 255.0;
515
516 final double varMin = Math.min(varR, Math.min(varG, varB));
517
518 double varMax;
519 boolean maxIsR = false;
520 boolean maxIsG = false;
521 if (varR >= varG && varR >= varB) {
522 varMax = varR;
523 maxIsR = true;
524 } else if (varG > varB) {
525 varMax = varG;
526 maxIsG = true;
527 } else {
528 varMax = varB;
529 }
530 final double delMax = varMax - varMin;
531
532 final double l = (varMax + varMin) / 2.0;
533
534 double h, s;
535
536 if (delMax == 0) {
537
538
539 h = 0;
540 s = 0;
541 } else {
542
543
544
545
546 if (l < 0.5) {
547 s = delMax / (varMax + varMin);
548 } else {
549 s = delMax / (2 - varMax - varMin);
550 }
551
552
553
554 final double delR = ((varMax - varR) / 6 + delMax / 2) / delMax;
555 final double delG = ((varMax - varG) / 6 + delMax / 2) / delMax;
556 final double delB = ((varMax - varB) / 6 + delMax / 2) / delMax;
557
558 if (maxIsR) {
559 h = delB - delG;
560 } else if (maxIsG) {
561 h = 1 / 3.0 + delR - delB;
562 } else {
563 h = 2 / 3.0 + delG - delR;
564 }
565
566
567
568 if (h < 0) {
569 h += 1;
570 }
571 if (h > 1) {
572 h -= 1;
573 }
574
575
576 }
577
578 return new ColorHsl(h, s, l);
579 }
580
581 public static ColorHsv convertRgbToHsv(final int rgb) {
582 final int r = 0xff & rgb >> 16;
583 final int g = 0xff & rgb >> 8;
584 final int b = 0xff & rgb >> 0;
585
586 final double varR = r / 255.0;
587 final double varG = g / 255.0;
588 final double varB = b / 255.0;
589
590 final double varMin = Math.min(varR, Math.min(varG, varB));
591
592 boolean maxIsR = false;
593 boolean maxIsG = false;
594 double varMax;
595 if (varR >= varG && varR >= varB) {
596 varMax = varR;
597 maxIsR = true;
598 } else if (varG > varB) {
599 varMax = varG;
600 maxIsG = true;
601 } else {
602 varMax = varB;
603 }
604 final double delMax = varMax - varMin;
605
606 final double v = varMax;
607
608 double h, s;
609 if (delMax == 0) {
610
611 h = 0;
612 s = 0;
613 } else {
614
615 s = delMax / varMax;
616
617 final double delR = ((varMax - varR) / 6 + delMax / 2) / delMax;
618 final double delG = ((varMax - varG) / 6 + delMax / 2) / delMax;
619 final double delB = ((varMax - varB) / 6 + delMax / 2) / delMax;
620
621 if (maxIsR) {
622 h = delB - delG;
623 } else if (maxIsG) {
624 h = 1 / 3.0 + delR - delB;
625 } else {
626 h = 2 / 3.0 + delG - delR;
627 }
628
629 if (h < 0) {
630 h += 1;
631 }
632 if (h > 1) {
633 h -= 1;
634 }
635 }
636
637 return new ColorHsv(h, s, v);
638 }
639
640 private static int convertRgbToRgb(final double r, final double g, final double b) {
641 int red = (int) Math.round(r);
642 int green = (int) Math.round(g);
643 int blue = (int) Math.round(b);
644
645 red = Math.min(255, Math.max(0, red));
646 green = Math.min(255, Math.max(0, green));
647 blue = Math.min(255, Math.max(0, blue));
648
649 final int alpha = 0xff;
650
651 return alpha << 24 | red << 16 | green << 8 | blue << 0;
652 }
653
654 private static int convertRgbToRgb(int red, int green, int blue) {
655 red = Math.min(255, Math.max(0, red));
656 green = Math.min(255, Math.max(0, green));
657 blue = Math.min(255, Math.max(0, blue));
658
659 final int alpha = 0xff;
660
661 return alpha << 24 | red << 16 | green << 8 | blue << 0;
662 }
663
664
665
666 public static ColorXyz convertRgbToXyz(final int rgb) {
667 final int r = 0xff & rgb >> 16;
668 final int g = 0xff & rgb >> 8;
669 final int b = 0xff & rgb >> 0;
670
671 double varR = r / 255.0;
672 double varG = g / 255.0;
673 double varB = b / 255.0;
674
675
676 varR = unPivotRgb(varR);
677 varG = unPivotRgb(varG);
678 varB = unPivotRgb(varB);
679
680 varR *= 100;
681 varG *= 100;
682 varB *= 100;
683
684
685
686 final double X = varR * 0.4124564 + varG * 0.3575761 + varB * 0.1804375;
687 final double Y = varR * 0.2126729 + varG * 0.7151522 + varB * 0.0721750;
688 final double Z = varR * 0.0193339 + varG * 0.1191920 + varB * 0.9503041;
689
690
691
692
693
694
695 return new ColorXyz(X, Y, Z);
696 }
697
698 public static ColorCieLuv convertXuzToCieLuv(final double x, final double y, final double z) {
699
700
701 final double varU = 4 * x / (x + 15 * y + 3 * z);
702 final double varV = 9 * y / (x + 15 * y + 3 * z);
703
704
705
706
707 double varY = y / 100.0;
708
709
710 varY = pivotXyz(varY);
711
712
713
714 final double refU = 4 * REF_X / (REF_X + 15 * REF_Y + 3 * REF_Z);
715 final double refV = 9 * REF_Y / (REF_X + 15 * REF_Y + 3 * REF_Z);
716
717
718
719
720 final double l = 116 * varY - 16;
721 final double u = 13 * l * (varU - refU);
722 final double v = 13 * l * (varV - refV);
723
724 return new ColorCieLuv(l, u, v);
725 }
726
727 public static ColorCieLab convertXyzToCieLab(final ColorXyz xyz) {
728 return convertXyzToCieLab(xyz.x, xyz.y, xyz.z);
729 }
730
731 public static ColorCieLab convertXyzToCieLab(final double x, final double y, final double z) {
732
733 double varX = x / REF_X;
734 double varY = y / REF_Y;
735 double varZ = z / REF_Z;
736
737
738 varX = pivotXyz(varX);
739 varY = pivotXyz(varY);
740 varZ = pivotXyz(varZ);
741
742
743 final double l = Math.max(0, 116 * varY - 16);
744 final double a = 500 * (varX - varY);
745 final double b = 200 * (varY - varZ);
746 return new ColorCieLab(l, a, b);
747 }
748
749 public static ColorCieLuv convertXyzToCieLuv(final ColorXyz xyz) {
750 return convertXuzToCieLuv(xyz.x, xyz.y, xyz.z);
751 }
752
753 public static ColorHunterLab convertXyzToHunterLab(final ColorXyz xyz) {
754 return convertXyzToHunterLab(xyz.x, xyz.y, xyz.z);
755 }
756
757 public static ColorHunterLab convertXyzToHunterLab(final double x, final double y, final double z) {
758 final double l = 10 * Math.sqrt(y);
759 final double a = y == 0.0 ? 0.0 : 17.5 * ((1.02 * x - y) / Math.sqrt(y));
760 final double b = y == 0.0 ? 0.0 : 7 * ((y - 0.847 * z) / Math.sqrt(y));
761
762 return new ColorHunterLab(l, a, b);
763 }
764
765 public static int convertXyzToRgb(final ColorXyz xyz) {
766 return convertXyzToRgb(xyz.x, xyz.y, xyz.z);
767 }
768
769 public static int convertXyzToRgb(final double x, final double y, final double z) {
770
771 final double varX = x / 100.0;
772 final double varY = y / 100.0;
773 final double varZ = z / 100.0;
774
775
776 double varR = varX * 3.2404542 + varY * -1.5371385 + varZ * -0.4985314;
777 double varG = varX * -0.9692660 + varY * 1.8760108 + varZ * 0.0415560;
778 double varB = varX * 0.0556434 + varY * -0.2040259 + varZ * 1.0572252;
779
780
781
782
783
784
785 varR = pivotRgb(varR);
786 varG = pivotRgb(varG);
787 varB = pivotRgb(varB);
788
789 final double r = varR * 255;
790 final double g = varG * 255;
791 final double b = varB * 255;
792 return convertRgbToRgb(r, g, b);
793 }
794
795 public static double degree2radian(final double degree) {
796 return degree * Math.PI / 180.0;
797 }
798
799 private static double pivotRgb(double n) {
800 if (n > 0.0031308) {
801 n = 1.055 * Math.pow(n, 1 / 2.4) - 0.055;
802 } else {
803 n = 12.92 * n;
804 }
805 return n;
806 }
807
808 private static double pivotXyz(double n) {
809 if (n > XYZ_t0) {
810 n = Math.pow(n, 1 / 3.0);
811 } else {
812 n = XYZ_m * n + 16 / 116.0;
813 }
814 return n;
815 }
816
817 public static double radian2degree(final double radian) {
818 return radian * 180.0 / Math.PI;
819 }
820
821 private static double square(final double f) {
822 return f * f;
823 }
824
825 private static double unPivotRgb(double n) {
826 if (n > 0.04045) {
827 n = Math.pow((n + 0.055) / 1.055, 2.4);
828 } else {
829 n /= 12.92;
830 }
831 return n;
832 }
833
834 private static double unPivotXyz(double n) {
835 final double nCube = Math.pow(n, 3);
836 if (nCube > XYZ_t0) {
837 n = nCube;
838 } else {
839 n = (n - 16 / 116.0) / XYZ_m;
840 }
841 return n;
842 }
843
844 private ColorConversions() {
845 }
846
847 }