Reputation: 930
I'm looking for a description of algorithms for converting RGBA, HSL and HSLA colors to RGB color, or a library for converting them in an java.awt.Color object.
Can you help me?
Upvotes: 1
Views: 4027
Reputation: 930
Your regex rules don't work fine, because they allow uncorrect string (such as "rgba(1000,500%,500%,2)" ) and deny correct form (such as "#fff" ).
I wrote more strict and correct regex rules:
String keywords_color_regex = "^[a-z]*$";
String hex_color_regex = "^#[0-9a-f]{3}([0-9a-f]{3})?$";
String rgb_color_regex = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$";
String rgba_color_regex = "^rgba\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*((0.[1-9])|[01])\\s*\\)$";
String hsl_color_regex = "^hsl\\(\\s*(0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*,\\s*((0|[1-9]\\d?|100)%)\\s*\\)$";
For rgba and hsla, my goal is to calculate the actual color displayed. So I was wondering if, given an rgba/hsla color and its background color, there's a way to "mix" them to calculate the displayed color...
Upvotes: 1
Reputation: 17329
You can use Color.HSBtoRGB
and Color.RGBtoHSB
. For example:
int r = 0, g = 255, b = 255;
float[] hsb = Color.RGBtoHSB(r, g, b, null);
for (float f : hsb) {
System.out.println(f);
}
This outputs:
0.5
1.0
1.0
These three float
values are the H, S, and B values respectively. For colors with alpha, the alpha doesn't change from RGB to HSB, so A == A
.
To create a Color
with the returned array:
Color color = Color.getHSBColor(hsb[0], hsb[1], hsb[2]);
I can't help you much more without more details about what exactly you want as input and output.
Edit: See my other answer.
Upvotes: 2
Reputation: 17329
With the new info from the comments, I'll change this to reflect what you have versus what you need.
First, we need to parse the rgb/hsl string. This can be done pretty easily using a couple regular expressions and String.split
:
private static final Pattern hexRegex = Pattern.compile("#[\\dA-Fa-f]{6}");
private static final Pattern rgbRegex = Pattern.compile("rgba?\\([^)]*\\)", Pattern.CASE_INSENSITIVE);
private static final Pattern hlsRegex = Pattern.compile("hlsa?\\([^)]*\\)", Pattern.CASE_INSENSITIVE);
The first Pattern
matches any hex-encoded value. The second one matches rgb(something)
or rgba(something)
. The third one is the same as the second one, but with hsl
and hsla
. To use these:
public static int[] getRGB(String cssString) {
if (hexRegex.matcher(cssString).matches())
return getRgbFromHex(cssString);
if (rgbRegex.matcher(cssString).matches())
return getRgbFromRgb(cssString);
if (hslRegex.matcher(cssString).matches())
return getRgbFromHsl(cssString);
return null; // no match
}
private static int[] getRgbFromHex(String hexString) {
int rgb = Integer.decode(hexString);
Color c = new Color(rgb);
return new int[] { c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha() };
}
private static int[] getRgbFromRgb(String rgbString) {
String[] values = rgbString.split("[\\s,()]");
// values[0] is just "rgb" or "rgba"
String red = values[1];
String green = values[2];
String blue = values[3];
String alpha = "1.0";
if (values.length >= 5) {
alpha = values[4];
}
return new int[] {
parseValue(red, 255),
parseValue(green, 255),
parseValue(blue, 255),
parseAlpha(alpha),
};
}
private static int[] getRgbFromHsl(String hslString) {
String[] values = hslString.split("[\\s,()]");
// values[0] is just "hsl" or "hsla"
String hue = values[1];
String sat = values[2];
String light = values[3];
String alpha = "1.0";
if (values.length >= 5) {
alpha = values[4];
}
int h = parseValue(hue, 360);
double s = parsePercent(sat);
double l = parsePercent(light);
int a = parseAlpha(alpha);
return hslToRgb(h, s, l, a);
}
private static int[] hslToRgb(int h, double s, double l, int a) {
// TODO Calculate me
int r = 0;
int g = 0;
int b = 0;
return new int[] { r, g, b, a };
}
private static int parseValue(String val, int max) {
if (val.endsWith("%")) {
return (int) (parsePercent(val) * max);
}
return Integer.parseInt(val);
}
private static double parsePercent(String perc) {
return Integer.parseInt(perc.substring(0, perc.length() - 1)) / 100.0;
}
private static int parseAlpha(String alpha) {
return (int) (Double.parseDouble(alpha) * 255);
}
Here's what everything does:
getRGB
- Takes the CSS string, determines its format, then proceeds to parsing it. Returns null
if it can't determine the format (i.e., it's invalid). The array returned will be 4 elements, r, g, b, and a, and all values will be between 0 and 255.getRgbFromHex
- Uses Integer.decode
to parse it since this does #
hex numbers for us, then uses Color
to get the RGB values.getRgbFromRgb
- Parses for the values from an rgb
string (includes rgba
). Splits the string on whitespace, commas, or parentheses, then parses each individual value and creates an array from them.getRgbFromHsl
- Behaves similarly to getRgbFromRgb
, but with HSL values and the appropriate parsing instead of the RGB parsing.hslToRgb
- This is your calculation logic that you said you've already done in your comments. Just calculate the int
values of r, g, and b here from h, s, and l, and this method will work.parseValue
- If it's a percentage, it returns the parsed percentage times the value of max
, otherwise, it simply parses it as an int
using Integer.parseInt
.parsePercent
- Parses the integer part of the string and returns the value as a double
divided by 100.parseAlpha
- Parses the alpha as a double
and returns it times 255.Testing with rgb/rgba confirms that this works:
public static void main(String[] args) {
System.out.println(Arrays.toString(getRGB("#FF00CC")));
System.out.println(Arrays.toString(getRGB("rgb(255,0,0)")));
System.out.println(Arrays.toString(getRGB("rgba(255,0,0,0.5)")));
System.out.println(Arrays.toString(getRGB("rgba(100%,0%,30%,0.5)")));
}
This prints:
[255, 0, 204, 255]
[255, 0, 0, 255]
[255, 0, 0, 127]
[255, 0, 76, 127]
One other thing you may want to consider is using rounding instead of casting directly to int
for the percentage stuff. This would improve the accuracy of the percentages.
Let me know if you have any further questions.
Upvotes: 3