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 022 // White reference 023 /** see: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 024 private static final double REF_X = 95.047; // Observer= 2°, Illuminant= D65 025 026 /** see: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 027 private static final double REF_Y = 100.000; 028 029 /** see: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 030 private static final double REF_Z = 108.883; 031 032 /** see: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 033 private static final double XYZ_m = 7.787037; // match in slope. Note commonly seen 7.787 gives worse results 034 035 /** see: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 036 private static final double XYZ_t0 = 0.008856; 037 038 private ColorConversions() { 039 } 040 041 public static ColorCieLab convertXYZtoCIELab(final ColorXyz xyz) { 042 return convertXYZtoCIELab(xyz.X, xyz.Y, xyz.Z); 043 } 044 045 public static ColorCieLab convertXYZtoCIELab(final double X, final double Y, final double Z) { 046 047 double var_X = X / REF_X; // REF_X = 95.047 Observer= 2°, Illuminant= D65 048 double var_Y = Y / REF_Y; // REF_Y = 100.000 049 double var_Z = Z / REF_Z; // REF_Z = 108.883 050 051 // Pivot XÝZ: 052 var_X = pivotXYZ(var_X); 053 var_Y = pivotXYZ(var_Y); 054 var_Z = pivotXYZ(var_Z); 055 056 // Math.max added from https://github.com/muak/ColorMinePortable/blob/master/ColorMinePortable/ColorSpaces/Conversions/LabConverter.cs 057 final double L = Math.max(0, 116 * var_Y - 16); 058 final double a = 500 * (var_X - var_Y); 059 final double b = 200 * (var_Y - var_Z); 060 return new ColorCieLab(L, a, b); 061 } 062 063 public static ColorXyz convertCIELabtoXYZ(final ColorCieLab cielab) { 064 return convertCIELabtoXYZ(cielab.L, cielab.a, cielab.b); 065 } 066 067 public static ColorXyz convertCIELabtoXYZ(final double L, final double a, final double b) { 068 double var_Y = (L + 16) / 116.0; 069 double var_X = a / 500 + var_Y; 070 double var_Z = var_Y - b / 200.0; 071 072 var_Y = unPivotXYZ(var_Y); 073 var_X = unPivotXYZ(var_X); 074 var_Z = unPivotXYZ(var_Z); 075 076 final double X = REF_X * var_X; // REF_X = 95.047 Observer= 2°, Illuminant= 077 // D65 078 final double Y = REF_Y * var_Y; // REF_Y = 100.000 079 final double Z = REF_Z * var_Z; // REF_Z = 108.883 080 081 return new ColorXyz(X, Y, Z); 082 } 083 084 public static ColorHunterLab convertXYZtoHunterLab(final ColorXyz xyz) { 085 return convertXYZtoHunterLab(xyz.X, xyz.Y, xyz.Z); 086 } 087 088 public static ColorHunterLab convertXYZtoHunterLab(final double X, 089 final double Y, final double Z) { 090 final double L = 10 * Math.sqrt(Y); 091 final double a = Y == 0.0 ? 0.0 : 17.5 * (((1.02 * X) - Y) / Math.sqrt(Y)); 092 final double b = Y == 0.0 ? 0.0 : 7 * ((Y - (0.847 * Z)) / Math.sqrt(Y)); 093 094 return new ColorHunterLab(L, a, b); 095 } 096 097 public static ColorXyz convertHunterLabtoXYZ(final ColorHunterLab cielab) { 098 return convertHunterLabtoXYZ(cielab.L, cielab.a, cielab.b); 099 } 100 101 public static ColorXyz convertHunterLabtoXYZ(final double L, final double a, 102 final double b) { 103 final double var_Y = L / 10; 104 final double var_X = a / 17.5 * L / 10; 105 final double var_Z = b / 7 * L / 10; 106 107 final double Y = Math.pow(var_Y, 2); 108 final double X = (var_X + Y) / 1.02; 109 final double Z = -(var_Z - Y) / 0.847; 110 111 return new ColorXyz(X, Y, Z); 112 } 113 114 115 public static int convertXYZtoRGB(final ColorXyz xyz) { 116 return convertXYZtoRGB(xyz.X, xyz.Y, xyz.Z); 117 } 118 119 public static int convertXYZtoRGB(final double X, final double Y, final double Z) { 120 // Observer = 2°, Illuminant = D65 121 final double var_X = X / 100.0; // Where X = 0 ÷ 95.047 122 final double var_Y = Y / 100.0; // Where Y = 0 ÷ 100.000 123 final double var_Z = Z / 100.0; // Where Z = 0 ÷ 108.883 124 125 // see: https://github.com/StanfordHCI/c3/blob/master/java/src/edu/stanford/vis/color/LAB.java 126 double var_R = var_X * 3.2404542 + var_Y * -1.5371385 + var_Z * -0.4985314; 127 double var_G = var_X * -0.9692660 + var_Y * 1.8760108 + var_Z * 0.0415560; 128 double var_B = var_X * 0.0556434 + var_Y * -0.2040259 + var_Z * 1.0572252; 129 130 // Attention: A lot of sources do list these values with less precision. But it makes a visual difference: 131 // double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986; 132 // double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415; 133 // double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570; 134 135 var_R = pivotRGB(var_R); 136 var_G = pivotRGB(var_G); 137 var_B = pivotRGB(var_B); 138 139 final double R = (var_R * 255); 140 final double G = (var_G * 255); 141 final double B = (var_B * 255); 142 return convertRGBtoRGB(R, G, B); 143 } 144 145 // See also c# implementation: 146 // https://github.com/muak/ColorMinePortable/blob/master/ColorMinePortable/ColorSpaces/Conversions/XyzConverter.cs 147 public static ColorXyz convertRGBtoXYZ(final int rgb) { 148 final int r = 0xff & (rgb >> 16); 149 final int g = 0xff & (rgb >> 8); 150 final int b = 0xff & (rgb >> 0); 151 152 double var_R = r / 255.0; // Where R = 0 ÷ 255 153 double var_G = g / 255.0; // Where G = 0 ÷ 255 154 double var_B = b / 255.0; // Where B = 0 ÷ 255 155 156 // Pivot RGB: 157 var_R = unPivotRGB(var_R); 158 var_G = unPivotRGB(var_G); 159 var_B = unPivotRGB(var_B); 160 161 var_R *= 100; 162 var_G *= 100; 163 var_B *= 100; 164 165 // Observer. = 2°, Illuminant = D65 166 // see: https://github.com/StanfordHCI/c3/blob/master/java/src/edu/stanford/vis/color/LAB.java 167 final double X = var_R * 0.4124564 + var_G * 0.3575761 + var_B * 0.1804375; 168 final double Y = var_R * 0.2126729 + var_G * 0.7151522 + var_B * 0.0721750; 169 final double Z = var_R * 0.0193339 + var_G * 0.1191920 + var_B * 0.9503041; 170 171 // Attention: A lot of sources do list these values with less precision. But it makes a visual difference: 172 // final double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805; 173 // final double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722; 174 // final double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505; 175 176 return new ColorXyz(X, Y, Z); 177 } 178 179 public static ColorCmy convertRGBtoCMY(final int rgb) { 180 final int R = 0xff & (rgb >> 16); 181 final int G = 0xff & (rgb >> 8); 182 final int B = 0xff & (rgb >> 0); 183 184 // RGB values = 0 ÷ 255 185 // CMY values = 0 ÷ 1 186 187 final double C = 1 - (R / 255.0); 188 final double M = 1 - (G / 255.0); 189 final double Y = 1 - (B / 255.0); 190 191 return new ColorCmy(C, M, Y); 192 } 193 194 public static int convertCMYtoRGB(final ColorCmy cmy) { 195 // From Ghostscript's gdevcdj.c: 196 // * Ghostscript: R = (1.0 - C) * (1.0 - K) 197 // * Adobe: R = 1.0 - min(1.0, C + K) 198 // and similarly for G and B. 199 // This is Ghostscript's formula with K = 0. 200 201 // CMY values = 0 ÷ 1 202 // RGB values = 0 ÷ 255 203 204 final double R = (1 - cmy.C) * 255.0; 205 final double G = (1 - cmy.M) * 255.0; 206 final double B = (1 - cmy.Y) * 255.0; 207 208 return convertRGBtoRGB(R, G, B); 209 } 210 211 public static ColorCmyk convertCMYtoCMYK(final ColorCmy cmy) { 212 // Where CMYK and CMY values = 0 ÷ 1 213 214 double C = cmy.C; 215 double M = cmy.M; 216 double Y = cmy.Y; 217 218 double var_K = 1.0; 219 220 if (C < var_K) { 221 var_K = C; 222 } 223 if (M < var_K) { 224 var_K = M; 225 } 226 if (Y < var_K) { 227 var_K = Y; 228 } 229 if (var_K == 1) { // Black 230 C = 0; 231 M = 0; 232 Y = 0; 233 } else { 234 C = (C - var_K) / (1 - var_K); 235 M = (M - var_K) / (1 - var_K); 236 Y = (Y - var_K) / (1 - var_K); 237 } 238 return new ColorCmyk(C, M, Y, var_K); 239 } 240 241 public static ColorCmy convertCMYKtoCMY(final ColorCmyk cmyk) { 242 return convertCMYKtoCMY(cmyk.C, cmyk.M, cmyk.Y, cmyk.K); 243 } 244 245 public static ColorCmy convertCMYKtoCMY(double C, double M, double Y, 246 final double K) { 247 // Where CMYK and CMY values = 0 ÷ 1 248 249 C = (C * (1 - K) + K); 250 M = (M * (1 - K) + K); 251 Y = (Y * (1 - K) + K); 252 253 return new ColorCmy(C, M, Y); 254 } 255 256 public static int convertCMYKtoRGB(final int c, final int m, final int y, final int k) { 257 final double C = c / 255.0; 258 final double M = m / 255.0; 259 final double Y = y / 255.0; 260 final double K = k / 255.0; 261 262 return convertCMYtoRGB(convertCMYKtoCMY(C, M, Y, K)); 263 } 264 265 public static ColorHsl convertRGBtoHSL(final int rgb) { 266 267 final int R = 0xff & (rgb >> 16); 268 final int G = 0xff & (rgb >> 8); 269 final int B = 0xff & (rgb >> 0); 270 271 final double var_R = (R / 255.0); // Where RGB values = 0 ÷ 255 272 final double var_G = (G / 255.0); 273 final double var_B = (B / 255.0); 274 275 final double var_Min = Math.min(var_R, Math.min(var_G, var_B)); // Min. value 276 // of RGB 277 double var_Max; 278 boolean maxIsR = false; 279 boolean maxIsG = false; 280 if (var_R >= var_G && var_R >= var_B) { 281 var_Max = var_R; 282 maxIsR = true; 283 } else if (var_G > var_B) { 284 var_Max = var_G; 285 maxIsG = true; 286 } else { 287 var_Max = var_B; 288 } 289 final double del_Max = var_Max - var_Min; // Delta RGB value 290 291 final double L = (var_Max + var_Min) / 2.0; 292 293 double H, S; 294 // Debug.debug("del_Max", del_Max); 295 if (del_Max == 0) { 296 // This is a gray, no chroma... 297 298 H = 0; // HSL results = 0 ÷ 1 299 S = 0; 300 } else { 301 // Chromatic data... 302 303 // Debug.debug("L", L); 304 305 if (L < 0.5) { 306 S = del_Max / (var_Max + var_Min); 307 } else { 308 S = del_Max / (2 - var_Max - var_Min); 309 } 310 311 // Debug.debug("S", S); 312 313 final double del_R = (((var_Max - var_R) / 6) + (del_Max / 2)) / del_Max; 314 final double del_G = (((var_Max - var_G) / 6) + (del_Max / 2)) / del_Max; 315 final double del_B = (((var_Max - var_B) / 6) + (del_Max / 2)) / del_Max; 316 317 if (maxIsR) { 318 H = del_B - del_G; 319 } else if (maxIsG) { 320 H = (1 / 3.0) + del_R - del_B; 321 } else { 322 H = (2 / 3.0) + del_G - del_R; 323 } 324 325 // Debug.debug("H1", H); 326 327 if (H < 0) { 328 H += 1; 329 } 330 if (H > 1) { 331 H -= 1; 332 } 333 334 // Debug.debug("H2", H); 335 } 336 337 return new ColorHsl(H, S, L); 338 } 339 340 public static int convertHSLtoRGB(final ColorHsl hsl) { 341 return convertHSLtoRGB(hsl.H, hsl.S, hsl.L); 342 } 343 344 public static int convertHSLtoRGB(final double H, final double S, final double L) { 345 double R, G, B; 346 347 if (S == 0) { 348 // HSL values = 0 ÷ 1 349 R = L * 255; // RGB results = 0 ÷ 255 350 G = L * 255; 351 B = L * 255; 352 } else { 353 double var_2; 354 355 if (L < 0.5) { 356 var_2 = L * (1 + S); 357 } else { 358 var_2 = (L + S) - (S * L); 359 } 360 361 final double var_1 = 2 * L - var_2; 362 363 R = 255 * convertHuetoRGB(var_1, var_2, H + (1 / 3.0)); 364 G = 255 * convertHuetoRGB(var_1, var_2, H); 365 B = 255 * convertHuetoRGB(var_1, var_2, H - (1 / 3.0)); 366 } 367 368 return convertRGBtoRGB(R, G, B); 369 } 370 371 private static double convertHuetoRGB(final double v1, final double v2, double vH) { 372 if (vH < 0) { 373 vH += 1; 374 } 375 if (vH > 1) { 376 vH -= 1; 377 } 378 if ((6 * vH) < 1) { 379 return (v1 + (v2 - v1) * 6 * vH); 380 } 381 if ((2 * vH) < 1) { 382 return (v2); 383 } 384 if ((3 * vH) < 2) { 385 return (v1 + (v2 - v1) * ((2 / 3.0) - vH) * 6); 386 } 387 return (v1); 388 } 389 390 public static ColorHsv convertRGBtoHSV(final int rgb) { 391 final int R = 0xff & (rgb >> 16); 392 final int G = 0xff & (rgb >> 8); 393 final int B = 0xff & (rgb >> 0); 394 395 final double var_R = (R / 255.0); // RGB values = 0 ÷ 255 396 final double var_G = (G / 255.0); 397 final double var_B = (B / 255.0); 398 399 final double var_Min = Math.min(var_R, Math.min(var_G, var_B)); // Min. value 400 // of RGB 401 boolean maxIsR = false; 402 boolean maxIsG = false; 403 double var_Max; 404 if (var_R >= var_G && var_R >= var_B) { 405 var_Max = var_R; 406 maxIsR = true; 407 } else if (var_G > var_B) { 408 var_Max = var_G; 409 maxIsG = true; 410 } else { 411 var_Max = var_B; 412 } 413 final double del_Max = var_Max - var_Min; // Delta RGB value 414 415 final double V = var_Max; 416 417 double H, S; 418 if (del_Max == 0) { 419 // This is a gray, no chroma... 420 H = 0; // HSV results = 0 ÷ 1 421 S = 0; 422 } else { 423 // Chromatic data... 424 S = del_Max / var_Max; 425 426 final double del_R = (((var_Max - var_R) / 6) + (del_Max / 2)) / del_Max; 427 final double del_G = (((var_Max - var_G) / 6) + (del_Max / 2)) / del_Max; 428 final double del_B = (((var_Max - var_B) / 6) + (del_Max / 2)) / del_Max; 429 430 if (maxIsR) { 431 H = del_B - del_G; 432 } else if (maxIsG) { 433 H = (1 / 3.0) + del_R - del_B; 434 } else { 435 H = (2 / 3.0) + del_G - del_R; 436 } 437 438 if (H < 0) { 439 H += 1; 440 } 441 if (H > 1) { 442 H -= 1; 443 } 444 } 445 446 return new ColorHsv(H, S, V); 447 } 448 449 public static int convertHSVtoRGB(final ColorHsv HSV) { 450 return convertHSVtoRGB(HSV.H, HSV.S, HSV.V); 451 } 452 453 public static int convertHSVtoRGB(final double H, final double S, final double V) { 454 double R, G, B; 455 456 if (S == 0) { 457 // HSV values = 0 ÷ 1 458 R = V * 255; 459 G = V * 255; 460 B = V * 255; 461 } else { 462 double var_h = H * 6; 463 if (var_h == 6) { 464 var_h = 0; // H must be < 1 465 } 466 final double var_i = Math.floor(var_h); // Or ... var_i = floor( var_h ) 467 final double var_1 = V * (1 - S); 468 final double var_2 = V * (1 - S * (var_h - var_i)); 469 final double var_3 = V * (1 - S * (1 - (var_h - var_i))); 470 471 double var_r, var_g, var_b; 472 473 if (var_i == 0) { 474 var_r = V; 475 var_g = var_3; 476 var_b = var_1; 477 } else if (var_i == 1) { 478 var_r = var_2; 479 var_g = V; 480 var_b = var_1; 481 } else if (var_i == 2) { 482 var_r = var_1; 483 var_g = V; 484 var_b = var_3; 485 } else if (var_i == 3) { 486 var_r = var_1; 487 var_g = var_2; 488 var_b = V; 489 } else if (var_i == 4) { 490 var_r = var_3; 491 var_g = var_1; 492 var_b = V; 493 } else { 494 var_r = V; 495 var_g = var_1; 496 var_b = var_2; 497 } 498 499 R = var_r * 255; // RGB results = 0 ÷ 255 500 G = var_g * 255; 501 B = var_b * 255; 502 } 503 504 return convertRGBtoRGB(R, G, B); 505 } 506 507 public static int convertCMYKtoRGB_Adobe(final int sc, final int sm, final int sy, 508 final int sk) { 509 final int red = 255 - (sc + sk); 510 final int green = 255 - (sm + sk); 511 final int blue = 255 - (sy + sk); 512 513 return convertRGBtoRGB(red, green, blue); 514 } 515 516 private static double cube(final double f) { 517 return f * f * f; 518 } 519 520 private static double square(final double f) { 521 return f * f; 522 } 523 524 public static int convertCIELabtoARGBTest(final int cieL, final int cieA, final int cieB) { 525 double X, Y, Z; 526 { 527 528 double var_Y = ((cieL * 100.0 / 255.0) + 16.0) / 116.0; 529 double var_X = cieA / 500.0 + var_Y; 530 double var_Z = var_Y - cieB / 200.0; 531 532 var_X = unPivotXYZ(var_X); 533 var_Y = unPivotXYZ(var_Y); 534 var_Z = unPivotXYZ(var_Z); 535 536 X = REF_X * var_X; // REF_X = 95.047 Observer= 2°, Illuminant= D65 537 Y = REF_Y * var_Y; // REF_Y = 100.000 538 Z = REF_Z * var_Z; // REF_Z = 108.883 539 540 } 541 542 double R, G, B; 543 { 544 final double var_X = X / 100; // X = From 0 to REF_X 545 final double var_Y = Y / 100; // Y = From 0 to REF_Y 546 final double var_Z = Z / 100; // Z = From 0 to REF_Y 547 548 double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986; 549 double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415; 550 double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570; 551 552 var_R = pivotRGB(var_R); 553 var_G = pivotRGB(var_G); 554 var_B = pivotRGB(var_B); 555 556 R = (var_R * 255); 557 G = (var_G * 255); 558 B = (var_B * 255); 559 } 560 561 return convertRGBtoRGB(R, G, B); 562 } 563 564 private static int convertRGBtoRGB(final double R, final double G, final double B) { 565 int red = (int) Math.round(R); 566 int green = (int) Math.round(G); 567 int blue = (int) Math.round(B); 568 569 red = Math.min(255, Math.max(0, red)); 570 green = Math.min(255, Math.max(0, green)); 571 blue = Math.min(255, Math.max(0, blue)); 572 573 final int alpha = 0xff; 574 575 return (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); 576 } 577 578 private static int convertRGBtoRGB(int red, int green, int blue) { 579 red = Math.min(255, Math.max(0, red)); 580 green = Math.min(255, Math.max(0, green)); 581 blue = Math.min(255, Math.max(0, blue)); 582 583 final int alpha = 0xff; 584 585 return (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); 586 } 587 588 public static ColorCieLch convertCIELabtoCIELCH(final ColorCieLab cielab) { 589 return convertCIELabtoCIELCH(cielab.L, cielab.a, cielab.b); 590 } 591 592 public static ColorCieLch convertCIELabtoCIELCH(final double L, final double a, final double b) { 593 // atan2(y,x) returns atan(y/x) 594 final double atanba = Math.atan2(b, a); // Quadrant by signs 595 596 final double h = atanba > 0 // 597 ? Math.toDegrees(atanba) // 598 : Math.toDegrees(atanba) + 360; 599 600 // L = L; 601 final double C = Math.sqrt(square(a) + square(b)); 602 603 return new ColorCieLch(L, C, h); 604 } 605 606 public static ColorCieLab convertCIELCHtoCIELab(final ColorCieLch cielch) { 607 return convertCIELCHtoCIELab(cielch.L, cielch.C, cielch.h); 608 } 609 610 public static ColorCieLab convertCIELCHtoCIELab(final double L, final double C, final double H) { 611 // Where CIE-H° = 0 ÷ 360° 612 613 // CIE-L* = CIE-L; 614 final double a = Math.cos(degree_2_radian(H)) * C; 615 final double b = Math.sin(degree_2_radian(H)) * C; 616 617 return new ColorCieLab(L, a, b); 618 } 619 620 public static double degree_2_radian(final double degree) { 621 return degree * Math.PI / 180.0; 622 } 623 624 public static double radian_2_degree(final double radian) { 625 return radian * 180.0 / Math.PI; 626 } 627 628 public static ColorCieLuv convertXYZtoCIELuv(final ColorXyz xyz) { 629 return convertXYZtoCIELuv(xyz.X, xyz.Y, xyz.Z); 630 } 631 632 public static ColorCieLuv convertXYZtoCIELuv(final double X, final double Y, final double Z) { 633 // problems here with div by zero 634 635 final double var_U = (4 * X) / (X + (15 * Y) + (3 * Z)); 636 final double var_V = (9 * Y) / (X + (15 * Y) + (3 * Z)); 637 638 // Debug.debug("var_U", var_U); 639 // Debug.debug("var_V", var_V); 640 641 double var_Y = Y / 100.0; 642 // Debug.debug("var_Y", var_Y); 643 644 var_Y = pivotXYZ(var_Y); 645 646 // Debug.debug("var_Y", var_Y); 647 648 final double ref_U = (4 * REF_X) / (REF_X + (15 * REF_Y) + (3 * REF_Z)); 649 final double ref_V = (9 * REF_Y) / (REF_X + (15 * REF_Y) + (3 * REF_Z)); 650 651 // Debug.debug("ref_U", ref_U); 652 // Debug.debug("ref_V", ref_V); 653 654 final double L = (116 * var_Y) - 16; 655 final double u = 13 * L * (var_U - ref_U); 656 final double v = 13 * L * (var_V - ref_V); 657 658 return new ColorCieLuv(L, u, v); 659 } 660 661 public static ColorXyz convertCIELuvtoXYZ(final ColorCieLuv cielch) { 662 return convertCIELuvtoXYZ(cielch.L, cielch.u, cielch.v); 663 } 664 665 public static ColorXyz convertCIELuvtoXYZ(final double L, final double u, final double v) { 666 // problems here with div by zero 667 668 double var_Y = (L + 16) / 116.0; 669 var_Y = unPivotXYZ(var_Y); 670 671 final double ref_U = (4 * REF_X) / (REF_X + (15 * REF_Y) + (3 * REF_Z)); 672 final double ref_V = (9 * REF_Y) / (REF_X + (15 * REF_Y) + (3 * REF_Z)); 673 final double var_U = u / (13 * L) + ref_U; 674 final double var_V = v / (13 * L) + ref_V; 675 676 final double Y = var_Y * 100; 677 final double X = -(9 * Y * var_U) / ((var_U - 4) * var_V - var_U * var_V); 678 final double Z = (9 * Y - (15 * var_V * Y) - (var_V * X)) / (3 * var_V); 679 680 return new ColorXyz(X, Y, Z); 681 } 682 683 public static ColorDin99Lab convertCIELabToDIN99bLab(final ColorCieLab cie) { 684 return convertCIELabToDIN99bLab(cie.L, cie.a, cie.b); 685 } 686 687 public static ColorDin99Lab convertCIELabToDIN99bLab(final double L, final double a, final double b) { 688 final double FAC_1 = 100.0 / Math.log(129.0 / 50.0); // = 105.51 689 final double kE = 1.0; // brightness factor, 1.0 for CIE reference conditions 690 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 691 final double ang = Math.toRadians(16.0); 692 693 final double L99 = kE * FAC_1 * Math.log(1. + 0.0158 * L); 694 double a99 = 0.0; 695 double b99 = 0.0; 696 if (a != 0.0 || b != 0.0) { 697 final double e = a * Math.cos(ang) + b * Math.sin(ang); 698 final double f = 0.7 * (b * Math.cos(ang) - a * Math.sin(ang)); 699 final double G = Math.sqrt(e * e + f * f); 700 if (G != 0.) { 701 final double k = Math.log(1. + 0.045 * G) / (0.045 * kCH * kE * G); 702 a99 = k * e; 703 b99 = k * f; 704 } 705 } 706 return new ColorDin99Lab(L99, a99, b99); 707 } 708 709 public static ColorCieLab convertDIN99bLabToCIELab(final ColorDin99Lab dinb) { 710 return convertDIN99bLabToCIELab(dinb.L99, dinb.a99, dinb.b99); 711 } 712 713 public static ColorCieLab convertDIN99bLabToCIELab(final double L99b, final double a99b, final double b99b) { 714 final double kE = 1.0; // brightness factor, 1.0 for CIE reference conditions 715 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 716 final double FAC_1 = 100.0 / Math.log(129.0 / 50.0); // L99 scaling factor = 105.50867113783109 717 final double ang = Math.toRadians(16.0); 718 719 final double hef = Math.atan2(b99b, a99b); 720 final double C = Math.sqrt(a99b * a99b + b99b * b99b); 721 final double G = (Math.exp(0.045 * C * kCH * kE) - 1.0) / 0.045; 722 final double e = G * Math.cos(hef); 723 final double f = G * Math.sin(hef) / 0.7; 724 725 final double L = (Math.exp(L99b * kE / FAC_1) - 1.) / 0.0158; 726 final double a = e * Math.cos(ang) - f * Math.sin(ang); 727 final double b = e * Math.sin(ang) + f * Math.cos(ang); 728 return new ColorCieLab(L, a, b); 729 } 730 731 /** 732 * DIN99o. 733 * 734 * @param cie CIE color. 735 * @return CIELab colors converted to DIN99oLab color space. 736 * @see <a href="https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 737 */ 738 public static ColorDin99Lab convertCIELabToDIN99oLab(final ColorCieLab cie) { 739 return convertCIELabToDIN99oLab(cie.L, cie.a, cie.b); 740 } 741 742 /** 743 * DIN99o. 744 * 745 * @param L lightness of color. 746 * @param a position between red and green. 747 * @param b position between yellow and blue. 748 * @return CIBELab colors converted to DIN99oLab color space. 749 * @see <a href="https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 750 */ 751 public static ColorDin99Lab convertCIELabToDIN99oLab(final double L, final double a, final double b) { 752 final double kE = 1.0; // brightness factor, 1.0 for CIE reference conditions 753 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 754 final double FAC_1 = 100.0 / Math.log(139.0 / 100.0); // L99 scaling factor = 303.67100547050995 755 final double ang = Math.toRadians(26.0); 756 757 final double L99o = FAC_1 / kE * Math.log(1 + 0.0039 * L); // Lightness correction kE 758 double a99o = 0.0; 759 double b99o = 0.0; 760 if (a != 0.0 || b != 0.0) { 761 final double eo = a * Math.cos(ang) + b * Math.sin(ang); // a stretching 762 final double fo = 0.83 * (b * Math.cos(ang) - a * Math.sin(ang)); // b rotation/stretching 763 final double Go = Math.sqrt(eo * eo + fo * fo); // chroma 764 final double C99o = Math.log(1.0 + 0.075 * Go) / (0.0435 * kCH * kE); // factor for chroma compression and viewing conditions 765 final double heofo = Math.atan2(fo, eo); // arctan in four quadrants 766 final double h99o = heofo + ang; // hue rotation 767 a99o = C99o * Math.cos(h99o); 768 b99o = C99o * Math.sin(h99o); 769 } 770 return new ColorDin99Lab(L99o, a99o, b99o); 771 } 772 773 /** 774 * DIN99o. 775 * 776 * @param dino color in the DIN99 color space. 777 * @return DIN99o colors converted to CIELab color space. 778 * @see <a href="https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 779 */ 780 public static ColorCieLab convertDIN99oLabToCIELab(final ColorDin99Lab dino) { 781 return convertDIN99oLabToCIELab(dino.L99, dino.a99, dino.b99); 782 } 783 784 /** 785 * DIN99o. 786 * 787 * @param L99o lightness of color. 788 * @param a99o position between red and green. 789 * @param b99o position between yellow and blue. 790 * @return DIN99o colors converted to CIELab color space. 791 * @see <a href="https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 792 */ 793 public static ColorCieLab convertDIN99oLabToCIELab(final double L99o, final double a99o, final double b99o) { 794 final double kE = 1.0; // brightness factor, 1.0 for CIE reference conditions 795 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 796 final double FAC_1 = 100.0 / Math.log(139.0 / 100.0); // L99 scaling factor = 303.67100547050995 797 final double ang = Math.toRadians(26.0); 798 799 final double L = (Math.exp(L99o * kE / FAC_1) - 1.0) / 0.0039; 800 801 final double h99ef = Math.atan2(b99o, a99o); // arctan in four quadrants 802 803 final double heofo = h99ef - ang; // backwards hue rotation 804 805 final double C99 = Math.sqrt(a99o * a99o + b99o * b99o); // DIN99 chroma 806 final double G = (Math.exp(0.0435 * kE * kCH * C99) - 1.0) / 0.075; // factor for chroma decompression and viewing conditions 807 final double e = G * Math.cos(heofo); 808 final double f = G * Math.sin(heofo); 809 810 final double a = e * Math.cos(ang) - f / 0.83 * Math.sin(ang); // rotation by 26 degrees 811 final double b = e * Math.sin(ang) + f / 0.83 * Math.cos(ang); // rotation by 26 degrees 812 813 return new ColorCieLab(L, a, b); 814 } 815 816 private static double pivotRGB(double n) { 817 if (n > 0.0031308) { 818 n = 1.055 * Math.pow(n, 1 / 2.4) - 0.055; 819 } else { 820 n = 12.92 * n; 821 } 822 return n; 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 = n / 12.92; 830 } 831 return n; 832 } 833 834 private static double pivotXYZ(double n) { 835 if (n > XYZ_t0) { 836 n = Math.pow(n, 1 / 3.0); 837 } else { 838 n = XYZ_m * n + 16 / 116.0; 839 } 840 return n; 841 } 842 843 private static double unPivotXYZ(double n) { 844 final double nCube = Math.pow(n, 3); 845 if (nCube > XYZ_t0) { 846 n = nCube; 847 } else { 848 n = (n - 16 / 116.0) / XYZ_m; 849 } 850 return n; 851 } 852 853}