I need to accurately convert HSB to RGB but I am not sure how to get around the problem of turning decimals into whole numbers without rounding. This is the current function I have out of a colorpicker library:
HSBToRGB = function (hsb) {
var rgb = { };
var h = Math.round(hsb.h);
var s = Math.round(hsb.s * 255 / 100);
var v = Math.round(hsb.b * 255 / 100);
if (s == 0) {
rgb.r = rgb.g = rgb.b = v;
} else {
var t1 = v;
var t2 = (255 - s) * v / 255;
var t3 = (t1 - t2) * (h % 60) / 60;
if (h == 360) h = 0;
if (h < 60) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3 }
else if (h < 120) { rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3 }
else if (h < 180) { rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3 }
else if (h < 240) { rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3 }
else if (h < 300) { rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3 }
else if (h < 360) { rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3 }
else { rgb.r = 0; rgb.g = 0; rgb.b = 0 }
}
return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) };
As you can see the inaccuracy in this function comes from the Math.round
From Parthik Gosar's link in this comment with slight modification to let you enter each value independently or all at once as an object
/* accepts parameters
* h Object = {h:x, s:y, v:z}
* OR
* h, s, v
*/
function HSVtoRGB(h, s, v) {
var r, g, b, i, f, p, q, t;
if (arguments.length === 1) {
s = h.s, v = h.v, h = h.h;
}
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
This code expects 0 <= h, s, v <= 1
, if you're using degrees or radians, remember to divide them out.
The returned 0 <= r, g, b <= 255
are rounded to the nearest Integer. If you don't want this behaviour remove the Math.round
s from the returned object.
And the reverse (with less division)
/* accepts parameters
* r Object = {r:x, g:y, b:z}
* OR
* r, g, b
*/
function RGBtoHSV(r, g, b) {
if (arguments.length === 1) {
g = r.g, b = r.b, r = r.r;
}
var max = Math.max(r, g, b), min = Math.min(r, g, b),
d = max - min,
h,
s = (max === 0 ? 0 : d / max),
v = max / 255;
switch (max) {
case min: h = 0; break;
case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break;
case g: h = (b - r) + d * 2; h /= 6 * d; break;
case b: h = (r - g) + d * 4; h /= 6 * d; break;
}
return {
h: h,
s: s,
v: v
};
}
This code will output 0 <= h, s, v <= 1
, but this time takes any 0 <= r, g, b <= 255
(does not need to be an integer)
For completeness,
function HSVtoHSL(h, s, v) {
if (arguments.length === 1) {
s = h.s, v = h.v, h = h.h;
}
var _h = h,
_s = s * v,
_l = (2 - s) * v;
_s /= (_l <= 1) ? _l : 2 - _l;
_l /= 2;
return {
h: _h,
s: _s,
l: _l
};
}
function HSLtoHSV(h, s, l) {
if (arguments.length === 1) {
s = h.s, l = h.l, h = h.h;
}
var _h = h,
_s,
_v;
l *= 2;
s *= (l <= 1) ? l : 2 - l;
_v = (l + s) / 2;
_s = (2 * s) / (l + s);
return {
h: _h,
s: _s,
v: _v
};
}
All of these values should be in the range 0
to 1
. For HSL<->RGB
go via HSV.