1

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?

3 Answers3

3

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.

Brian
  • 17,079
  • 6
  • 43
  • 66
2

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.

Brian
  • 17,079
  • 6
  • 43
  • 66
  • 1
    My goal is to convert all the CSS Legal Color Values ( http://www.w3schools.com/cssref/css_colors_legal.asp ) to RGB. Converting Hexadecimal Colors and Color Names is easy, i'm looking for a RGBA, HSL and HSLA conversion. Your method doesn't work, HSL and HSB are different representations' systems. – Antonio Giovanni Schiavone Sep 11 '12 at 22:38
  • @AntonioGiovanniSchiavone So what do you have for input then? Something like `rgb(0,255,255)` or `#00FFFF`? If you need to parse down to that value, here's [a good CSS parser](https://github.com/corgrath/osbcp-css-parser) for Java. Can I assume this is what you have for input? The actual property value from CSS as a string? – Brian Sep 11 '12 at 22:48
  • @AntonioGiovanniSchiavone Also, sorry about the HSB/HSL thing, I totally misread your post :X I'll fix it once I know what you're looking for. – Brian Sep 11 '12 at 22:50
  • As described in the link above, for the HSL I have an input as "hsl(220,100%,63%)", my output is an array [r,g,b]. For HSL I resolved implementing this algorithm http://www.geekymonkey.com/Programming/CSharp/RGB2HSL_HSL2RGB.htm. Still needing help for RGBA and HSLA. – Antonio Giovanni Schiavone Sep 11 '12 at 23:04
  • @AntonioGiovanniSchiavone the extra `A` is just the alpha (transparency) value, which AFAIK doesn't change from RGBA to HSLA. In other words, no conversion should be needed to translate A to A. You may want to consider making your output array have 4 parameters, `[r, g, b, a]`, and make `a` default to 100% alpha (255, 1.0, or 100%) if it's not specified (this is the default otherwise). I'll update my answer in a little bit, I can't ATM. – Brian Sep 11 '12 at 23:12
  • Do you mean that rgb(r,g,b) and rgba(r,g,b,a) (with a included in [0-1]) are ALWAYS the same color? And also do you mean that hsl(h,s%,l%) and hsla(h,s%,l%,a) (with a included in [0-1]) are ALWAYS the same color? (I'm not an expert in colors' rapresentations) – Antonio Giovanni Schiavone Sep 12 '12 at 08:29
  • @AntonioGiovanniSchiavone In a sense, they are the same color, but with different _transparency_. This means that they may _appear_ differently since they may blend with the background, but the base color is the same. In other words, `rgb(255,0,0)` (red, completely opaque) is exactly the same as `rgb(255,0,0,1.0)` (red, completely opaque). `rgb(255,0,0,0.5)` is the same base color (red), but is half transparent (a.k.a translucent). On a white background, it will appear lighter than its opaque counterpart, but it will appear differently on say a green background. I'll update my answer now. – Brian Sep 12 '12 at 14:54
1

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...