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.
RGB = hsv2rgb( HSV ) converts the hue, saturation, and value (HSV) values of an HSV image to red, green, and blue values of an RGB image. rgbmap = hsv2rgb( hsvmap ) converts an HSV colormap to an RGB colormap.
Converting RGB to HSV H = 360 - cos-1[ (R - ½G - ½B)/√R² + G² + B² - RG - RB - GB ] if B > G. Inverse cosine is calculated in degrees.
HSBtoRGB. Converts the components of a color, as specified by the HSB model, to an equivalent set of values for the default RGB model. The saturation and brightness components should be floating-point values between zero and one (numbers in the range 0.0-1.0). The hue component can be any floating-point number.
Returns the green component in the range 0-255 in the default sRGB space. Creates a Color object based on the specified values for the HSB color model. Returns the red component in the range 0-255 in the default sRGB space. Returns the RGB value representing the color in the default sRGB ColorModel.
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.
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]);
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;
}
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;
}
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.
My code for converting:
/**
* @param H
* 0-360
* @param S
* 0-100
* @param V
* 0-100
* @return color in hex string
*/
public static String hsvToRgb(float H, float S, float V) {
float R, G, B;
H /= 360f;
S /= 100f;
V /= 100f;
if (S == 0)
{
R = V * 255;
G = V * 255;
B = V * 255;
} else {
float var_h = H * 6;
if (var_h == 6)
var_h = 0; // H must be < 1
int var_i = (int) Math.floor((double) var_h); // Or ... var_i =
// floor( var_h )
float var_1 = V * (1 - S);
float var_2 = V * (1 - S * (var_h - var_i));
float var_3 = V * (1 - S * (1 - (var_h - var_i)));
float var_r;
float var_g;
float var_b;
if (var_i == 0) {
var_r = V;
var_g = var_3;
var_b = var_1;
} else if (var_i == 1) {
var_r = var_2;
var_g = V;
var_b = var_1;
} else if (var_i == 2) {
var_r = var_1;
var_g = V;
var_b = var_3;
} else if (var_i == 3) {
var_r = var_1;
var_g = var_2;
var_b = V;
} else if (var_i == 4) {
var_r = var_3;
var_g = var_1;
var_b = V;
} else {
var_r = V;
var_g = var_1;
var_b = var_2;
}
R = var_r * 255; // RGB results from 0 to 255
G = var_g * 255;
B = var_b * 255;
}
String rs = Integer.toHexString((int) (R));
String gs = Integer.toHexString((int) (G));
String bs = Integer.toHexString((int) (B));
if (rs.length() == 1)
rs = "0" + rs;
if (gs.length() == 1)
gs = "0" + gs;
if (bs.length() == 1)
bs = "0" + bs;
return "#" + rs + gs + bs;
}
Example of use on Android:
tv.setBackgroundColor(Color.parseColor((ColorOperations.hsvToRgb(100, 100, 57))));
The answer by @Peter Recore do not use rounding.
Probably somewhat more correct way to use it is to copy the content from java.awt.Color and this is how it looked in Java 6:
public static int HSBtoRGB(float hue, float saturation, float brightness) {
int r = 0, g = 0, b = 0;
if (saturation == 0) {
r = g = b = (int) (brightness * 255.0f + 0.5f);
} else {
float h = (hue - (float)Math.floor(hue)) * 6.0f;
float f = h - (float)java.lang.Math.floor(h);
float p = brightness * (1.0f - saturation);
float q = brightness * (1.0f - saturation * f);
float t = brightness * (1.0f - (saturation * (1.0f - f)));
switch ((int) h) {
case 0:
r = (int) (brightness * 255.0f + 0.5f);
g = (int) (t * 255.0f + 0.5f);
b = (int) (p * 255.0f + 0.5f);
break;
case 1:
r = (int) (q * 255.0f + 0.5f);
g = (int) (brightness * 255.0f + 0.5f);
b = (int) (p * 255.0f + 0.5f);
break;
case 2:
r = (int) (p * 255.0f + 0.5f);
g = (int) (brightness * 255.0f + 0.5f);
b = (int) (t * 255.0f + 0.5f);
break;
case 3:
r = (int) (p * 255.0f + 0.5f);
g = (int) (q * 255.0f + 0.5f);
b = (int) (brightness * 255.0f + 0.5f);
break;
case 4:
r = (int) (t * 255.0f + 0.5f);
g = (int) (p * 255.0f + 0.5f);
b = (int) (brightness * 255.0f + 0.5f);
break;
case 5:
r = (int) (brightness * 255.0f + 0.5f);
g = (int) (p * 255.0f + 0.5f);
b = (int) (q * 255.0f + 0.5f);
break;
}
}
return 0xff000000 | (r << 16) | (g << 8) | (b << 0);
}
Rounding here seems to be correct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With