001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.imaging.color;
018
019
020public final class ColorConversions {
021    private static final double REF_X = 95.047;  // Observer= 2°, Illuminant= D65
022    private static final double REF_Y = 100.000;
023    private static final double REF_Z = 108.883;
024
025    private ColorConversions() {
026    }
027
028    public static ColorCieLab convertXYZtoCIELab(final ColorXyz xyz) {
029        return convertXYZtoCIELab(xyz.X, xyz.Y, xyz.Z);
030    }
031
032    public static ColorCieLab convertXYZtoCIELab(final double X, final double Y,
033            final double Z) {
034
035        double var_X = X / REF_X; // REF_X = 95.047 Observer= 2°, Illuminant=
036                                  // D65
037        double var_Y = Y / REF_Y; // REF_Y = 100.000
038        double var_Z = Z / REF_Z; // REF_Z = 108.883
039
040        if (var_X > 0.008856) {
041            var_X = Math.pow(var_X, (1 / 3.0));
042        } else {
043            var_X = (7.787 * var_X) + (16 / 116.0);
044        }
045        if (var_Y > 0.008856) {
046            var_Y = Math.pow(var_Y, 1 / 3.0);
047        } else {
048            var_Y = (7.787 * var_Y) + (16 / 116.0);
049        }
050        if (var_Z > 0.008856) {
051            var_Z = Math.pow(var_Z, 1 / 3.0);
052        } else {
053            var_Z = (7.787 * var_Z) + (16 / 116.0);
054        }
055
056        final double L = (116 * var_Y) - 16;
057        final double a = 500 * (var_X - var_Y);
058        final double b = 200 * (var_Y - var_Z);
059        return new ColorCieLab(L, a, b);
060    }
061
062    public static ColorXyz convertCIELabtoXYZ(final ColorCieLab cielab) {
063        return convertCIELabtoXYZ(cielab.L, cielab.a, cielab.b);
064    }
065
066    public static ColorXyz convertCIELabtoXYZ(final double L, final double a, final double b) {
067        double var_Y = (L + 16) / 116.0;
068        double var_X = a / 500 + var_Y;
069        double var_Z = var_Y - b / 200.0;
070
071        if (Math.pow(var_Y, 3) > 0.008856) {
072            var_Y = Math.pow(var_Y, 3);
073        } else {
074            var_Y = (var_Y - 16 / 116.0) / 7.787;
075        }
076        if (Math.pow(var_X, 3) > 0.008856) {
077            var_X = Math.pow(var_X, 3);
078        } else {
079            var_X = (var_X - 16 / 116.0) / 7.787;
080        }
081        if (Math.pow(var_Z, 3) > 0.008856) {
082            var_Z = Math.pow(var_Z, 3);
083        } else {
084            var_Z = (var_Z - 16 / 116.0) / 7.787;
085        }
086
087        final double X = REF_X * var_X; // REF_X = 95.047 Observer= 2°, Illuminant=
088                                  // D65
089        final double Y = REF_Y * var_Y; // REF_Y = 100.000
090        final double Z = REF_Z * var_Z; // REF_Z = 108.883
091
092        return new ColorXyz(X, Y, Z);
093    }
094
095    public static ColorHunterLab convertXYZtoHunterLab(final ColorXyz xyz) {
096        return convertXYZtoHunterLab(xyz.X, xyz.Y, xyz.Z);
097    }
098
099    public static ColorHunterLab convertXYZtoHunterLab(final double X,
100            final double Y, final double Z) {
101        final double L = 10 * Math.sqrt(Y);
102        final double a = 17.5 * (((1.02 * X) - Y) / Math.sqrt(Y));
103        final double b = 7 * ((Y - (0.847 * Z)) / Math.sqrt(Y));
104
105        return new ColorHunterLab(L, a, b);
106    }
107
108    public static ColorXyz convertHunterLabtoXYZ(final ColorHunterLab cielab) {
109        return convertHunterLabtoXYZ(cielab.L, cielab.a, cielab.b);
110    }
111
112    public static ColorXyz convertHunterLabtoXYZ(final double L, final double a,
113            final double b) {
114        final double var_Y = L / 10;
115        final double var_X = a / 17.5 * L / 10;
116        final double var_Z = b / 7 * L / 10;
117
118        final double Y = Math.pow(var_Y, 2);
119        final double X = (var_X + Y) / 1.02;
120        final double Z = -(var_Z - Y) / 0.847;
121
122        return new ColorXyz(X, Y, Z);
123    }
124
125    public static int convertXYZtoRGB(final ColorXyz xyz) {
126        return convertXYZtoRGB(xyz.X, xyz.Y, xyz.Z);
127    }
128
129    public static int convertXYZtoRGB(final double X, final double Y, final double Z) {
130        // Observer = 2°, Illuminant = D65
131        final double var_X = X / 100.0; // Where X = 0 ÷ 95.047
132        final double var_Y = Y / 100.0; // Where Y = 0 ÷ 100.000
133        final double var_Z = Z / 100.0; // Where Z = 0 ÷ 108.883
134
135        double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986;
136        double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415;
137        double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570;
138
139        if (var_R > 0.0031308) {
140            var_R = 1.055 * Math.pow(var_R, (1 / 2.4)) - 0.055;
141        } else {
142            var_R = 12.92 * var_R;
143        }
144        if (var_G > 0.0031308) {
145            var_G = 1.055 * Math.pow(var_G, (1 / 2.4)) - 0.055;
146        } else {
147            var_G = 12.92 * var_G;
148        }
149        if (var_B > 0.0031308) {
150            var_B = 1.055 * Math.pow(var_B, (1 / 2.4)) - 0.055;
151        } else {
152            var_B = 12.92 * var_B;
153        }
154
155        final double R = (var_R * 255);
156        final double G = (var_G * 255);
157        final double B = (var_B * 255);
158
159        return convertRGBtoRGB(R, G, B);
160    }
161
162    public static ColorXyz convertRGBtoXYZ(final int rgb) {
163        final int r = 0xff & (rgb >> 16);
164        final int g = 0xff & (rgb >> 8);
165        final int b = 0xff & (rgb >> 0);
166
167        double var_R = r / 255.0; // Where R = 0 ÷ 255
168        double var_G = g / 255.0; // Where G = 0 ÷ 255
169        double var_B = b / 255.0; // Where B = 0 ÷ 255
170
171        if (var_R > 0.04045) {
172            var_R = Math.pow((var_R + 0.055) / 1.055, 2.4);
173        } else {
174            var_R = var_R / 12.92;
175        }
176        if (var_G > 0.04045) {
177            var_G = Math.pow((var_G + 0.055) / 1.055, 2.4);
178        } else {
179            var_G = var_G / 12.92;
180        }
181        if (var_B > 0.04045) {
182            var_B = Math.pow((var_B + 0.055) / 1.055, 2.4);
183        } else {
184            var_B = var_B / 12.92;
185        }
186
187        var_R = var_R * 100;
188        var_G = var_G * 100;
189        var_B = var_B * 100;
190
191        // Observer. = 2°, Illuminant = D65
192        final double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
193        final double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
194        final double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;
195
196        return new ColorXyz(X, Y, Z);
197    }
198
199    public static ColorCmy convertRGBtoCMY(final int rgb) {
200        final int R = 0xff & (rgb >> 16);
201        final int G = 0xff & (rgb >> 8);
202        final int B = 0xff & (rgb >> 0);
203
204        // RGB values = 0 ÷ 255
205        // CMY values = 0 ÷ 1
206
207        final double C = 1 - (R / 255.0);
208        final double M = 1 - (G / 255.0);
209        final double Y = 1 - (B / 255.0);
210
211        return new ColorCmy(C, M, Y);
212    }
213
214    public static int convertCMYtoRGB(final ColorCmy cmy) {
215        // From Ghostscript's gdevcdj.c:
216        // * Ghostscript: R = (1.0 - C) * (1.0 - K)
217        // * Adobe: R = 1.0 - min(1.0, C + K)
218        // and similarly for G and B.
219        // This is Ghostscript's formula with K = 0.
220
221        // CMY values = 0 ÷ 1
222        // RGB values = 0 ÷ 255
223
224        final double R = (1 - cmy.C) * 255.0;
225        final double G = (1 - cmy.M) * 255.0;
226        final double B = (1 - cmy.Y) * 255.0;
227
228        return convertRGBtoRGB(R, G, B);
229    }
230
231    public static ColorCmyk convertCMYtoCMYK(final ColorCmy cmy) {
232        // Where CMYK and CMY values = 0 ÷ 1
233
234        double C = cmy.C;
235        double M = cmy.M;
236        double Y = cmy.Y;
237
238        double var_K = 1.0;
239
240        if (C < var_K) {
241            var_K = C;
242        }
243        if (M < var_K) {
244            var_K = M;
245        }
246        if (Y < var_K) {
247            var_K = Y;
248        }
249        if (var_K == 1) { // Black
250            C = 0;
251            M = 0;
252            Y = 0;
253        } else {
254            C = (C - var_K) / (1 - var_K);
255            M = (M - var_K) / (1 - var_K);
256            Y = (Y - var_K) / (1 - var_K);
257        }
258        return new ColorCmyk(C, M, Y, var_K);
259    }
260
261    public static ColorCmy convertCMYKtoCMY(final ColorCmyk cmyk) {
262        return convertCMYKtoCMY(cmyk.C, cmyk.M, cmyk.Y, cmyk.K);
263    }
264
265    public static ColorCmy convertCMYKtoCMY(double C, double M, double Y,
266            final double K) {
267        // Where CMYK and CMY values = 0 ÷ 1
268
269        C = (C * (1 - K) + K);
270        M = (M * (1 - K) + K);
271        Y = (Y * (1 - K) + K);
272
273        return new ColorCmy(C, M, Y);
274    }
275
276    public static int convertCMYKtoRGB(final int c, final int m, final int y, final int k) {
277        final double C = c / 255.0;
278        final double M = m / 255.0;
279        final double Y = y / 255.0;
280        final double K = k / 255.0;
281
282        return convertCMYtoRGB(convertCMYKtoCMY(C, M, Y, K));
283    }
284
285    public static ColorHsl convertRGBtoHSL(final int rgb) {
286
287        final int R = 0xff & (rgb >> 16);
288        final int G = 0xff & (rgb >> 8);
289        final int B = 0xff & (rgb >> 0);
290
291        final double var_R = (R / 255.0); // Where RGB values = 0 ÷ 255
292        final double var_G = (G / 255.0);
293        final double var_B = (B / 255.0);
294
295        final double var_Min = Math.min(var_R, Math.min(var_G, var_B)); // Min. value
296                                                                  // of RGB
297        double var_Max;
298        boolean maxIsR = false;
299        boolean maxIsG = false;
300        if (var_R >= var_G && var_R >= var_B) {
301            var_Max = var_R;
302            maxIsR = true;
303        } else if (var_G > var_B) {
304            var_Max = var_G;
305            maxIsG = true;
306        } else {
307            var_Max = var_B;
308        }
309        final double del_Max = var_Max - var_Min; // Delta RGB value
310
311        final double L = (var_Max + var_Min) / 2.0;
312
313        double H, S;
314        // Debug.debug("del_Max", del_Max);
315        if (del_Max == 0) {
316            // This is a gray, no chroma...
317
318            H = 0; // HSL results = 0 ÷ 1
319            S = 0;
320        } else {
321        // Chromatic data...
322
323            // Debug.debug("L", L);
324
325            if (L < 0.5) {
326                S = del_Max / (var_Max + var_Min);
327            } else {
328                S = del_Max / (2 - var_Max - var_Min);
329            }
330
331            // Debug.debug("S", S);
332
333            final double del_R = (((var_Max - var_R) / 6) + (del_Max / 2)) / del_Max;
334            final double del_G = (((var_Max - var_G) / 6) + (del_Max / 2)) / del_Max;
335            final double del_B = (((var_Max - var_B) / 6) + (del_Max / 2)) / del_Max;
336
337            if (maxIsR) {
338                H = del_B - del_G;
339            } else if (maxIsG) {
340                H = (1 / 3.0) + del_R - del_B;
341            } else {
342                H = (2 / 3.0) + del_G - del_R;
343            }
344
345            // Debug.debug("H1", H);
346
347            if (H < 0) {
348                H += 1;
349            }
350            if (H > 1) {
351                H -= 1;
352            }
353
354            // Debug.debug("H2", H);
355        }
356
357        return new ColorHsl(H, S, L);
358    }
359
360    public static int convertHSLtoRGB(final ColorHsl hsl) {
361        return convertHSLtoRGB(hsl.H, hsl.S, hsl.L);
362    }
363
364    public static int convertHSLtoRGB(final double H, final double S, final double L) {
365        double R, G, B;
366
367        if (S == 0) {
368            // HSL values = 0 ÷ 1
369            R = L * 255; // RGB results = 0 ÷ 255
370            G = L * 255;
371            B = L * 255;
372        } else {
373            double var_2;
374
375            if (L < 0.5) {
376                var_2 = L * (1 + S);
377            } else {
378                var_2 = (L + S) - (S * L);
379            }
380
381            final double var_1 = 2 * L - var_2;
382
383            R = 255 * convertHuetoRGB(var_1, var_2, H + (1 / 3.0));
384            G = 255 * convertHuetoRGB(var_1, var_2, H);
385            B = 255 * convertHuetoRGB(var_1, var_2, H - (1 / 3.0));
386        }
387
388        return convertRGBtoRGB(R, G, B);
389    }
390
391    private static double convertHuetoRGB(final double v1, final double v2, double vH) {
392        if (vH < 0) {
393            vH += 1;
394        }
395        if (vH > 1) {
396            vH -= 1;
397        }
398        if ((6 * vH) < 1) {
399            return (v1 + (v2 - v1) * 6 * vH);
400        }
401        if ((2 * vH) < 1) {
402            return (v2);
403        }
404        if ((3 * vH) < 2) {
405            return (v1 + (v2 - v1) * ((2 / 3.0) - vH) * 6);
406        }
407        return (v1);
408    }
409
410    public static ColorHsv convertRGBtoHSV(final int rgb) {
411        final int R = 0xff & (rgb >> 16);
412        final int G = 0xff & (rgb >> 8);
413        final int B = 0xff & (rgb >> 0);
414
415        final double var_R = (R / 255.0); // RGB values = 0 ÷ 255
416        final double var_G = (G / 255.0);
417        final double var_B = (B / 255.0);
418
419        final double var_Min = Math.min(var_R, Math.min(var_G, var_B)); // Min. value
420                                                                  // of RGB
421        boolean maxIsR = false;
422        boolean maxIsG = false;
423        double var_Max;
424        if (var_R >= var_G && var_R >= var_B) {
425            var_Max = var_R;
426            maxIsR = true;
427        } else if (var_G > var_B) {
428            var_Max = var_G;
429            maxIsG = true;
430        } else {
431            var_Max = var_B;
432        }
433        final double del_Max = var_Max - var_Min; // Delta RGB value
434
435        final double V = var_Max;
436
437        double H, S;
438        if (del_Max == 0) {
439            // This is a gray, no chroma...
440            H = 0; // HSV results = 0 ÷ 1
441            S = 0;
442        } else {
443        // Chromatic data...
444            S = del_Max / var_Max;
445
446            final double del_R = (((var_Max - var_R) / 6) + (del_Max / 2)) / del_Max;
447            final double del_G = (((var_Max - var_G) / 6) + (del_Max / 2)) / del_Max;
448            final double del_B = (((var_Max - var_B) / 6) + (del_Max / 2)) / del_Max;
449
450            if (maxIsR) {
451                H = del_B - del_G;
452            } else if (maxIsG) {
453                H = (1 / 3.0) + del_R - del_B;
454            } else {
455                H = (2 / 3.0) + del_G - del_R;
456            }
457
458            if (H < 0) {
459                H += 1;
460            }
461            if (H > 1) {
462                H -= 1;
463            }
464        }
465
466        return new ColorHsv(H, S, V);
467    }
468
469    public static int convertHSVtoRGB(final ColorHsv HSV) {
470        return convertHSVtoRGB(HSV.H, HSV.S, HSV.V);
471    }
472
473    public static int convertHSVtoRGB(final double H, final double S, final double V) {
474        double R, G, B;
475
476        if (S == 0) {
477            // HSV values = 0 ÷ 1
478            R = V * 255;
479            G = V * 255;
480            B = V * 255;
481        } else {
482            double var_h = H * 6;
483            if (var_h == 6) {
484                var_h = 0; // H must be < 1
485            }
486            final double var_i = Math.floor(var_h); // Or ... var_i = floor( var_h )
487            final double var_1 = V * (1 - S);
488            final double var_2 = V * (1 - S * (var_h - var_i));
489            final double var_3 = V * (1 - S * (1 - (var_h - var_i)));
490
491            double var_r, var_g, var_b;
492
493            if (var_i == 0) {
494                var_r = V;
495                var_g = var_3;
496                var_b = var_1;
497            } else if (var_i == 1) {
498                var_r = var_2;
499                var_g = V;
500                var_b = var_1;
501            } else if (var_i == 2) {
502                var_r = var_1;
503                var_g = V;
504                var_b = var_3;
505            } else if (var_i == 3) {
506                var_r = var_1;
507                var_g = var_2;
508                var_b = V;
509            } else if (var_i == 4) {
510                var_r = var_3;
511                var_g = var_1;
512                var_b = V;
513            } else {
514                var_r = V;
515                var_g = var_1;
516                var_b = var_2;
517            }
518
519            R = var_r * 255; // RGB results = 0 ÷ 255
520            G = var_g * 255;
521            B = var_b * 255;
522        }
523
524        return convertRGBtoRGB(R, G, B);
525    }
526
527    public static int convertCMYKtoRGB_Adobe(final int sc, final int sm, final int sy,
528            final int sk) {
529        final int red = 255 - (sc + sk);
530        final int green = 255 - (sm + sk);
531        final int blue = 255 - (sy + sk);
532
533        return convertRGBtoRGB(red, green, blue);
534    }
535
536    private static double cube(final double f) {
537        return f * f * f;
538    }
539
540    private static double square(final double f) {
541        return f * f;
542    }
543
544    public static int convertCIELabtoARGBTest(final int cieL, final int cieA, final int cieB) {
545        double X, Y, Z;
546
547        {
548
549            double var_Y = ((cieL * 100.0 / 255.0) + 16.0) / 116.0;
550            double var_X = cieA / 500.0 + var_Y;
551            double var_Z = var_Y - cieB / 200.0;
552
553            final double var_x_cube = cube(var_X);
554            final double var_y_cube = cube(var_Y);
555            final double var_z_cube = cube(var_Z);
556
557            if (var_y_cube > 0.008856) {
558                var_Y = var_y_cube;
559            } else {
560                var_Y = (var_Y - 16 / 116.0) / 7.787;
561            }
562
563            if (var_x_cube > 0.008856) {
564                var_X = var_x_cube;
565            } else {
566                var_X = (var_X - 16 / 116.0) / 7.787;
567            }
568
569            if (var_z_cube > 0.008856) {
570                var_Z = var_z_cube;
571            } else {
572                var_Z = (var_Z - 16 / 116.0) / 7.787;
573            }
574
575            // double REF_X = 95.047;
576            // double REF_Y = 100.000;
577            // double REF_Z = 108.883;
578
579            X = REF_X * var_X; // REF_X = 95.047 Observer= 2°, Illuminant= D65
580            Y = REF_Y * var_Y; // REF_Y = 100.000
581            Z = REF_Z * var_Z; // REF_Z = 108.883
582
583        }
584
585        double R, G, B;
586        {
587            final double var_X = X / 100; // X = From 0 to REF_X
588            final double var_Y = Y / 100; // Y = From 0 to REF_Y
589            final double var_Z = Z / 100; // Z = From 0 to REF_Y
590
591            double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986;
592            double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415;
593            double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570;
594
595            if (var_R > 0.0031308) {
596                var_R = 1.055 * Math.pow(var_R, (1 / 2.4)) - 0.055;
597            } else {
598                var_R = 12.92 * var_R;
599            }
600            if (var_G > 0.0031308) {
601                var_G = 1.055 * Math.pow(var_G, (1 / 2.4)) - 0.055;
602            } else {
603                var_G = 12.92 * var_G;
604            }
605
606            if (var_B > 0.0031308) {
607                var_B = 1.055 * Math.pow(var_B, (1 / 2.4)) - 0.055;
608            } else {
609                var_B = 12.92 * var_B;
610            }
611
612            R = (var_R * 255);
613            G = (var_G * 255);
614            B = (var_B * 255);
615        }
616
617        return convertRGBtoRGB(R, G, B);
618    }
619
620    private static int convertRGBtoRGB(final double R, final double G, final double B) {
621        int red = (int) Math.round(R);
622        int green = (int) Math.round(G);
623        int blue = (int) Math.round(B);
624
625        red = Math.min(255, Math.max(0, red));
626        green = Math.min(255, Math.max(0, green));
627        blue = Math.min(255, Math.max(0, blue));
628
629        final int alpha = 0xff;
630        final int rgb = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
631
632        return rgb;
633    }
634
635    private static int convertRGBtoRGB(int red, int green, int blue) {
636        red = Math.min(255, Math.max(0, red));
637        green = Math.min(255, Math.max(0, green));
638        blue = Math.min(255, Math.max(0, blue));
639
640        final int alpha = 0xff;
641        final int rgb = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
642
643        return rgb;
644    }
645
646    public static ColorCieLch convertCIELabtoCIELCH(final ColorCieLab cielab) {
647        return convertCIELabtoCIELCH(cielab.L, cielab.a, cielab.b);
648    }
649
650    public static ColorCieLch convertCIELabtoCIELCH(final double L, final double a, final double b) {
651        double var_H = Math.atan2(b, a); // Quadrant by signs
652
653        if (var_H > 0) {
654            var_H = (var_H / Math.PI) * 180.0;
655        } else {
656            var_H = 360 - radian_2_degree(Math.abs(var_H));
657        }
658
659        // L = L;
660        final double C = Math.sqrt(square(a) + square(b));
661        final double H = var_H;
662
663        return new ColorCieLch(L, C, H);
664    }
665
666    public static ColorCieLab convertCIELCHtoCIELab(final ColorCieLch cielch) {
667        return convertCIELCHtoCIELab(cielch.L, cielch.C, cielch.H);
668    }
669
670    public static ColorCieLab convertCIELCHtoCIELab(final double L, final double C, final double H) {
671        // Where CIE-H° = 0 ÷ 360°
672
673        // CIE-L* = CIE-L;
674        final double a = Math.cos(degree_2_radian(H)) * C;
675        final double b = Math.sin(degree_2_radian(H)) * C;
676
677        return new ColorCieLab(L, a, b);
678    }
679
680    public static double degree_2_radian(final double degree) {
681        return degree * Math.PI / 180.0;
682    }
683
684    public static double radian_2_degree(final double radian) {
685        return radian * 180.0 / Math.PI;
686    }
687
688    public static ColorCieLuv convertXYZtoCIELuv(final ColorXyz xyz) {
689        return convertXYZtoCIELuv(xyz.X, xyz.Y, xyz.Z);
690    }
691
692    public static ColorCieLuv convertXYZtoCIELuv(final double X, final double Y, final double Z) {
693        // problems here with div by zero
694
695        final double var_U = (4 * X) / (X + (15 * Y) + (3 * Z));
696        final double var_V = (9 * Y) / (X + (15 * Y) + (3 * Z));
697
698        // Debug.debug("var_U", var_U);
699        // Debug.debug("var_V", var_V);
700
701        double var_Y = Y / 100.0;
702        // Debug.debug("var_Y", var_Y);
703
704        if (var_Y > 0.008856) {
705            var_Y = Math.pow(var_Y, (1 / 3.0));
706        } else {
707            var_Y = (7.787 * var_Y) + (16 / 116.0);
708        }
709
710        // Debug.debug("var_Y", var_Y);
711
712        final double ref_U = (4 * REF_X) / (REF_X + (15 * REF_Y) + (3 * REF_Z));
713        final double ref_V = (9 * REF_Y) / (REF_X + (15 * REF_Y) + (3 * REF_Z));
714
715        // Debug.debug("ref_U", ref_U);
716        // Debug.debug("ref_V", ref_V);
717
718        final double L = (116 * var_Y) - 16;
719        final double u = 13 * L * (var_U - ref_U);
720        final double v = 13 * L * (var_V - ref_V);
721
722        return new ColorCieLuv(L, u, v);
723    }
724
725    public static ColorXyz convertCIELuvtoXYZ(final ColorCieLuv cielch) {
726        return convertCIELuvtoXYZ(cielch.L, cielch.u, cielch.v);
727    }
728
729    public static ColorXyz convertCIELuvtoXYZ(final double L, final double u, final double v) {
730        // problems here with div by zero
731
732        double var_Y = (L + 16) / 116;
733        if (Math.pow(var_Y, 3) > 0.008856) {
734            var_Y = Math.pow(var_Y, 3);
735        } else {
736            var_Y = (var_Y - 16 / 116) / 7.787;
737        }
738
739        final double ref_U = (4 * REF_X) / (REF_X + (15 * REF_Y) + (3 * REF_Z));
740        final double ref_V = (9 * REF_Y) / (REF_X + (15 * REF_Y) + (3 * REF_Z));
741        final double var_U = u / (13 * L) + ref_U;
742        final double var_V = v / (13 * L) + ref_V;
743
744        final double Y = var_Y * 100;
745        final double X = -(9 * Y * var_U) / ((var_U - 4) * var_V - var_U * var_V);
746        final double Z = (9 * Y - (15 * var_V * Y) - (var_V * X)) / (3 * var_V);
747
748        return new ColorXyz(X, Y, Z);
749    }
750}