

#include "rgb-hsv.h"


/* RGB <=> HSV conversions from Foley and Van Dam  */




/* h in [0,360], s in [0,1], v in [0,1] where
   hue is the amount of color, sat is the purity of the color ie.
   how little the color is diluted by white light and val is the
   brightness independant of hue and sat (achromatic intensity  */


HSV RGBFtoHSV(RGBFloat rgb)
{				   
  float max, min;
  float h, s, v;
  

  max = MAX(rgb.r, MAX(rgb.g, rgb.b));
  min = MIN(rgb.r, MIN(rgb.g, rgb.b));
  
  v = max;
  
  if (max != 0.0)
    s = (max - min)/max;   
  else
    s = 0.0;
  
  if (s == 0.0)                              /* h is undefined */ 
    h = -1.0;                                /*   => give some value outside [0,360] */
  else {
    float delta = max - min;
    
    if (rgb.r == max)
      h = (rgb.g - rgb.b)/delta;             /* color between yellow and magenta  */
    else if (rgb.g == max)
      h = 2.0 + (rgb.b - rgb.r)/delta;       /* color between cyan and yellow  */
    else /* (rgb.b == max) */
      h = 4.0 + (rgb.r - rgb.g)/delta;       /* color between magenta and cyan  */

    h *= 60.0;                               /* convert h to degrees  */
    
    while (h < 0.0)      h += 360.0;
    while (h >= 360.0)   h -= 360.0;
  }

  return ((HSV) {h: h, s: s, v: v});
}


HSV RGBtoHSV(RGB rgb)
{
  return RGBFtoHSV(((RGBFloat) {r: rgb.r / 255.0,
                                g: rgb.g / 255.0,
                                b: rgb.b / 255.0}));
}

				    
RGBFloat HSVtoRGBF(HSV hsv)
{				   
  float f, p, q, t, v;
  int i;
  RGBFloat rgb;


  v = hsv.v;
  
  if (hsv.s == 0.0) {
    if (hsv.h == -1.0) {                     /* no hue .. achromatic  */
      return ((RGBFloat) {r: v, g: v, b: v});
    }
    else {                                   /* if s is 0 and h is defined .. error  */
      return ((RGBFloat) {r: -1.0, g: -1.0, b: -1.0});
    }
  }
  else {                                     /* if h has a value .. chromatic color */
    while (hsv.h < 0.0)      hsv.h += 360.0;
    while (hsv.h >= 360.0)   hsv.h -= 360.0;

    hsv.h /= 60.0;                           /* h is in [0,6)  */
    
    i = (int) hsv.h;                         /* largest integer <= h  */
    f = hsv.h - i;                           /* fractional part of h  */
    
    p  = v * (1.0 - hsv.s);
    q  = v * (1.0 - hsv.s * f);
    t  = v * (1.0 - hsv.s * (1.0 - f));

    switch (i) {
    case 0: rgb = ((RGBFloat) {r: v, g: t, b: p}); break;
    case 1: rgb = ((RGBFloat) {r: q, g: v, b: p}); break;
    case 2: rgb = ((RGBFloat) {r: p, g: v, b: t}); break;
    case 3: rgb = ((RGBFloat) {r: p, g: q, b: v}); break;
    case 4: rgb = ((RGBFloat) {r: t, g: p, b: v}); break;
    case 5: rgb = ((RGBFloat) {r: v, g: p, b: q}); break;
    }

    return rgb;
  }
}


RGB HSVtoRGB(HSV hsv)
{
  RGBFloat rgb = HSVtoRGBF(hsv);

  return ((RGB) {r: (uchar) (255.0 * rgb.r + 0.5),
                 g: (uchar) (255.0 * rgb.g + 0.5),
                 b: (uchar) (255.0 * rgb.b + 0.5)});
}
