Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both

C++CAlgorithm

C++ Problem Overview


I am looking for color space converter from RGB to HSV, specifically for the range 0 to 255 for both color spaces.

C++ Solutions


Solution 1 - C++

I've used these for a long time - no idea where they came from at this point... Note that the inputs and outputs, except for the angle in degrees, are in the range of 0 to 1.0.

NOTE: this code does no real sanity checking on inputs. Proceed with caution!

typedef struct {
	double r;		// a fraction between 0 and 1
	double g;		// a fraction between 0 and 1
	double b;		// a fraction between 0 and 1
} rgb;

typedef struct {
	double h;		// angle in degrees
	double s;		// a fraction between 0 and 1
	double v;		// a fraction between 0 and 1
} hsv;

static hsv   rgb2hsv(rgb in);
static rgb   hsv2rgb(hsv in);

hsv rgb2hsv(rgb in)
{
	hsv			out;
	double		min, max, delta;
	
	min = in.r < in.g ? in.r : in.g;
	min = min  < in.b ? min  : in.b;

	max = in.r > in.g ? in.r : in.g;
	max = max  > in.b ? max  : in.b;
	
	out.v = max;								// v
	delta = max - min;
    if (delta < 0.00001)
    {
        out.s = 0;
        out.h = 0; // undefined, maybe nan?
        return out;
    }
	if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
		out.s = (delta / max);					// s
	} else {
		// if max is 0, then r = g = b = 0				
        // s = 0, h is undefined
		out.s = 0.0;
		out.h = NAN;							// its now undefined
		return out;
	}
	if( in.r >= max )							// > is bogus, just keeps compilor happy
		out.h = ( in.g - in.b ) / delta;		// between yellow & magenta
	else
	if( in.g >= max )
		out.h = 2.0 + ( in.b - in.r ) / delta;	// between cyan & yellow
	else
		out.h = 4.0 + ( in.r - in.g ) / delta;	// between magenta & cyan
	
	out.h *= 60.0;								// degrees
	
	if( out.h < 0.0 )
		out.h += 360.0;
		
	return out;
}


rgb hsv2rgb(hsv in)
{
	double		hh, p, q, t, ff;
	long		i;
	rgb			out;
	
	if(in.s <= 0.0) {		// < is bogus, just shuts up warnings
		out.r = in.v;
		out.g = in.v;
		out.b = in.v;
		return out;
	}
	hh = in.h;
	if(hh >= 360.0) hh = 0.0;
	hh /= 60.0;
	i = (long)hh;
	ff = hh - i;
	p = in.v * (1.0 - in.s);
	q = in.v * (1.0 - (in.s * ff));
	t = in.v * (1.0 - (in.s * (1.0 - ff)));
	
	switch(i) {
	case 0:
		out.r = in.v;
		out.g = t;
		out.b = p;
		break;
	case 1:
		out.r = q;
		out.g = in.v;
		out.b = p;
		break;
	case 2:
		out.r = p;
		out.g = in.v;
		out.b = t;
		break;
		
	case 3:
		out.r = p;
		out.g = q;
		out.b = in.v;
		break;
	case 4:
		out.r = t;
		out.g = p;
		out.b = in.v;
		break;
	case 5:
	default:
		out.r = in.v;
		out.g = p;
		out.b = q;
		break;
	}
	return out;		
}

Solution 2 - C++

You can also try this code without floats (faster but less accurate):

typedef struct RgbColor
{
	unsigned char r;
	unsigned char g;
	unsigned char b;
} RgbColor;

typedef struct HsvColor
{
	unsigned char h;
	unsigned char s;
	unsigned char v;
} HsvColor;

