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