Converting from HSV (HSB in Java) to RGB without using java.awt.Color (disallowed on Google App Engine)

36,870

Solution 1

I don't know anything about color math, but I can offer this alternative structure for the code, which tickles my aesthetic sense because it made it obvious to me how each of the 6 cases is just a different permutation of value, t and p. (Also I have an irrational fear of long if-else chains.)

public static String hsvToRgb(float hue, float saturation, float value) {

    int h = (int)(hue * 6);
    float f = hue * 6 - h;
    float p = value * (1 - saturation);
    float q = value * (1 - f * saturation);
    float t = value * (1 - (1 - f) * saturation);

    switch (h) {
      case 0: return rgbToString(value, t, p);
      case 1: return rgbToString(q, value, p);
      case 2: return rgbToString(p, value, t);
      case 3: return rgbToString(p, q, value);
      case 4: return rgbToString(t, p, value);
      case 5: return rgbToString(value, p, q);
      default: throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation + ", " + value);
    }
}

public static String rgbToString(float r, float g, float b) {
    String rs = Integer.toHexString((int)(r * 256));
    String gs = Integer.toHexString((int)(g * 256));
    String bs = Integer.toHexString((int)(b * 256));
    return rs + gs + bs;
}

Solution 2

You should use the HSBtoRGB implementation provided by Oracle, copying its source code into your project. java.awt.Color is open-source. The algorithms provided by Peter Recore and Yngling are not robust and will return illegal RGB values like "256,256,0" for certain inputs. Oracle's implementation is robust, use it instead.

Solution 3

Use ColorUtils which provides

HSLToColor(float\[\] hsl) 

And

[RGBToHSL(int r, int g, int b, float\[\] hsl)]

Methods which are very easy to convert to each other!

For example:

float[] hsl = new float[]{1.5, 2.0, 1.5};
int color = ColorUtils.HSLToColor(hsl);

Now get the color

float[] hslStub = new float[3];
float[] hslFromColor = ColorUtils.colorToHSL(color, hslStub);

Now get the hsl

Here is the sourcecode.

Solution 4

The solution was found here: http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/

Martin Ankerl provides a good post on the subject, and provides Ruby script. For those too busy (or lazy) to implement it in Java, here's the one I did (I am sure it can be written more effectively, please feel free to comment):

public static String hsvToRgb(float hue, float saturation, float value) {
    float r, g, b;

    int h = (int)(hue * 6);
    float f = hue * 6 - h;
    float p = value * (1 - saturation);
    float q = value * (1 - f * saturation);
    float t = value * (1 - (1 - f) * saturation);

    if (h == 0) {
        r = value;
        g = t;
        b = p;
    } else if (h == 1) {
        r = q;
        g = value;
        b = p;
    } else if (h == 2) {
        r = p;
        g = value;
        b = t;
    } else if (h == 3) {
        r = p;
        g = q;
        b = value;
    } else if (h == 4) {
        r = t;
        g = p;
        b = value;
    } else if (h <= 6) {
        r = value;
        g = p;
        b = q;
    } else {
        throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation + ", " + value);
    }

    String rs = Integer.toHexString((int)(r * 255));
    String gs = Integer.toHexString((int)(g * 255));
    String bs = Integer.toHexString((int)(b * 255));
    return rs + gs + bs;
}

Solution 5

Using SWT you can use following code snippet:

RGB rgb = new RGB(r, g, b);
float[] hsbColor = rgb.getHSB();
rgb = new RGB(hsbColor[0], hsbColor[1], hsbColor[2]);
Share:
36,870
yngling
Author by

yngling

Programmer, Pankratiast, Pâtissier, PhD student, Political animal &amp; Prospective Entrepreneur.

Updated on May 19, 2020

Comments

  • yngling
    yngling almost 4 years

    I figured I should post this question, even if I have already found a solution, as a Java implementation was not readily available when I searched for it.

    Using HSV instead of RGB allows the generation of colors with the same saturation and brightness (something I wanted).

    Google App Engine does not allow use of java.awt.Color, so doing the following to convert between HSV and RGB is not an option:

    Color c = Color.getHSBColor(hue, saturation, value);
    String rgb = Integer.toHexString(c.getRGB());
    

    Edit: I moved my answer as described in the comment by Nick Johnson.

    Ex animo, - Alexander.

  • yngling
    yngling over 12 years
    That is indeed a lot prettier, at the mere cost of another method invocation.
  • Peter Recore
    Peter Recore over 12 years
    I'm optimistic that either the compiler or the JIT would inline the extra method if it were a major performance gain.
  • devrobf
    devrobf over 10 years
    Sorry to drag up a 2 year old thread, but I wondered if someone would confirm something for me - should rgbToString multiply by 255, not 256? Because otherwise, when r is 1.0, rs would be 100 in hex, which is wrong.
  • piepera
    piepera over 10 years
    Yes, although if you simply multiply by 255, then results like 0.9999 will get truncated to 254, which is not correct either. A correct algorithm would implement the rounding present in java.awt.Color.HSBtoRGB()
  • ehartwell
    ehartwell over 9 years
    The last else condition should probably say | else if (h <= 6) {. And the conversion should be (r * 255) etc not * 256 since the color range is 0 to 255.
  • ehartwell
    ehartwell over 9 years
    If your hue value is exactly 1, then h could easily be 6, throwing an unnecessary exception.
  • Peter O.
    Peter O. almost 9 years
    @ehartwell: Edited based on your comments.
  • Aaron Spalding
    Aaron Spalding about 8 years
    Take a look at github.com/sualeh/SchemaCrawler/blob/… for a complete realization of the code above, which takes into account round results, and having the hue be any float.
  • Andy Thomas
    Andy Thomas over 2 years
    Confirmed. Although the (int,int,int) constructor accepts RGB, the (float,float,float) constructor accepts HSB.
  • Jitendra Mistry
    Jitendra Mistry almost 2 years
    hello are you still alive??