RgbColor HsvToRgb(HsvColor hsv)
{
	RgbColor rgb;
	unsigned char region, remainder, p, q, t;
	
	if (hsv.s == 0)
	{
		rgb.r = hsv.v;
		rgb.g = hsv.v;
		rgb.b = hsv.v;
		return rgb;
	}
	
	region = hsv.h / 43;
	remainder = (hsv.h - (region * 43)) * 6; 
	
	p = (hsv.v * (255 - hsv.s)) >> 8;
	q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
	t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
	
	switch (region)
	{
		case 0:
			rgb.r = hsv.v; rgb.g = t; rgb.b = p;
			break;
		case 1:
			rgb.r = q; rgb.g = hsv.v; rgb.b = p;
			break;
		case 2:
			rgb.r = p; rgb.g = hsv.v; rgb.b = t;
			break;
		case 3:
			rgb.r = p; rgb.g = q; rgb.b = hsv.v;
			break;
		case 4:
			rgb.r = t; rgb.g = p; rgb.b = hsv.v;
			break;
		default:
			rgb.r = hsv.v; rgb.g = p; rgb.b = q;
			break;
	}
	
	return rgb;
}

HsvColor RgbToHsv(RgbColor rgb)
{
	HsvColor hsv;
	unsigned char rgbMin, rgbMax;

	rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
	rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
	
	hsv.v = rgbMax;
	if (hsv.v == 0)
	{
		hsv.h = 0;
		hsv.s = 0;
		return hsv;
	}

	hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
	if (hsv.s == 0)
	{
		hsv.h = 0;
		return hsv;
	}

	if (rgbMax == rgb.r)
		hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
	else if (rgbMax == rgb.g)
		hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
	else
		hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);

	return hsv;
}

Note that this algorithm uses 0-255 as it's range (not 0-360) as that was requested by the author of this question.

Solution 3 - C++

I wrote this in HLSL for our rendering engine, it has no conditions in it:

	float3	HSV2RGB( float3 _HSV )
	{
		_HSV.x = fmod( 100.0 + _HSV.x, 1.0 );										// Ensure [0,1[

		float	HueSlice = 6.0 * _HSV.x;											// In [0,6[
		float	HueSliceInteger = floor( HueSlice );
		float	HueSliceInterpolant = HueSlice - HueSliceInteger;					// In [0,1[ for each hue slice

		float3	TempRGB = float3(	_HSV.z * (1.0 - _HSV.y),
									_HSV.z * (1.0 - _HSV.y * HueSliceInterpolant),
									_HSV.z * (1.0 - _HSV.y * (1.0 - HueSliceInterpolant)) );

		// The idea here to avoid conditions is to notice that the conversion code can be rewritten:
		//    if      ( var_i == 0 ) { R = V		 ; G = TempRGB.z ; B = TempRGB.x }
		//    else if ( var_i == 2 ) { R = TempRGB.x ; G = V		 ; B = TempRGB.z }
		//    else if ( var_i == 4 ) { R = TempRGB.z ; G = TempRGB.x ; B = V     }
		// 
		//    else if ( var_i == 1 ) { R = TempRGB.y ; G = V		 ; B = TempRGB.x }
		//    else if ( var_i == 3 ) { R = TempRGB.x ; G = TempRGB.y ; B = V     }
		//    else if ( var_i == 5 ) { R = V		 ; G = TempRGB.x ; B = TempRGB.y }
		//
		// This shows several things:
		//	. A separation between even and odd slices
		//	. If slices (0,2,4) and (1,3,5) can be rewritten as basically being slices (0,1,2) then
		//		the operation simply amounts to performing a "rotate right" on the RGB components
		//	. The base value to rotate is either (V, B, R) for even slices or (G, V, R) for odd slices
		//
		float	IsOddSlice = fmod( HueSliceInteger, 2.0 );							// 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
		float	ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice);			// (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)

		float3	ScrollingRGBForEvenSlices = float3( _HSV.z, TempRGB.zx );			// (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
		float3	ScrollingRGBForOddSlices = float3( TempRGB.y, _HSV.z, TempRGB.x );	// (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
		float3	ScrollingRGB = lerp( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );

		float	IsNotFirstSlice = saturate( ThreeSliceSelector );					// 1 if NOT the first slice (true for slices 1 and 2)
		float	IsNotSecondSlice = saturate( ThreeSliceSelector-1.0 );				// 1 if NOT the first or second slice (true only for slice 2)

		return	lerp( ScrollingRGB.xyz, lerp( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice );	// Make the RGB rotate right depending on final slice index
	}

