'Converting from HSV (HSB in Java) to RGB without using java.awt.Color (disallowed on Google App Engine)
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.
Solution 1:[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:[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:[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:[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:[5]
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))));
Solution 6:[6]
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.
Solution 7:[7]
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]);
Solution 8:[8]
I know that this is an old question, but all the answers I've seen here multiply the hue with 6. This is wrong. I took a look at the Wikipedia article and there it says that you have to divide by 60.
Here is one which I tested written in Kotlin
fun hsvToRgb(hsv: FloatArray): IntArray {
val (hue, saturation, value) = hsv
val h: Int = (hue / 60).toInt()
val f = hue / 60 - h
val p = value * (1 - saturation)
val q = value * (1 - f * saturation)
val t = value * (1 - (1 - f) * saturation)
val rgb = when (h) {
0 -> floatArrayOf(value, t, p)
1 -> floatArrayOf(q, value, p)
2 -> floatArrayOf(p, value, t)
3 -> floatArrayOf(p, q, value)
4 -> floatArrayOf(t, p, value)
5, 6 -> floatArrayOf(value, p, q)
else -> throw Exception()
}.map { it * 255 }
val (r, g, b) = rgb
return intArrayOf(r.toInt(), g.toInt(), b.toInt())
}
Here my Java implementation
public static int[] hsvToRgb(float[] hsv) {
final float hue = hsv[0];
final float saturation = hsv[1];
final float value = hsv[2];
final int h = (int) hue / 60;
final float f = hue / 60 - h;
final float p = value * (1 - saturation);
final float q = value * (1 - f * saturation);
final float t = value * (1 - (1 - f) * saturation);
float[] rgb = switch (h) {
case 0 -> new float[]{value, t, p};
case 1 -> new float[]{q, value, p};
case 2 -> new float[]{p, value, t};
case 3 -> new float[]{p, q, value};
case 4 -> new float[]{t, p, value};
case 5, 6 -> new float[]{value, p, q};
default -> throw new IllegalStateException();
};
rgb[0] = rgb[0] * 255;
rgb[1] = rgb[1] * 255;
rgb[2] = rgb[2] * 255;
return new int[]{(int) rgb[0], (int) rgb[1], (int) rgb[2]};
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Peter Recore |
Solution 2 | piepera |
Solution 3 | Peter O. |
Solution 4 | Peter O. |
Solution 5 | Mateusz Kaflowski |
Solution 6 | Mladen Adamovic |
Solution 7 | JoBl |
Solution 8 | Marian |