Skip to content

Commit d3f1639

Browse files
committed
A HSL Color Representation
HSL color representations can be helpful for different color calculations (e.g., correct lighter or darker colors, contrast colors).
1 parent b8bed38 commit d3f1639

File tree

1 file changed

+213
-0
lines changed

1 file changed

+213
-0
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Johannes Kepler University Linz
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Alois Zoitl - initial API and implementation
12+
*******************************************************************************/
13+
14+
package org.eclipse.draw2d.colors;
15+
16+
import org.eclipse.swt.SWT;
17+
import org.eclipse.swt.graphics.Color;
18+
import org.eclipse.swt.graphics.RGB;
19+
20+
/**
21+
* Represents a color in the HSL (Hue, Saturation, Lightness) color space.
22+
*
23+
* @param h The color hue in degrees, in the range {@code [0.0, 360.0)}. 0 is
24+
* red, 120 is green, and 240 is blue.
25+
* @param s The intensity of the color, in the range {@code [0.0, 1.0]}. 0.0 is
26+
* grayscale and 1.0 is fully saturated.
27+
* @param l The brightness of the color, in the range {@code [0.0, 1.0]}. 0.0 is
28+
* black, 0.5 is the pure color, and 1.0 is white.
29+
*
30+
* @since 3.22
31+
*/
32+
public record HSLColor(double h, double s, double l) {
33+
34+
private static final double MAX_RGB_VALUE = 255.0;
35+
36+
public HSLColor {
37+
if (h < 0 || h > 360) {
38+
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
39+
}
40+
if (s < 0 || s > 1.0) {
41+
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
42+
}
43+
if (l < 0 || l > 1.0) {
44+
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
45+
}
46+
}
47+
48+
/**
49+
* Create a darker version of the color.
50+
*
51+
* @param percentage The percentage how much darker the color should be
52+
* (0.0..1.0).
53+
* @return The lighter color
54+
*/
55+
public HSLColor darker(double percentage) {
56+
double newL = l * (1 - Math.max(0.0, Math.min(1.0, percentage)));
57+
return new HSLColor(h, s, newL);
58+
}
59+
60+
/**
61+
* Test if the given color is dark.
62+
*
63+
* This can be used to identify dark mode or find the right contrast color
64+
* (e.g., white text on dark background or opposite).
65+
*
66+
* @return True of the lightness value is below 0.5
67+
*/
68+
public boolean isDark() {
69+
return l < 0.5;
70+
}
71+
72+
/**
73+
* Create a lighter version of the color.
74+
*
75+
* @param percentage The percentage how much lighter the color should be
76+
* (0.0..1.0).
77+
* @return The lighter color
78+
*/
79+
public HSLColor ligher(double percentage) {
80+
double newL = l * (1 - l) * Math.max(0.0, Math.min(1.0, percentage));
81+
return new HSLColor(h, s, newL);
82+
}
83+
84+
/**
85+
* Transforms the RGB color into the according HSL color space.
86+
*
87+
* @param col The RGB color to be transformed.
88+
* @return The color in HSL space.
89+
*/
90+
public static HSLColor fromColor(Color col) {
91+
return fromRGB(col.getRed(), col.getGreen(), col.getBlue());
92+
}
93+
94+
/**
95+
* Transforms the RGB color into the according HSL color space.
96+
*
97+
* @param rgb The RGB color to be transformed.
98+
* @return The color in HSL space.
99+
*/
100+
public static HSLColor fromRGB(RGB rgb) {
101+
return fromRGB(rgb.red, rgb.green, rgb.blue);
102+
}
103+
104+
/**
105+
* Transforms the RGB color into the according HSL color space.
106+
*
107+
* The used algorithm is based on the Book: Computer Graphics: Principles and
108+
* Practice in C. James D. Foley, Andries van Dam, Steven K. Feiner, John F.
109+
* Hughes, 2nd Edition, Published Aug 4, 1995 by Addison-Wesley Professional.
110+
* ISBN-13: 978-0-201-84840-3
111+
*
112+
* @param r The red component of the source color (0..255).
113+
* @param g The green component of the source color (0..255).
114+
* @param b The blue component of the source color (0..255).
115+
* @return The color in HSL color space.
116+
*/
117+
public static HSLColor fromRGB(int r, int g, int b) {
118+
final double rRel = r / MAX_RGB_VALUE;
119+
final double gRel = g / MAX_RGB_VALUE;
120+
final double bRel = b / MAX_RGB_VALUE;
121+
final double max = Math.max(Math.max(rRel, gRel), bRel);
122+
final double min = Math.min(Math.min(rRel, gRel), bRel);
123+
124+
double h = 0.0;
125+
double s = 0.0;
126+
double l = (max + min) / 2.0;
127+
128+
if (max != min) {
129+
// we are not just grey
130+
final double delta = max - min;
131+
132+
if (l <= 0.5) {
133+
s = (delta / (max + min));
134+
} else {
135+
s = (delta / (2.0 - (max + min)));
136+
}
137+
138+
if (Math.abs(max - rRel) <= Double.MIN_VALUE) { // how to check equality for doubles
139+
h = (gRel - bRel) / delta;
140+
} else if (Math.abs(max - gRel) <= Double.MIN_VALUE) {
141+
h = 2.0 + ((bRel - rRel) / delta);
142+
} else if (Math.abs(max - bRel) <= Double.MIN_VALUE) {
143+
h = 4.0 + ((rRel - gRel) / delta);
144+
}
145+
h *= 60.0;
146+
147+
if (h < 0.0) {
148+
h += 360.0;
149+
}
150+
}
151+
return new HSLColor(h, s, l);
152+
}
153+
154+
/**
155+
* Transforms the HSL color into the according RGB color space.
156+
*
157+
* @return The color in RGB color space.
158+
*/
159+
public Color toColor() {
160+
return new Color(toRGB());
161+
}
162+
163+
/**
164+
* Transforms the HSL color into the according RGB color space.
165+
*
166+
* The used algorithm is based on the Book: Computer Graphics: Principles and
167+
* Practice in C. James D. Foley, Andries van Dam, Steven K. Feiner, John F.
168+
* Hughes, 2nd Edition, Published Aug 4, 1995 by Addison-Wesley Professional.
169+
* ISBN-13: 978-0-201-84840-3
170+
*
171+
* @return The color in RGB color space.
172+
*/
173+
public RGB toRGB() {
174+
final RGB retVal = new RGB(0, 0, 0);
175+
176+
if (s == 0.0) {
177+
if (h == 0.0) {
178+
retVal.red = retVal.green = retVal.blue = (int) (l * MAX_RGB_VALUE);
179+
} else {
180+
// in the achromatic (grey) case h must not have a value
181+
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
182+
}
183+
} else {
184+
final double m2 = ((l <= 0.5) ? (l * (1.0 + s)) : ((l + s) - (l * s)));
185+
final double m1 = (2.0 * l) - m2;
186+
187+
retVal.red = hslValue(m1, m2, h + 120.0);
188+
retVal.green = hslValue(m1, m2, h);
189+
retVal.blue = hslValue(m1, m2, h - 120.0);
190+
}
191+
return retVal;
192+
}
193+
194+
private static int hslValue(final double m1, final double m2, double hue) {
195+
double retVal = m1;
196+
197+
if (hue > 360.0) {
198+
hue -= 360.0;
199+
} else if (hue < 0.0) {
200+
hue += 360.0;
201+
}
202+
203+
if (hue < 60.0) {
204+
retVal = m1 + (((m2 - m1) * hue) / 60.0);
205+
} else if (hue < 180.0) {
206+
retVal = m2;
207+
} else if (hue < 240.0) {
208+
retVal = m1 + (((m2 - m1) * (240.0 - hue)) / 60.0);
209+
}
210+
211+
return (int) (MAX_RGB_VALUE * retVal);
212+
}
213+
}

0 commit comments

Comments
 (0)