-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pending changes exported from your codespace
- Loading branch information
Showing
2 changed files
with
466 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,374 @@ | ||
package org.hsluv; | ||
|
||
public class HsluvColorConverter { | ||
private static double refY = 1.0; | ||
private static double refU = 0.19783000664283; | ||
private static double refV = 0.46831999493879; | ||
private static double kappa = 903.2962962; | ||
private static double epsilon = 0.0088564516; | ||
private static double m_r0 = 3.240969941904521; | ||
private static double m_r1 = -1.537383177570093; | ||
private static double m_r2 = -0.498610760293; | ||
private static double m_g0 = -0.96924363628087; | ||
private static double m_g1 = 1.87596750150772; | ||
private static double m_g2 = 0.041555057407175; | ||
private static double m_b0 = 0.055630079696993; | ||
private static double m_b1 = -0.20397695888897; | ||
private static double m_b2 = 1.056971514242878; | ||
|
||
// RGB | ||
public String hex = "#000000"; | ||
public double rgb_r = 0; | ||
public double rgb_g = 0; | ||
public double rgb_b = 0; | ||
|
||
// CIE XYZ | ||
public double xyz_x = 0; | ||
public double xyz_y = 0; | ||
public double xyz_z = 0; | ||
|
||
// CIE LUV | ||
public double luv_l = 0; | ||
public double luv_u = 0; | ||
public double luv_v = 0; | ||
|
||
// CIE LUV LCh | ||
public double lch_l = 0; | ||
public double lch_c = 0; | ||
public double lch_h = 0; | ||
|
||
// HSLuv | ||
public double hsluv_h = 0; | ||
public double hsluv_s = 0; | ||
public double hsluv_l = 0; | ||
|
||
// HPLuv | ||
public double hpluv_h = 0; | ||
public double hpluv_p = 0; | ||
public double hpluv_l = 0; | ||
|
||
// 6 lines in slope-intercept format: R < 0, R > 1, G < 0, G > 1, B < 0, B > 1 | ||
public double r0s = 0; | ||
public double r0i = 0; | ||
public double r1s = 0; | ||
public double r1i = 0; | ||
|
||
public double g0s = 0; | ||
public double g0i = 0; | ||
public double g1s = 0; | ||
public double g1i = 0; | ||
|
||
public double b0s = 0; | ||
public double b0i = 0; | ||
public double b1s = 0; | ||
public double b1i = 0; | ||
|
||
private static double fromLinear(double c) { | ||
if (c <= 0.0031308) { | ||
return 12.92 * c; | ||
} else { | ||
return 1.055 * Math.pow(c, 1 / 2.4) - 0.055; | ||
} | ||
} | ||
|
||
private static double toLinear(double c) { | ||
if (c > 0.04045) { | ||
return Math.pow((c + 0.055) / 1.055, 2.4); | ||
} else { | ||
return c / 12.92; | ||
} | ||
} | ||
|
||
private static double yToL(double Y) { | ||
if (Y <= HsluvColorConverter.epsilon) { | ||
return Y / HsluvColorConverter.refY * HsluvColorConverter.kappa; | ||
} else { | ||
return 116 * Math.pow(Y / HsluvColorConverter.refY, 1 / 3) - 16; | ||
} | ||
} | ||
|
||
private static double lToY(double L) { | ||
if (L <= 8) { | ||
return HsluvColorConverter.refY * L / HsluvColorConverter.kappa; | ||
} else { | ||
return HsluvColorConverter.refY * Math.pow((L + 16) / 116, 3); | ||
} | ||
} | ||
|
||
private static String rgbChannelToHex(double chan) { | ||
return String.format("%02x", chan); | ||
} | ||
|
||
private static double hexToRgbChannel(String hex, int offset) { | ||
return (double) Integer.parseInt(hex.substring(offset, offset + 2)); | ||
} | ||
|
||
private static double distanceFromOriginAngle(double slope, double intercept, double angle) { | ||
double d = intercept / (Math.sin(angle) - slope * Math.cos(angle)); | ||
if (d < 0) { | ||
return Double.POSITIVE_INFINITY; | ||
} else { | ||
return d; | ||
} | ||
} | ||
|
||
private static double distanceFromOrigin(double slope, double intercept) { | ||
return Math.abs(intercept) / Math.sqrt(Math.pow(slope, 2) + 1); | ||
} | ||
|
||
private static double min6(double f1, double f2, double f3, double f4, double f5, double f6) { | ||
return Math.min(f1, Math.min(f2, Math.min(f3, Math.min(f4, Math.min(f5, f6))))); | ||
} | ||
|
||
public void rgbToHex() { | ||
this.hex = "#"; | ||
this.hex += HsluvColorConverter.rgbChannelToHex(this.rgb_r); | ||
this.hex += HsluvColorConverter.rgbChannelToHex(this.rgb_g); | ||
this.hex += HsluvColorConverter.rgbChannelToHex(this.rgb_b); | ||
} | ||
|
||
public void hexToRgb() { | ||
this.hex = this.hex.toLowerCase(); | ||
this.rgb_r = HsluvColorConverter.hexToRgbChannel(this.hex, 1); | ||
this.rgb_g = HsluvColorConverter.hexToRgbChannel(this.hex, 3); | ||
this.rgb_b = HsluvColorConverter.hexToRgbChannel(this.hex, 5); | ||
} | ||
|
||
public void xyzToRgb() { | ||
this.rgb_r = HsluvColorConverter.fromLinear(HsluvColorConverter.m_r0 * this.xyz_x | ||
+ HsluvColorConverter.m_r1 * this.xyz_y + HsluvColorConverter.m_r2 * this.xyz_z); | ||
this.rgb_g = HsluvColorConverter.fromLinear(HsluvColorConverter.m_g0 * this.xyz_x | ||
+ HsluvColorConverter.m_g1 * this.xyz_y + HsluvColorConverter.m_g2 * this.xyz_z); | ||
this.rgb_b = HsluvColorConverter.fromLinear(HsluvColorConverter.m_b0 * this.xyz_x | ||
+ HsluvColorConverter.m_b1 * this.xyz_y + HsluvColorConverter.m_b2 * this.xyz_z); | ||
} | ||
|
||
public void rgbToXyz() { | ||
double lr = HsluvColorConverter.toLinear(this.rgb_r); | ||
double lg = HsluvColorConverter.toLinear(this.rgb_g); | ||
double lb = HsluvColorConverter.toLinear(this.rgb_b); | ||
this.xyz_x = 0.41239079926595 * lr + 0.35758433938387 * lg + 0.18048078840183 * lb; | ||
this.xyz_y = 0.21263900587151 * lr + 0.71516867876775 * lg + 0.072192315360733 * lb; | ||
this.xyz_z = 0.019330818715591 * lr + 0.11919477979462 * lg + 0.95053215224966 * lb; | ||
} | ||
|
||
public void xyzToLuv() { | ||
double divider = this.xyz_x + 15 * this.xyz_y + 3 * this.xyz_z; | ||
double varU = 4 * this.xyz_x; | ||
double varV = 9 * this.xyz_y; | ||
if (divider != 0) { | ||
varU /= divider; | ||
varV /= divider; | ||
} else { | ||
varU = Double.NaN; | ||
varV = Double.NaN; | ||
} | ||
this.luv_l = HsluvColorConverter.yToL(this.xyz_y); | ||
if (this.luv_l == 0) { | ||
this.luv_u = 0; | ||
this.luv_v = 0; | ||
} else { | ||
this.luv_u = 13 * this.luv_l * (varU - HsluvColorConverter.refU); | ||
this.luv_v = 13 * this.luv_l * (varV - HsluvColorConverter.refV); | ||
} | ||
} | ||
|
||
public void luvToXyz() { | ||
if (this.luv_l == 0) { | ||
this.xyz_x = 0; | ||
this.xyz_y = 0; | ||
this.xyz_z = 0; | ||
return; | ||
} | ||
double varU = this.luv_u / (13 * this.luv_l) + HsluvColorConverter.refU; | ||
double varV = this.luv_v / (13 * this.luv_l) + HsluvColorConverter.refV; | ||
this.xyz_y = HsluvColorConverter.lToY(this.luv_l); | ||
this.xyz_x = 0 - 9 * this.xyz_y * varU / ((varU - 4) * varV - varU * varV); | ||
this.xyz_z = (9 * this.xyz_y - 15 * varV * this.xyz_y - varV * this.xyz_x) / (3 * varV); | ||
} | ||
|
||
public void luvToLch() { | ||
this.lch_l = this.luv_l; | ||
this.lch_c = Math.sqrt(this.luv_u * this.luv_u + this.luv_v * this.luv_v); | ||
if (this.lch_c < 0.00000001) { | ||
this.lch_h = 0; | ||
} else { | ||
double hrad = Math.atan2(this.luv_v, this.luv_u); | ||
this.lch_h = hrad * 180.0 / Math.PI; | ||
if (this.lch_h < 0) { | ||
this.lch_h = 360 + this.lch_h; | ||
} | ||
} | ||
} | ||
|
||
public void lchToLuv() { | ||
double hrad = this.lch_h / 180.0 * Math.PI; | ||
this.luv_l = this.lch_l; | ||
this.luv_u = Math.cos(hrad) * this.lch_c; | ||
this.luv_v = Math.sin(hrad) * this.lch_c; | ||
} | ||
|
||
public void calculateBoundingLines(double l) { | ||
double sub1 = Math.pow(l + 16, 3) / 1560896; | ||
double sub2 = sub1 > HsluvColorConverter.epsilon ? sub1 : l / HsluvColorConverter.kappa; | ||
double s1r = sub2 * (284517 * HsluvColorConverter.m_r0 - 94839 * HsluvColorConverter.m_r2); | ||
double s2r = sub2 * (838422 * HsluvColorConverter.m_r2 + 769860 * HsluvColorConverter.m_r1 | ||
+ 731718 * HsluvColorConverter.m_r0); | ||
double s3r = sub2 * (632260 * HsluvColorConverter.m_r2 - 126452 * HsluvColorConverter.m_r1); | ||
double s1g = sub2 * (284517 * HsluvColorConverter.m_g0 - 94839 * HsluvColorConverter.m_g2); | ||
double s2g = sub2 * (838422 * HsluvColorConverter.m_g2 + 769860 * HsluvColorConverter.m_g1 | ||
+ 731718 * HsluvColorConverter.m_g0); | ||
double s3g = sub2 * (632260 * HsluvColorConverter.m_g2 - 126452 * HsluvColorConverter.m_g1); | ||
double s1b = sub2 * (284517 * HsluvColorConverter.m_b0 - 94839 * HsluvColorConverter.m_b2); | ||
double s2b = sub2 * (838422 * HsluvColorConverter.m_b2 + 769860 * HsluvColorConverter.m_b1 | ||
+ 731718 * HsluvColorConverter.m_b0); | ||
double s3b = sub2 * (632260 * HsluvColorConverter.m_b2 - 126452 * HsluvColorConverter.m_b1); | ||
this.r0s = s1r / s3r; | ||
this.r0i = s2r * l / s3r; | ||
this.r1s = s1r / (s3r + 126452); | ||
this.r1i = (s2r - 769860) * l / (s3r + 126452); | ||
this.g0s = s1g / s3g; | ||
this.g0i = s2g * l / s3g; | ||
this.g1s = s1g / (s3g + 126452); | ||
this.g1i = (s2g - 769860) * l / (s3g + 126452); | ||
this.b0s = s1b / s3b; | ||
this.b0i = s2b * l / s3b; | ||
this.b1s = s1b / (s3b + 126452); | ||
this.b1i = (s2b - 769860) * l / (s3b + 126452); | ||
} | ||
|
||
public double calcMaxChromaHpluv() { | ||
double r0 = HsluvColorConverter.distanceFromOrigin(this.r0s, this.r0i); | ||
double r1 = HsluvColorConverter.distanceFromOrigin(this.r1s, this.r1i); | ||
double g0 = HsluvColorConverter.distanceFromOrigin(this.g0s, this.g0i); | ||
double g1 = HsluvColorConverter.distanceFromOrigin(this.g1s, this.g1i); | ||
double b0 = HsluvColorConverter.distanceFromOrigin(this.b0s, this.b0i); | ||
double b1 = HsluvColorConverter.distanceFromOrigin(this.b1s, this.b1i); | ||
return HsluvColorConverter.min6(r0, r1, g0, g1, b0, b1); | ||
} | ||
|
||
public double calcMaxChromaHsluv(double h) { | ||
double hueRad = h / 360 * Math.PI * 2; | ||
double r0 = HsluvColorConverter.distanceFromOriginAngle(this.r0s, this.r0i, hueRad); | ||
double r1 = HsluvColorConverter.distanceFromOriginAngle(this.r1s, this.r1i, hueRad); | ||
double g0 = HsluvColorConverter.distanceFromOriginAngle(this.g0s, this.g0i, hueRad); | ||
double g1 = HsluvColorConverter.distanceFromOriginAngle(this.g1s, this.g1i, hueRad); | ||
double b0 = HsluvColorConverter.distanceFromOriginAngle(this.b0s, this.b0i, hueRad); | ||
double b1 = HsluvColorConverter.distanceFromOriginAngle(this.b1s, this.b1i, hueRad); | ||
return HsluvColorConverter.min6(r0, r1, g0, g1, b0, b1); | ||
} | ||
|
||
public void hsluvToLch() { | ||
if (this.hsluv_l > 99.9999999) { | ||
this.lch_l = 100; | ||
this.lch_c = 0; | ||
} else if (this.hsluv_l < 0.00000001) { | ||
this.lch_l = 0; | ||
this.lch_c = 0; | ||
} else { | ||
this.lch_l = this.hsluv_l; | ||
this.calculateBoundingLines(this.hsluv_l); | ||
double max = this.calcMaxChromaHsluv(this.hsluv_h); | ||
this.lch_c = max / 100 * this.hsluv_s; | ||
} | ||
this.lch_h = this.hsluv_h; | ||
} | ||
|
||
public void lchToHsluv() { | ||
if (this.lch_l > 99.9999999) { | ||
this.hsluv_s = 0; | ||
this.hsluv_l = 100; | ||
} else if (this.lch_l < 0.00000001) { | ||
this.hsluv_s = 0; | ||
this.hsluv_l = 0; | ||
} else { | ||
this.calculateBoundingLines(this.lch_l); | ||
double max = this.calcMaxChromaHsluv(this.lch_h); | ||
this.hsluv_s = this.lch_c / max * 100; | ||
this.hsluv_l = this.lch_l; | ||
} | ||
this.hsluv_h = this.lch_h; | ||
} | ||
|
||
public void hpluvToLch() { | ||
if (this.hpluv_l > 99.9999999) { | ||
this.lch_l = 100; | ||
this.lch_c = 0; | ||
} else if (this.hpluv_l < 0.00000001) { | ||
this.lch_l = 0; | ||
this.lch_c = 0; | ||
} else { | ||
this.lch_l = this.hpluv_l; | ||
this.calculateBoundingLines(this.hpluv_l); | ||
double max = this.calcMaxChromaHpluv(); | ||
this.lch_c = max / 100 * this.hpluv_p; | ||
} | ||
this.lch_h = this.hpluv_h; | ||
} | ||
|
||
public void lchToHpluv() { | ||
if (this.lch_l > 99.9999999) { | ||
this.hpluv_p = 0; | ||
this.hpluv_l = 100; | ||
} else if (this.lch_l < 0.00000001) { | ||
this.hpluv_p = 0; | ||
this.hpluv_l = 0; | ||
} else { | ||
this.calculateBoundingLines(this.lch_l); | ||
double max = this.calcMaxChromaHpluv(); | ||
this.hpluv_p = this.lch_c / max * 100; | ||
this.hpluv_l = this.lch_l; | ||
} | ||
this.hpluv_h = this.lch_h; | ||
} | ||
|
||
public void hsluvToRgb() { | ||
this.hsluvToLch(); | ||
this.lchToLuv(); | ||
this.luvToXyz(); | ||
this.xyzToRgb(); | ||
} | ||
|
||
public void hpluvToRgb() { | ||
this.hpluvToLch(); | ||
this.lchToLuv(); | ||
this.luvToXyz(); | ||
this.xyzToRgb(); | ||
} | ||
|
||
public void hsluvToHex() { | ||
this.hsluvToRgb(); | ||
this.rgbToHex(); | ||
} | ||
|
||
public void hpluvToHex() { | ||
this.hpluvToRgb(); | ||
this.rgbToHex(); | ||
} | ||
|
||
public void rgbToHsluv() { | ||
this.rgbToXyz(); | ||
this.xyzToLuv(); | ||
this.luvToLch(); | ||
this.lchToHpluv(); | ||
this.lchToHsluv(); | ||
} | ||
|
||
public void rgbToHpluv() { | ||
this.rgbToXyz(); | ||
this.xyzToLuv(); | ||
this.luvToLch(); | ||
this.lchToHpluv(); | ||
this.lchToHpluv(); | ||
} | ||
|
||
public void hexToHsluv() { | ||
this.hexToRgb(); | ||
this.rgbToHsluv(); | ||
} | ||
|
||
public void hexToHpluv() { | ||
this.hexToRgb(); | ||
this.rgbToHpluv(); | ||
} | ||
} |
Oops, something went wrong.