Solution 4 - C++

Here's a C implementation based on Agoston's Computer Graphics and Geometric Modeling: Implementation and Algorithms p. 304, with H ∈ [0, 360] and S,V ∈ [0, 1].

#include <math.h>

typedef struct {
    double r;       // ∈ [0, 1]
    double g;       // ∈ [0, 1]
    double b;       // ∈ [0, 1]
} rgb;

typedef struct {
    double h;       // ∈ [0, 360]
    double s;       // ∈ [0, 1]
    double v;       // ∈ [0, 1]
} hsv;

rgb hsv2rgb(hsv HSV)
{
    rgb RGB;
    double H = HSV.h, S = HSV.s, V = HSV.v,
            P, Q, T,
            fract;

    (H == 360.)?(H = 0.):(H /= 60.);
    fract = H - floor(H);

    P = V*(1. - S);
    Q = V*(1. - S*fract);
    T = V*(1. - S*(1. - fract));

    if      (0. <= H && H < 1.)
        RGB = (rgb){.r = V, .g = T, .b = P};
    else if (1. <= H && H < 2.)
        RGB = (rgb){.r = Q, .g = V, .b = P};
    else if (2. <= H && H < 3.)
        RGB = (rgb){.r = P, .g = V, .b = T};
    else if (3. <= H && H < 4.)
        RGB = (rgb){.r = P, .g = Q, .b = V};
    else if (4. <= H && H < 5.)
        RGB = (rgb){.r = T, .g = P, .b = V};
    else if (5. <= H && H < 6.)
        RGB = (rgb){.r = V, .g = P, .b = Q};
    else
        RGB = (rgb){.r = 0., .g = 0., .b = 0.};

    return RGB;
}

Solution 5 - C++

@fins's answer has an overflow issue on Arduio as you turn the saturation down. Here it is with some values converted to int to prevent that.

typedef struct RgbColor
{
    unsigned char r;
    unsigned char g;
    unsigned char b;
} RgbColor;

typedef struct HsvColor
{
    unsigned char h;
    unsigned char s;
    unsigned char v;
} HsvColor;

RgbColor HsvToRgb(HsvColor hsv)
{
    RgbColor rgb;
    unsigned char region, p, q, t;
    unsigned int h, s, v, remainder;

    if (hsv.s == 0)
    {
        rgb.r = hsv.v;
        rgb.g = hsv.v;
        rgb.b = hsv.v;
        return rgb;
    }

    // converting to 16 bit to prevent overflow
    h = hsv.h;
    s = hsv.s;
    v = hsv.v;

    region = h / 43;
    remainder = (h - (region * 43)) * 6; 

    p = (v * (255 - s)) >> 8;
    q = (v * (255 - ((s * remainder) >> 8))) >> 8;
    t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;

    switch (region)
    {
        case 0:
            rgb.r = v;
            rgb.g = t;
            rgb.b = p;
            break;
        case 1:
            rgb.r = q;
            rgb.g = v;
            rgb.b = p;
            break;
        case 2:
            rgb.r = p;
            rgb.g = v;
            rgb.b = t;
            break;
        case 3:
            rgb.r = p;
            rgb.g = q;
            rgb.b = v;
            break;
        case 4:
            rgb.r = t;
            rgb.g = p;
            rgb.b = v;
            break;
        default:
            rgb.r = v;
            rgb.g = p;
            rgb.b = q;
            break;
    }

    return rgb;
}

