/*
* Copyright © 2010-2011 Rebecca G. Bettencourt / Kreative Software
* <p>
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a>
* <p>
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
* <p>
* Alternatively, the contents of this file may be used under the terms
* of the GNU Lesser General Public License (the "LGPL License"), in which
* case the provisions of LGPL License are applicable instead of those
* above. If you wish to allow use of your version of this file only
* under the terms of the LGPL License and not to allow others to use
* your version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the LGPL License. If you do not delete
* the provisions above, a recipient may use your version of this file
* under either the MPL or the LGPL License.
* @since KSFL 1.2
* @author Rebecca G. Bettencourt, Kreative Software
*/
package com.kreative.binpack;
import java.awt.Color;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Arrays;
public class ColorFormat {
public static enum ChannelOrder {
// TrueColor with Alpha
ARGB, ARBG, AGRB, AGBR, ABRG, ABGR,
RAGB, RABG, RGAB, RGBA, RBAG, RBGA,
GARB, GABR, GRAB, GRBA, GBAR, GBRA,
BARG, BAGR, BRAG, BRGA, BGAR, BGRA,
// TrueColor without Alpha
RGB, RBG, GRB, GBR, BRG, BGR,
// Grayscale with or without Alpha
AY, YA, Y, A,
// HSV with Alpha
AHSV, AHVS, ASHV, ASVH, AVHS, AVSH,
HASV, HAVS, HSAV, HSVA, HVAS, HVSA,
SAHV, SAVH, SHAV, SHVA, SVAH, SVHA,
VAHS, VASH, VHAS, VHSA, VSAH, VSHA,
// HSV without Alpha
HSV, HVS, SHV, SVH, VHS, VSH,
// HSL with Alpha
AHSL, AHLS, ASHL, ASLH, ALHS, ALSH,
HASL, HALS, HSAL, HSLA, HLAS, HLSA,
SAHL, SALH, SHAL, SHLA, SLAH, SLHA,
LAHS, LASH, LHAS, LHSA, LSAH, LSHA,
// HSL without Alpha
HSL, HLS, SHL, SLH, LHS, LSH,
// CMYK
CMYK, CMKY, CYMK, CYKM, CKMY, CKYM,
MCYK, MCKY, MYCK, MYKC, MKCY, MKYC,
YCMK, YCKM, YMCK, YMKC, YKCM, YKMC,
KCMY, KCYM, KMCY, KMYC, KYCM, KYMC,
// CMY
CMY, CYM, MCY, MYC, YCM, YMC,
// YIQ
YIQ, YQI, IYQ, IQY, QYI, QIY,
// YUV
YUV, YVU, UYV, UVY, VYU, VUY,
// XYZ
XYZ, XZY, YXZ, YZX, ZXY, ZYX;
}
private ChannelOrder channelOrder;
private int[] channelBits;
public ColorFormat(ChannelOrder channelOrder, int... channelBits) {
if (channelOrder == null) {
throw new IllegalArgumentException("Invalid channel order");
} else if (channelBits.length != channelOrder.name().length()) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
this.channelOrder = channelOrder;
this.channelBits = channelBits;
}
}
public ColorFormat(String format) {
String cos = format.replaceAll("[^A-Za-z]", "").toUpperCase();
this.channelOrder = ChannelOrder.valueOf(cos);
if (this.channelOrder == null) {
throw new IllegalArgumentException("Invalid channel order");
}
this.channelBits = new int[this.channelOrder.name().length()];
String[] cbs = format.replaceAll("[^0-9]", " ").trim().split("\\s+");
if (cbs.length == 1 && cbs[0].length() == 0) {
throw new IllegalArgumentException("Number of channels do not match");
} else if (cbs.length != this.channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
for (int i = 0; i < cbs.length; i++) {
this.channelBits[i] = Integer.parseInt(cbs[i]);
}
}
}
public ColorFormat(int width, String format) {
String cos = format.replaceAll("[^A-Za-z]", "").toUpperCase();
this.channelOrder = ChannelOrder.valueOf(cos);
if (this.channelOrder == null) {
throw new IllegalArgumentException("Invalid channel order");
}
this.channelBits = new int[this.channelOrder.name().length()];
String[] cbs = format.replaceAll("[^0-9]", " ").trim().split("\\s+");
if (cbs.length == 1 && cbs[0].length() == 0) cbs = new String[0];
if (cbs.length > this.channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
for (int i = 0; i < cbs.length; i++) {
this.channelBits[i] = Integer.parseInt(cbs[i]);
width -= this.channelBits[i];
}
if (cbs.length < this.channelBits.length) {
int ncr = this.channelBits.length - cbs.length;
int npc = width / ncr;
for (int i = cbs.length; i < this.channelBits.length; i++) {
this.channelBits[i] = npc;
width -= npc;
}
if (width > 0) {
int o;
if ((o = cos.indexOf('G')) >= 0) this.channelBits[o] += width; // GREEN
else if ((o = cos.indexOf('H')) >= 0) this.channelBits[o] += width; // HUE
else if ((o = cos.indexOf('K')) >= 0) this.channelBits[o] += width; // BLACK
else if ((o = cos.indexOf('Y')) >= 0) this.channelBits[o] += width; // GRAY, LUMA
else this.channelBits[this.channelBits.length/2] += width; // ANYTHING
}
}
}
}
public ChannelOrder channelOrder() {
return channelOrder;
}
public int channelCount() {
return channelBits.length;
}
public int channelWidth(int ch) {
return channelBits[ch];
}
public boolean equals(Object o) {
if (o instanceof ColorFormat) {
ColorFormat other = (ColorFormat)o;
if (this.channelOrder != other.channelOrder) return false;
if (this.channelBits.length != other.channelBits.length) return false;
for (int i = 0; i < this.channelBits.length; i++) {
if (this.channelBits[i] != other.channelBits[i]) return false;
}
return true;
} else {
return false;
}
}
public int hashCode() {
return channelOrder.hashCode() ^ Arrays.hashCode(channelBits);
}
public String toString() {
String cos = channelOrder.name().toLowerCase();
StringBuffer s = new StringBuffer();
for (int i = 0; i < channelBits.length; i++) {
s.append(cos.charAt(i));
s.append(channelBits[i]);
}
return s.toString();
}
public float[] toFloatArray(Number[] values) {
if (values.length != channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
float[] r = new float[values.length];
for (int i = 0; i < values.length; i++) {
r[i] = values[i].floatValue() / BigInteger.ONE.shiftLeft(channelBits[i]).subtract(BigInteger.ONE).floatValue();
}
return r;
}
}
public float[] toFloatArray(long[] values) {
if (values.length != channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
float[] r = new float[values.length];
for (int i = 0; i < values.length; i++) {
r[i] = (float)values[i] / BigInteger.ONE.shiftLeft(channelBits[i]).subtract(BigInteger.ONE).floatValue();
}
return r;
}
}
public float[] toFloatArray(int[] values) {
if (values.length != channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
float[] r = new float[values.length];
for (int i = 0; i < values.length; i++) {
r[i] = (float)values[i] / BigInteger.ONE.shiftLeft(channelBits[i]).subtract(BigInteger.ONE).floatValue();
}
return r;
}
}
public BigInteger[] toBigIntArray(float[] values) {
if (values.length != channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
MathContext mc = MathContext.DECIMAL128;
BigInteger[] r = new BigInteger[values.length];
for (int i = 0; i < values.length; i++) {
BigInteger m = BigInteger.ONE.shiftLeft(channelBits[i]).subtract(BigInteger.ONE);
r[i] = BigDecimal.valueOf(values[i]).multiply(new BigDecimal(m, mc), mc).toBigInteger();
}
return r;
}
}
public long[] toLongArray(float[] values) {
if (values.length != channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
long[] r = new long[values.length];
for (int i = 0; i < values.length; i++) {
r[i] = (long)((double)values[i] * BigInteger.ONE.shiftLeft(channelBits[i]).subtract(BigInteger.ONE).doubleValue());
}
return r;
}
}
public int[] toIntArray(float[] values) {
if (values.length != channelBits.length) {
throw new IllegalArgumentException("Number of channels do not match");
} else {
int[] r = new int[values.length];
for (int i = 0; i < values.length; i++) {
r[i] = (int)((double)values[i] * BigInteger.ONE.shiftLeft(channelBits[i]).subtract(BigInteger.ONE).doubleValue());
}
return r;
}
}
public float[] toRGBAFloatArray(float[] values) {
String cos = channelOrder.name().toUpperCase();
switch (channelOrder) {
case ARGB: case ARBG: case AGRB: case AGBR: case ABRG: case ABGR:
case RAGB: case RABG: case RGAB: case RGBA: case RBAG: case RBGA:
case GARB: case GABR: case GRAB: case GRBA: case GBAR: case GBRA:
case BARG: case BAGR: case BRAG: case BRGA: case BGAR: case BGRA:
return new float[] {
values[cos.indexOf('R')],
values[cos.indexOf('G')],
values[cos.indexOf('B')],
values[cos.indexOf('A')],
};
case RGB: case RBG: case GRB: case GBR: case BRG: case BGR:
return new float[] {
values[cos.indexOf('R')],
values[cos.indexOf('G')],
values[cos.indexOf('B')],
1.0f,
};
case AY: case YA:
return new float[] {
values[cos.indexOf('Y')],
values[cos.indexOf('Y')],
values[cos.indexOf('Y')],
values[cos.indexOf('A')],
};
case Y:
return new float[] {
values[cos.indexOf('Y')],
values[cos.indexOf('Y')],
values[cos.indexOf('Y')],
1.0f,
};
case A:
return new float[] {
0.0f,
0.0f,
0.0f,
values[cos.indexOf('A')],
};
case AHSV: case AHVS: case ASHV: case ASVH: case AVHS: case AVSH:
case HASV: case HAVS: case HSAV: case HSVA: case HVAS: case HVSA:
case SAHV: case SAVH: case SHAV: case SHVA: case SVAH: case SVHA:
case VAHS: case VASH: case VHAS: case VHSA: case VSAH: case VSHA:
int rgb1 = Color.HSBtoRGB(
values[cos.indexOf('H')],
values[cos.indexOf('S')],
values[cos.indexOf('V')]
);
return new float[] {
((rgb1 >> 16) & 0xFF) / 255.0f,
((rgb1 >> 8) & 0xFF) / 255.0f,
(rgb1 & 0xFF) / 255.0f,
values[cos.indexOf('A')],
};
case HSV: case HVS: case SHV: case SVH: case VHS: case VSH:
int rgb2 = Color.HSBtoRGB(
values[cos.indexOf('H')],
values[cos.indexOf('S')],
values[cos.indexOf('V')]
);
return new float[] {
((rgb2 >> 16) & 0xFF) / 255.0f,
((rgb2 >> 8) & 0xFF) / 255.0f,
(rgb2 & 0xFF) / 255.0f,
1.0f,
};
case AHSL: case AHLS: case ASHL: case ASLH: case ALHS: case ALSH:
case HASL: case HALS: case HSAL: case HSLA: case HLAS: case HLSA:
case SAHL: case SALH: case SHAL: case SHLA: case SLAH: case SLHA:
case LAHS: case LASH: case LHAS: case LHSA: case LSAH: case LSHA:
float hh1 = values[cos.indexOf('H')];
float ss1 = values[cos.indexOf('S')];
float ll1 = values[cos.indexOf('L')];
float h1 = hh1;
ll1 *= 2;
ss1 *= (ll1 <= 1) ? ll1 : 2 - ll1;
float v1 = (ll1 + ss1) / 2;
float s1 = ((ll1 + ss1) == 0) ? 0 : ((2 * ss1) / (ll1 + ss1));
int rgb3 = Color.HSBtoRGB(h1, s1, v1);
return new float[] {
((rgb3 >> 16) & 0xFF) / 255.0f,
((rgb3 >> 8) & 0xFF) / 255.0f,
(rgb3 & 0xFF) / 255.0f,
values[cos.indexOf('A')],
};
case HSL: case HLS: case SHL: case SLH: case LHS: case LSH:
float hh2 = values[cos.indexOf('H')];
float ss2 = values[cos.indexOf('S')];
float ll2 = values[cos.indexOf('L')];
float h2 = hh2;
ll2 *= 2;
ss2 *= (ll2 <= 1) ? ll2 : 2 - ll2;
float v2 = (ll2 + ss2) / 2;
float s2 = ((ll2 + ss2) == 0) ? 0 : ((2 * ss2) / (ll2 + ss2));
int rgb4 = Color.HSBtoRGB(h2, s2, v2);
return new float[] {
((rgb4 >> 16) & 0xFF) / 255.0f,
((rgb4 >> 8) & 0xFF) / 255.0f,
(rgb4 & 0xFF) / 255.0f,
1.0f,
};
case CMYK: case CMKY: case CYMK: case CYKM: case CKMY: case CKYM:
case MCYK: case MCKY: case MYCK: case MYKC: case MKCY: case MKYC:
case YCMK: case YCKM: case YMCK: case YMKC: case YKCM: case YKMC:
case KCMY: case KCYM: case KMCY: case KMYC: case KYCM: case KYMC:
float k1 = values[cos.indexOf('K')];
float c1 = k1 + (values[cos.indexOf('C')] * (1.0f-k1));
float m1 = k1 + (values[cos.indexOf('M')] * (1.0f-k1));
float y1 = k1 + (values[cos.indexOf('Y')] * (1.0f-k1));
return new float[] { 1.0f - c1, 1.0f - m1, 1.0f - y1, 1.0f };
case CMY: case CYM: case MCY: case MYC: case YCM: case YMC:
return new float[] {
1.0f - values[cos.indexOf('C')],
1.0f - values[cos.indexOf('M')],
1.0f - values[cos.indexOf('Y')],
1.0f,
};
case YIQ: case YQI: case IYQ: case IQY: case QYI: case QIY:
double[] rgb5 = YIQtoRGB(
values[cos.indexOf('Y')],
values[cos.indexOf('I')],
values[cos.indexOf('Q')]
);
return new float[]{ (float)rgb5[0], (float)rgb5[1], (float)rgb5[2], 1.0f };
case YUV: case YVU: case UYV: case UVY: case VYU: case VUY:
double[] rgb6 = YUVtoRGB(
values[cos.indexOf('Y')],
values[cos.indexOf('U')],
values[cos.indexOf('V')]
);
return new float[]{ (float)rgb6[0], (float)rgb6[1], (float)rgb6[2], 1.0f };
case XYZ: case XZY: case YXZ: case YZX: case ZXY: case ZYX:
double[] rgb8 = XYZtosRGB(
values[cos.indexOf('X')],
values[cos.indexOf('Y')],
values[cos.indexOf('Z')]
);
return new float[]{ (float)rgb8[0], (float)rgb8[1], (float)rgb8[2], 1.0f };
default:
throw new IllegalArgumentException("Invalid channel order");
}
}
public float[] fromRGBAFloatArray(float[] rgb) {
float[] values;
String cos = channelOrder.name().toUpperCase();
switch (channelOrder) {
case ARGB: case ARBG: case AGRB: case AGBR: case ABRG: case ABGR:
case RAGB: case RABG: case RGAB: case RGBA: case RBAG: case RBGA:
case GARB: case GABR: case GRAB: case GRBA: case GBAR: case GBRA:
case BARG: case BAGR: case BRAG: case BRGA: case BGAR: case BGRA:
values = new float[4];
values[cos.indexOf('R')] = rgb[0];
values[cos.indexOf('G')] = rgb[1];
values[cos.indexOf('B')] = rgb[2];
values[cos.indexOf('A')] = rgb[3];
return values;
case RGB: case RBG: case GRB: case GBR: case BRG: case BGR:
values = new float[3];
values[cos.indexOf('R')] = rgb[0];
values[cos.indexOf('G')] = rgb[1];
values[cos.indexOf('B')] = rgb[2];
return values;
case AY: case YA:
values = new float[2];
values[cos.indexOf('Y')] = 0.3f*rgb[0] + 0.59f*rgb[1] + 0.11f*rgb[2];
values[cos.indexOf('A')] = rgb[3];
return values;
case Y:
values = new float[1];
values[cos.indexOf('Y')] = 0.3f*rgb[0] + 0.59f*rgb[1] + 0.11f*rgb[2];
return values;
case A:
values = new float[1];
values[cos.indexOf('A')] = rgb[3];
return values;
case AHSV: case AHVS: case ASHV: case ASVH: case AVHS: case AVSH:
case HASV: case HAVS: case HSAV: case HSVA: case HVAS: case HVSA:
case SAHV: case SAVH: case SHAV: case SHVA: case SVAH: case SVHA:
case VAHS: case VASH: case VHAS: case VHSA: case VSAH: case VSHA:
values = new float[4];
float[] hsv1 = Color.RGBtoHSB(
(int)(rgb[0]*255.0f),
(int)(rgb[1]*255.0f),
(int)(rgb[2]*255.0f),
null
);
values[cos.indexOf('H')] = hsv1[0];
values[cos.indexOf('S')] = hsv1[1];
values[cos.indexOf('V')] = hsv1[2];
values[cos.indexOf('A')] = rgb[3];
return values;
case HSV: case HVS: case SHV: case SVH: case VHS: case VSH:
values = new float[3];
float[] hsv2 = Color.RGBtoHSB(
(int)(rgb[0]*255.0f),
(int)(rgb[1]*255.0f),
(int)(rgb[2]*255.0f),
null
);
values[cos.indexOf('H')] = hsv2[0];
values[cos.indexOf('S')] = hsv2[1];
values[cos.indexOf('V')] = hsv2[2];
return values;
case AHSL: case AHLS: case ASHL: case ASLH: case ALHS: case ALSH:
case HASL: case HALS: case HSAL: case HSLA: case HLAS: case HLSA:
case SAHL: case SALH: case SHAL: case SHLA: case SLAH: case SLHA:
case LAHS: case LASH: case LHAS: case LHSA: case LSAH: case LSHA:
values = new float[4];
float[] hsv3 = Color.RGBtoHSB(
(int)(rgb[0]*255.0f),
(int)(rgb[1]*255.0f),
(int)(rgb[2]*255.0f),
null
);
float hh1 = hsv3[0];
float ll1 = (2 - hsv3[1]) * hsv3[2];
float ss1 = hsv3[1] * hsv3[2];
if (ll1 != 0) ss1 /= (ll1 <= 1) ? ll1 : 2 - ll1;
ll1 /= 2;
values[cos.indexOf('H')] = hh1;
values[cos.indexOf('S')] = ss1;
values[cos.indexOf('L')] = ll1;
values[cos.indexOf('A')] = rgb[3];
return values;
case HSL: case HLS: case SHL: case SLH: case LHS: case LSH:
values = new float[3];
float[] hsv4 = Color.RGBtoHSB(
(int)(rgb[0]*255.0f),
(int)(rgb[1]*255.0f),
(int)(rgb[2]*255.0f),
null
);
float hh2 = hsv4[0];
float ll2 = (2 - hsv4[1]) * hsv4[2];
float ss2 = hsv4[1] * hsv4[2];
if (ll2 != 0) ss2 /= (ll2 <= 1) ? ll2 : 2 - ll2;
ll2 /= 2;
values[cos.indexOf('H')] = hh2;
values[cos.indexOf('S')] = ss2;
values[cos.indexOf('L')] = ll2;
return values;
case CMYK: case CMKY: case CYMK: case CYKM: case CKMY: case CKYM:
case MCYK: case MCKY: case MYCK: case MYKC: case MKCY: case MKYC:
case YCMK: case YCKM: case YMCK: case YMKC: case YKCM: case YKMC:
case KCMY: case KCYM: case KMCY: case KMYC: case KYCM: case KYMC:
values = new float[4];
float c1 = 1.0f - rgb[0];
float m1 = 1.0f - rgb[1];
float y1 = 1.0f - rgb[2];
float k1 = Math.min(c1, Math.min(m1, y1));
values[cos.indexOf('C')] = ((1.0f-k1) == 0) ? 0.0f : ((c1-k1)/(1.0f-k1));
values[cos.indexOf('M')] = ((1.0f-k1) == 0) ? 0.0f : ((m1-k1)/(1.0f-k1));
values[cos.indexOf('Y')] = ((1.0f-k1) == 0) ? 0.0f : ((y1-k1)/(1.0f-k1));
values[cos.indexOf('K')] = k1;
return values;
case CMY: case CYM: case MCY: case MYC: case YCM: case YMC:
values = new float[3];
values[cos.indexOf('C')] = 1.0f - rgb[0];
values[cos.indexOf('M')] = 1.0f - rgb[1];
values[cos.indexOf('Y')] = 1.0f - rgb[2];
return values;
case YIQ: case YQI: case IYQ: case IQY: case QYI: case QIY:
values = new float[3];
double[] yiq = RGBtoYIQ( rgb[0], rgb[1], rgb[2] );
values[cos.indexOf('Y')] = (float)yiq[0];
values[cos.indexOf('I')] = (float)yiq[1];
values[cos.indexOf('Q')] = (float)yiq[2];
return values;
case YUV: case YVU: case UYV: case UVY: case VYU: case VUY:
values = new float[3];
double[] yuv = RGBtoYUV( rgb[0], rgb[1], rgb[2] );
values[cos.indexOf('Y')] = (float)yuv[0];
values[cos.indexOf('U')] = (float)yuv[1];
values[cos.indexOf('V')] = (float)yuv[2];
return values;
case XYZ: case XZY: case YXZ: case YZX: case ZXY: case ZYX:
values = new float[3];
double[] xyz = sRGBtoXYZ( rgb[0], rgb[1], rgb[2] );
values[cos.indexOf('X')] = (float)xyz[0];
values[cos.indexOf('Y')] = (float)xyz[1];
values[cos.indexOf('Z')] = (float)xyz[2];
return values;
default:
throw new IllegalArgumentException("Invalid channel order");
}
}
private static final double[] RGBtoYIQ(double r, double g, double b) {
double y = +0.3000*r +0.5900*g +0.1100*b;
double i = +0.5990*r -0.2773*g -0.3217*b;
double q = +0.2130*r -0.5251*g +0.3121*b;
return new double[] { y, i, q };
}
private static final double[] YIQtoRGB(double y, double i, double q) {
double r = y +0.9469*i +0.6236*q;
double g = y -0.2748*i -0.6357*q;
double b = y -1.1086*i +1.7090*q;
return new double[] { r, g, b };
}
private static final double[] RGBtoYUV(double r, double g, double b) {
double y = +0.29900*r +0.58700*g +0.11400*b;
double u = -0.14713*r -0.28886*g +0.43600*b;
double v = +0.61500*r -0.51499*g -0.10001*b;
return new double[] { y, u, v };
}
private static final double[] YUVtoRGB(double y, double u, double v) {
double r = y +1.13983*v;
double g = y -0.39465*u -0.58060*v;
double b = y +2.03211*u ;
return new double[] { r, g, b };
}
private static final double[] XYZtosRGB(double x, double y, double z) {
double rl = +3.2410*x -1.5374*y -0.4986*z;
double gl = -0.9692*x +1.8760*y +0.0416*z;
double bl = +0.0556*x -0.2040*y +1.0570*z;
double r = (rl <= 0.0031308) ? (12.92 * rl) : (1.055 * Math.pow(rl, 1/2.4) - 0.055);
double g = (gl <= 0.0031308) ? (12.92 * gl) : (1.055 * Math.pow(gl, 1/2.4) - 0.055);
double b = (bl <= 0.0031308) ? (12.92 * bl) : (1.055 * Math.pow(bl, 1/2.4) - 0.055);
return new double[] { r, g, b };
}
private static final double[] sRGBtoXYZ(double r, double g, double b) {
double rl = (r <= 0.04045) ? (r / 12.92) : Math.pow((r+0.055)/1.055, 2.4);
double gl = (g <= 0.04045) ? (g / 12.92) : Math.pow((g+0.055)/1.055, 2.4);
double bl = (b <= 0.04045) ? (b / 12.92) : Math.pow((b+0.055)/1.055, 2.4);
double x = +0.4124*rl +0.3576*gl +0.1805*bl;
double y = +0.2126*rl +0.7152*gl +0.0722*bl;
double z = +0.0193*rl +0.1192*gl +0.9505*bl;
return new double[] { x, y, z };
}
}