    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}