HsvColor RgbToHsv(RgbColor rgb)
{
    HsvColor hsv;
    unsigned char rgbMin, rgbMax;

    rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
    rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);

    hsv.v = rgbMax;
    if (hsv.v == 0)
    {
        hsv.h = 0;
        hsv.s = 0;
        return hsv;
    }

    hsv.s = 255 * ((long)(rgbMax - rgbMin)) / hsv.v;
    if (hsv.s == 0)
    {
        hsv.h = 0;
        return hsv;
    }

    if (rgbMax == rgb.r)
        hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
    else if (rgbMax == rgb.g)
        hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
    else
        hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);

    return hsv;
}

Solution 6 - C++

this should be on here: it works anyway. And it looks good compared to the above ones.

hlsl code

		float3 Hue(float H)
		{
			half R = abs(H * 6 - 3) - 1;
			half G = 2 - abs(H * 6 - 2);
			half B = 2 - abs(H * 6 - 4);
			return saturate(half3(R,G,B));
		}

		half4 HSVtoRGB(in half3 HSV)
		{
			return half4(((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z,1);
		}

float3 is 16 bit precision vector3 data type, i.e. float3 hue() is returns a data type (x,y,z) e.g. (r,g,b), half is same with half precision, 8bit, a float4 is (r,g,b,a) 4 values.

Solution 7 - C++

This isn't C, but it's certainly does work. All the other methods I see here work by casing everything into parts of a hexagon, and approximating "angles" from that. By instead starting with a different equation using cosines, and solving for h s and v, you get a lot nicer relationship between hsv and rgb, and tweening becomes smoother (at the cost of it being way slower).

Assume everything is floating point. If r g and b go from 0 to 1, h goes from 0 to 2pi, v goes from 0 to 4/3, and s goes from 0 to 2/3.

The following code is written in Lua. It's easily translatable into anything else.

local hsv do
	hsv			={}
	local atan2	=math.atan2
	local cos	=math.cos
	local sin	=math.sin

	function hsv.fromrgb(r,b,g)
		local c=r+g+b
		if c<1e-4 then
			return 0,2/3,0
		else
			local p=2*(b*b+g*g+r*r-g*r-b*g-b*r)^0.5
			local h=atan2(b-g,(2*r-b-g)/3^0.5)
			local s=p/(c+p)
			local v=(c+p)/3
			return h,s,v
		end
	end

	function hsv.torgb(h,s,v)
		local r=v*(1+s*(cos(h)-1))
		local g=v*(1+s*(cos(h-2.09439)-1))
		local b=v*(1+s*(cos(h+2.09439)-1))
		return r,g,b
	end

	function hsv.tween(h0,s0,v0,h1,s1,v1,t)
		local dh=(h1-h0+3.14159)%6.28318-3.14159
		local h=h0+t*dh
		local s=s0+t*(s1-s0)
		local v=v0+t*(v1-v0)
		return h,s,v
	end
end

Solution 8 - C++

GLSL Shader version based on Patapoms answer:

vec3 HSV2RGB( vec3 hsv )
{
	hsv.x = mod( 100.0 + hsv.x, 1.0 ); // Ensure [0,1[
	float   HueSlice = 6.0 * hsv.x; // In [0,6[
	float   HueSliceInteger = floor( HueSlice );
	float   HueSliceInterpolant = HueSlice - HueSliceInteger; // In [0,1[ for each hue slice
	vec3  TempRGB = vec3(   hsv.z * (1.0 - hsv.y), hsv.z * (1.0 - hsv.y * HueSliceInterpolant), hsv.z * (1.0 - hsv.y * (1.0 - HueSliceInterpolant)) );
	float   IsOddSlice = mod( HueSliceInteger, 2.0 ); // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
	float   ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
	vec3  ScrollingRGBForEvenSlices = vec3( hsv.z, TempRGB.zx );           // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
	vec3  ScrollingRGBForOddSlices = vec3( TempRGB.y, hsv.z, TempRGB.x );  // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
	vec3  ScrollingRGB = mix( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
	float   IsNotFirstSlice = clamp( ThreeSliceSelector, 0.0,1.0 );                   // 1 if NOT the first slice (true for slices 1 and 2)
	float   IsNotSecondSlice = clamp( ThreeSliceSelector-1.0, 0.0,1. );              // 1 if NOT the first or second slice (true only for slice 2)
	return  mix( ScrollingRGB.xyz, mix( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice );    // Make the RGB rotate right depending on final slice index
}

Solution 9 - C++

I'm not C++ developer so I will not provide code. But I can provide simple hsv2rgb algorithm (rgb2hsv here) which I currently discover - I update wiki with description: HSV and HLS. Main improvement is that I carefully observe r,g,b as hue functions and introduce simpler shape function to describe them (without loosing accuracy). The Algorithm - on input we have: h (0-255), s (0-255), v(0-255)

r = 255*f(5),   g = 255*f(3),   b = 255*f(1)

We use function f described as follows

f(n) = v/255 - (v/255)*(s/255)*max(min(k,4-k,1),0)

where (mod can return fraction part; k is floating point number)

k = (n+h*360/(255*60)) mod 6;

Here are snippets/PoV in SO in JS: HSV and HSL

Solution 10 - C++

Here is an online converter with an article after explaining all the algorithms for color conversion.

You probably would prefer a ready-made C version but it should not be long to apply and it could help other people trying to do the same in another language or with another color space.

Solution 11 - C++

Here's one which i just wrote this morning based on pretty much the same math as above:

/* math adapted from: http://www.rapidtables.com/convert/color/rgb-to-hsl.htm
 * reasonably optimized for speed, without going crazy */
void rgb_to_hsv (int r, int g, int b, float *r_h, float *r_s, float *r_v) {
  float rp, gp, bp, cmax, cmin, delta, l;
  int cmaxwhich, cminwhich;
  
  rp = ((float) r) / 255;
  gp = ((float) g) / 255;
  bp = ((float) b) / 255;
  
  //debug ("rgb=%d,%d,%d rgbprime=%f,%f,%f", r, g, b, rp, gp, bp);
  
  cmax = rp;
  cmaxwhich = 0; /* faster comparison afterwards */
  if (gp > cmax) { cmax = gp; cmaxwhich = 1; }
  if (bp > cmax) { cmax = bp; cmaxwhich = 2; }
  cmin = rp;
  cminwhich = 0;
  if (gp < cmin) { cmin = gp; cminwhich = 1; }
  if (bp < cmin) { cmin = bp; cminwhich = 2; }
  
  //debug ("cmin=%f,cmax=%f", cmin, cmax);
  delta = cmax - cmin;
  
  /* HUE */
  if (delta == 0) {
    *r_h = 0;
  } else {
    switch (cmaxwhich) {
      case 0: /* cmax == rp */
        *r_h = HUE_ANGLE * (fmod ((gp - bp) / delta, 6));
      break;
      
      case 1: /* cmax == gp */
        *r_h = HUE_ANGLE * (((bp - rp) / delta) + 2);
      break;
      
      case 2: /* cmax == bp */
        *r_h = HUE_ANGLE * (((rp - gp) / delta) + 4);
      break;
    }
    if (*r_h < 0)
      *r_h += 360;
  }
  
  /* LIGHTNESS/VALUE */
  //l = (cmax + cmin) / 2;
  *r_v = cmax;
  
  /* SATURATION */
  /*if (delta == 0) {
    *r_s = 0;
  } else {
    *r_s = delta / (1 - fabs (1 - (2 * (l - 1))));
  }*/
  if (cmax == 0) {
    *r_s = 0;
  } else {
    *r_s = delta / cmax;
  }
  //debug ("rgb=%d,%d,%d ---> hsv=%f,%f,%f", r, g, b, *r_h, *r_s, *r_v);
}


void hsv_to_rgb (float h, float s, float v, int *r_r, int *r_g, int *r_b) {
  if (h > 360)
    h -= 360;
  if (h < 0)
    h += 360;
  h = CLAMP (h, 0, 360);
  s = CLAMP (s, 0, 1);
  v = CLAMP (v, 0, 1);
  float c = v * s;
  float x = c * (1 - fabsf (fmod ((h / HUE_ANGLE), 2) - 1));
  float m = v - c;
  float rp, gp, bp;
  int a = h / 60;
  
  //debug ("h=%f, a=%d", h, a);
  
  switch (a) {
    case 0:
      rp = c;
      gp = x;
      bp = 0;
    break;
    
    case 1:
      rp = x;
      gp = c;
      bp = 0;
    break;
    
    case 2:
      rp = 0;
      gp = c;
      bp = x;
    break;
    
    case 3:
      rp = 0;
      gp = x;
      bp = c;
    break;
    
    case 4:
      rp = x;
      gp = 0;
      bp = c;
    break;
    
    default: // case 5:
      rp = c;
      gp = 0;
      bp = x;
    break;
  }
  
  *r_r = (rp + m) * 255;
  *r_g = (gp + m) * 255;
  *r_b = (bp + m) * 255;
  
  //debug ("hsv=%f,%f,%f, ---> rgb=%d,%d,%d", h, s, v, *r_r, *r_g, *r_b);
}

Solution 12 - C++

I created a possibly faster implementation by using 0-1 range for RGBS and V and 0-6 range for Hue (avoiding the division), and grouping the cases into two categories:

#include <math.h>
#include <float.h>

void fromRGBtoHSV(float rgb[], float hsv[])
{
//    for(int i=0; i<3; ++i)
//        rgb[i] = max(0.0f, min(1.0f, rgb[i]));

     hsv[0] = 0.0f;
     hsv[2] = max(rgb[0], max(rgb[1], rgb[2]));
     const float delta = hsv[2] - min(rgb[0], min(rgb[1], rgb[2]));
     
     if (delta < FLT_MIN)
         hsv[1] = 0.0f;
     else
     {
         hsv[1] = delta / hsv[2];
         if (rgb[0] >= hsv[2])
         {
             hsv[0] = (rgb[1] - rgb[2]) / delta;
             if (hsv[0] < 0.0f)
                 hsv[0] += 6.0f;
         }
         else if (rgb[1] >= hsv[2])
             hsv[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
         else
             hsv[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
     }    
}

void fromHSVtoRGB(const float hsv[], float rgb[])
{
    if(hsv[1] < FLT_MIN)
        rgb[0] = rgb[1] = rgb[2] = hsv[2];
    else
    {
        const float h = hsv[0];
        const int i = (int)h;
        const float f = h - i;
        const float p = hsv[2] * (1.0f - hsv[1]);

        if (i & 1) {
            const float q = hsv[2] * (1.0f - (hsv[1] * f));
            switch(i) {
            case 1:
                rgb[0] = q;
                rgb[1] = hsv[2];
                rgb[2] = p;
                break;
            case 3:
                rgb[0] = p;
                rgb[1] = q;
                rgb[2] = hsv[2];
                break;
            default:
                rgb[0] = hsv[2];
                rgb[1] = p;
                rgb[2] = q;
                break;
            }
        }
        else
        {
            const float t = hsv[2] * (1.0f - (hsv[1] * (1.0f - f)));
            switch(i) {
            case 0:
                rgb[0] = hsv[2];
                rgb[1] = t;
                rgb[2] = p;
                break;
            case 2:
                rgb[0] = p;
                rgb[1] = hsv[2];
                rgb[2] = t;
                break;
            default:
                rgb[0] = t;
                rgb[1] = p;
                rgb[2] = hsv[2];
                break;
            }
        }
    }
}

For 0-255 range just * 255.0f + 0.5f and assign it to an unsigned char (or divide by 255.0 to get the opposite).

Solution 13 - C++

// This pair of functions convert HSL to RGB and vice-versa.
// It's pretty optimized for execution speed

typedef unsigned char		BYTE
typedef struct _RGB
{
    BYTE R;
    BYTE G;
    BYTE B;
} RGB, *pRGB;
typedef struct _HSL
{
    float   H;  // color Hue (0.0 to 360.0 degrees)
    float   S;  // color Saturation (0.0 to 1.0)
    float   L;  // Luminance (0.0 to 1.0)
    float   V;  // Value (0.0 to 1.0)
} HSL, *pHSL;

float 	*fMin		(float *a, float *b)
{
	return *a <= *b?  a : b;
}

float 	*fMax		(float *a, float *b)
{
	return *a >= *b? a : b;
}

void 	RGBtoHSL	(pRGB rgb, pHSL hsl)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
// rgb->R, rgb->G, rgb->B: [0 to 255]
	float r = 		(float) rgb->R / 255;
	float g = 		(float) rgb->G / 255;
	float b = 		(float) rgb->B / 255;
	float *min = 	fMin(fMin(&r, &g), &b);
	float *max = 	fMax(fMax(&r, &g), &b);
	float delta = 	*max - *min;

// L, V [0.0 to 1.0]
	hsl->L = (*max + *min)/2;
	hsl->V = *max;
// Special case for H and S
	if (delta == 0)
	{
		hsl->H = 0.0f;
		hsl->S = 0.0f;
	}
	else
	{
// Special case for S
		if((*max == 0) || (*min == 1))
			hsl->S = 0;
		else
// S [0.0 to 1.0]
			hsl->S = (2 * *max - 2*hsl->L)/(1 - fabsf(2*hsl->L - 1));
// H [0.0 to 360.0]
		if 		(max == &r)  	hsl->H = fmod((g - b)/delta, 6);  	// max is R
		else if (max == &g)		hsl->H = (b - r)/delta + 2;			// max is G
		else					hsl->H = (r - g)/delta + 4;			// max is B
		hsl->H *= 60;
	}
}

void 	HSLtoRGB	(pHSL hsl, pRGB rgb)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
	float a, k, fm1, fp1, f1, f2, *f3;
// L, V, S: [0.0 to 1.0]
// rgb->R, rgb->G, rgb->B: [0 to 255]
	fm1 = -1;
	fp1 = 1;
	f1 = 1-hsl->L;
	a = hsl->S * *fMin(&hsl->L, &f1);
	k = fmod(0 + hsl->H/30, 12);
	f1 = k - 3;
	f2 = 9 - k;
	f3 = fMin(fMin(&f1, &f2), &fp1) ;
	rgb->R = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));

	k = fmod(8 + hsl->H/30, 12);
	f1 = k - 3;
	f2 = 9 - k;
	f3 = fMin(fMin(&f1, &f2), &fp1) ;
	rgb->G = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));

	k = fmod(4 + hsl->H/30, 12);
	f1 = k - 3;
	f2 = 9 - k;
	f3 = fMin(fMin(&f1, &f2), &fp1) ;
	rgb->B = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
}

Solution 14 - C++

This link has formulas for what you want. Then it's a matter of performance (numerical techniques) if you want it fast.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjmasterxView Question on Stackoverflow
Solution 1 - C++David HView Answer on Stackoverflow
Solution 2 - C++Leszek SzaryView Answer on Stackoverflow
Solution 3 - C++PatapomView Answer on Stackoverflow
Solution 4 - C++GeremiaView Answer on Stackoverflow
Solution 5 - C++ShonnView Answer on Stackoverflow
Solution 6 - C++LifeInTheTreesView Answer on Stackoverflow
Solution 7 - C++Trey ReynoldsView Answer on Stackoverflow
Solution 8 - C++Ray HulhaView Answer on Stackoverflow
Solution 9 - C++Kamil KiełczewskiView Answer on Stackoverflow
Solution 10 - C++MigView Answer on Stackoverflow
Solution 11 - C++deltView Answer on Stackoverflow
Solution 12 - C++Adriel JrView Answer on Stackoverflow
Solution 13 - C++CRISTIANView Answer on Stackoverflow
Solution 14 - C++JohnView Answer on Stackoverflow