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 019public final class ColorConversions { 020 021 // White reference 022 /** See: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 023 private static final double REF_X = 95.047; // Observer= 2°, Illuminant= D65 024 025 /** See: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 026 private static final double REF_Y = 100.000; 027 028 /** See: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 029 private static final double REF_Z = 108.883; 030 031 /** See: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 032 private static final double XYZ_m = 7.787037; // match in slope. Note commonly seen 7.787 gives worse results 033 034 /** See: https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB[10] */ 035 private static final double XYZ_t0 = 0.008856; 036 037 public static int convertCieLabToArgbTest(final int cieL, final int cieA, final int cieB) { 038 double x, y, z; 039 { 040 041 double varY = (cieL * 100.0 / 255.0 + 16.0) / 116.0; 042 double varX = cieA / 500.0 + varY; 043 double varZ = varY - cieB / 200.0; 044 045 varX = unPivotXyz(varX); 046 varY = unPivotXyz(varY); 047 varZ = unPivotXyz(varZ); 048 049 x = REF_X * varX; // REF_X = 95.047 Observer= 2°, Illuminant= D65 050 y = REF_Y * varY; // REF_Y = 100.000 051 z = REF_Z * varZ; // REF_Z = 108.883 052 053 } 054 055 double r, g, b; 056 { 057 final double varX = x / 100; // X = From 0 to REF_X 058 final double varY = y / 100; // Y = From 0 to REF_Y 059 final double varZ = z / 100; // Z = From 0 to REF_Y 060 061 double varR = varX * 3.2406 + varY * -1.5372 + varZ * -0.4986; 062 double varG = varX * -0.9689 + varY * 1.8758 + varZ * 0.0415; 063 double varB = varX * 0.0557 + varY * -0.2040 + varZ * 1.0570; 064 065 varR = pivotRgb(varR); 066 varG = pivotRgb(varG); 067 varB = pivotRgb(varB); 068 069 r = varR * 255; 070 g = varG * 255; 071 b = varB * 255; 072 } 073 074 return convertRgbToRgb(r, g, b); 075 } 076 077 public static ColorCieLch convertCieLabToCieLch(final ColorCieLab cielab) { 078 return convertCieLabToCieLch(cielab.l, cielab.a, cielab.b); 079 } 080 081 public static ColorCieLch convertCieLabToCieLch(final double l, final double a, final double b) { 082 // atan2(y,x) returns atan(y/x) 083 final double atanba = Math.atan2(b, a); // Quadrant by signs 084 085 final double h = atanba > 0 // 086 ? Math.toDegrees(atanba) // 087 : Math.toDegrees(atanba) + 360; 088 089 // L = L; 090 final double C = Math.sqrt(square(a) + square(b)); 091 092 return new ColorCieLch(l, C, h); 093 } 094 095 public static ColorDin99Lab convertCieLabToDin99bLab(final ColorCieLab cie) { 096 return convertCieLabToDin99bLab(cie.l, cie.a, cie.b); 097 } 098 099 public static ColorDin99Lab convertCieLabToDin99bLab(final double l, final double a, final double b) { 100 final double fac1 = 100.0 / Math.log(129.0 / 50.0); // = 105.51 101 final double kE = 1.0; // brightness factor, 1.0 for CIE reference conditions 102 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 103 final double ang = Math.toRadians(16.0); 104 105 final double l99 = kE * fac1 * Math.log(1. + 0.0158 * l); 106 double a99 = 0.0; 107 double b99 = 0.0; 108 if (a != 0.0 || b != 0.0) { 109 final double e = a * Math.cos(ang) + b * Math.sin(ang); 110 final double f = 0.7 * (b * Math.cos(ang) - a * Math.sin(ang)); 111 final double G = Math.sqrt(e * e + f * f); 112 if (G != 0.) { 113 final double k = Math.log(1. + 0.045 * G) / (0.045 * kCH * kE * G); 114 a99 = k * e; 115 b99 = k * f; 116 } 117 } 118 return new ColorDin99Lab(l99, a99, b99); 119 } 120 121 /** 122 * DIN99o. 123 * 124 * @param cie CIE color. 125 * @return CIELab colors converted to DIN99oLab color space. 126 * @see <a href= 127 * "https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 128 */ 129 public static ColorDin99Lab convertCieLabToDin99oLab(final ColorCieLab cie) { 130 return convertCieLabToDin99oLab(cie.l, cie.a, cie.b); 131 } 132 133 /** 134 * DIN99o. 135 * 136 * @param l lightness of color. 137 * @param a position between red and green. 138 * @param b position between yellow and blue. 139 * @return CIBELab colors converted to DIN99oLab color space. 140 * @see <a href= 141 * "https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 142 */ 143 public static ColorDin99Lab convertCieLabToDin99oLab(final double l, final double a, final double b) { 144 final double kE = 1.0; // brightness factor, 1.0 for CIE reference conditions 145 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 146 final double fac1 = 100.0 / Math.log(139.0 / 100.0); // L99 scaling factor = 303.67100547050995 147 final double ang = Math.toRadians(26.0); 148 149 final double l99o = fac1 / kE * Math.log(1 + 0.0039 * l); // Lightness correction kE 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); // a stretching 154 final double fo = 0.83 * (b * Math.cos(ang) - a * Math.sin(ang)); // b rotation/stretching 155 final double Go = Math.sqrt(eo * eo + fo * fo); // chroma 156 final double C99o = Math.log(1.0 + 0.075 * Go) / (0.0435 * kCH * kE); // factor for chroma compression and viewing conditions 157 final double heofo = Math.atan2(fo, eo); // arctan in four quadrants 158 final double h99o = heofo + ang; // hue rotation 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; // REF_X = 95.047 Observer= 2°, Illuminant= 179 // D65 180 final double y = REF_Y * varY; // REF_Y = 100.000 181 final double z = REF_Z * varZ; // REF_Z = 108.883 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 // Where CIE-H° = 0 ÷ 360° 192 193 // CIE-L* = CIE-L; 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 // problems here with div by zero 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 // Where CMYK and CMY values = 0 ÷ 1 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 // Where CMYK and CMY values = 0 ÷ 1 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) { // Black 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 // From Ghostscript's gdevcdj.c: 285 // * Ghostscript: R = (1.0 - C) * (1.0 - K) 286 // * Adobe: R = 1.0 - min(1.0, C + K) 287 // and similarly for G and B. 288 // This is Ghostscript's formula with K = 0. 289 290 // CMY values = 0 ÷ 1 291 // RGB values = 0 ÷ 255 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; // brightness factor, 1.0 for CIE reference conditions 306 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 307 final double fac1 = 100.0 / Math.log(129.0 / 50.0); // L99 scaling factor = 105.50867113783109 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 * DIN99o. 324 * 325 * @param dino color in the DIN99 color space. 326 * @return DIN99o colors converted to CIELab color space. 327 * @see <a href= 328 * "https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 329 */ 330 public static ColorCieLab convertDin99oLabToCieLab(final ColorDin99Lab dino) { 331 return convertDin99oLabToCieLab(dino.l99, dino.a99, dino.b99); 332 } 333 334 /** 335 * DIN99o. 336 * 337 * @param l99o lightness of color. 338 * @param a99o position between red and green. 339 * @param b99o position between yellow and blue. 340 * @return DIN99o colors converted to CIELab color space. 341 * @see <a href= 342 * "https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum">https://de.wikipedia.org/w/index.php?title=Diskussion:DIN99-Farbraum</a> 343 */ 344 public static ColorCieLab convertDin99oLabToCieLab(final double l99o, final double a99o, final double b99o) { 345 final double kE = 1.0; // brightness factor, 1.0 for CIE reference conditions 346 final double kCH = 1.0; // chroma and hue factor, 1.0 for CIE reference conditions 347 final double fac1 = 100.0 / Math.log(139.0 / 100.0); // L99 scaling factor = 303.67100547050995 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); // arctan in four quadrants 353 354 final double heofo = h99ef - ang; // backwards hue rotation 355 356 final double c99 = Math.sqrt(a99o * a99o + b99o * b99o); // DIN99 chroma 357 final double g = (Math.exp(0.0435 * kE * kCH * c99) - 1.0) / 0.075; // factor for chroma decompression and viewing conditions 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); // rotation by 26 degrees 362 final double b = e * Math.sin(ang) + f / 0.83 * Math.cos(ang); // rotation by 26 degrees 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 // HSL values = 0 ÷ 1 376 r = l * 255; // RGB results = 0 ÷ 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 // HSV values = 0 ÷ 1 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; // H must be < 1 414 } 415 final double varI = Math.floor(varH); // Or ... varI = 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; // RGB results = 0 ÷ 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 // RGB values = 0 ÷ 255 497 // CMY values = 0 ÷ 1 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; // Where RGB values = 0 ÷ 255 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)); // Min. value 517 // of RGB 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; // Delta RGB value 531 532 final double l = (varMax + varMin) / 2.0; 533 534 double h, s; 535 // Debug.debug("del_Max", del_Max); 536 if (delMax == 0) { 537 // This is a gray, no chroma... 538 539 h = 0; // HSL results = 0 ÷ 1 540 s = 0; 541 } else { 542 // Chromatic data... 543 544 // Debug.debug("L", L); 545 546 if (l < 0.5) { 547 s = delMax / (varMax + varMin); 548 } else { 549 s = delMax / (2 - varMax - varMin); 550 } 551 552 // Debug.debug("S", S); 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 // Debug.debug("H1", H); 567 568 if (h < 0) { 569 h += 1; 570 } 571 if (h > 1) { 572 h -= 1; 573 } 574 575 // Debug.debug("H2", H); 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; // RGB values = 0 ÷ 255 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)); // Min. value 591 // of RGB 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; // Delta RGB value 605 606 final double v = varMax; 607 608 double h, s; 609 if (delMax == 0) { 610 // This is a gray, no chroma... 611 h = 0; // HSV results = 0 ÷ 1 612 s = 0; 613 } else { 614 // Chromatic data... 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 // See also c# implementation: 665 // https://github.com/muak/ColorMinePortable/blob/master/ColorMinePortable/ColorSpaces/Conversions/XyzConverter.cs 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; // Where R = 0 ÷ 255 672 double varG = g / 255.0; // Where G = 0 ÷ 255 673 double varB = b / 255.0; // Where B = 0 ÷ 255 674 675 // Pivot RGB: 676 varR = unPivotRgb(varR); 677 varG = unPivotRgb(varG); 678 varB = unPivotRgb(varB); 679 680 varR *= 100; 681 varG *= 100; 682 varB *= 100; 683 684 // Observer. = 2°, Illuminant = D65 685 // see: https://github.com/StanfordHCI/c3/blob/master/java/src/edu/stanford/vis/color/LAB.java 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 // Attention: A lot of sources do list these values with less precision. But it makes a visual difference: 691 // final double X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805; 692 // final double Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722; 693 // final double Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505; 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 // problems here with div by zero 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 // Debug.debug("var_U", var_U); 705 // Debug.debug("var_V", var_V); 706 707 double varY = y / 100.0; 708 // Debug.debug("var_Y", var_Y); 709 710 varY = pivotXyz(varY); 711 712 // Debug.debug("var_Y", var_Y); 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 // Debug.debug("ref_U", ref_U); 718 // Debug.debug("ref_V", ref_V); 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; // REF_X = 95.047 Observer= 2°, Illuminant= D65 734 double varY = y / REF_Y; // REF_Y = 100.000 735 double varZ = z / REF_Z; // REF_Z = 108.883 736 737 // Pivot XÝZ: 738 varX = pivotXyz(varX); 739 varY = pivotXyz(varY); 740 varZ = pivotXyz(varZ); 741 742 // Math.max added from https://github.com/muak/ColorMinePortable/blob/master/ColorMinePortable/ColorSpaces/Conversions/LabConverter.cs 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 // Observer = 2°, Illuminant = D65 771 final double varX = x / 100.0; // Where X = 0 ÷ 95.047 772 final double varY = y / 100.0; // Where Y = 0 ÷ 100.000 773 final double varZ = z / 100.0; // Where Z = 0 ÷ 108.883 774 775 // see: https://github.com/StanfordHCI/c3/blob/master/java/src/edu/stanford/vis/color/LAB.java 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 // Attention: A lot of sources do list these values with less precision. But it makes a visual difference: 781 // double var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986; 782 // double var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415; 783 // double var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570; 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}