 /*
  * Khoros: $Id: lvcrgbhsv.c,v 1.1 1991/05/10 15:41:54 khoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: lvcrgbhsv.c,v 1.1 1991/05/10 15:41:54 khoros Exp $";
#endif

 /*
  * $Log: lvcrgbhsv.c,v $
 * Revision 1.1  1991/05/10  15:41:54  khoros
 * Initial revision
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1991, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"        /* Copyright 1991 by UNM */

/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 >>>>
 >>>>         File Name: lvcrgbhsv.c
 >>>>
 >>>>      Program Name: vcrgbhsv
 >>>>
 >>>> Date Last Updated: Tue Mar  5 22:22:11 1991 
 >>>>
 >>>>          Routines: lvcrgbhsv - the library call for vcrgbhsv
 >>>>
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/


#include "vinclude.h"


/* -library_includes */
#define MIN2(x,y)       (((x) < (y)) ? (x) : (y))
#define MAX2(x,y)       (((x) > (y)) ? (x) : (y))
#define MIN3(a,b,c)     (MIN2(a,MIN2(b,c)))
#define MAX3(a,b,c)     (MAX2(a,MAX2(b,c)))
/* -library_includes_end */


/****************************************************************
*
* Routine Name: lvcrgbhsv - library call for vcrgbhsv
*
* Purpose:
*    
*    Conversion between NTSC RGB and HSV color spaces.
*    
*    
* Input:
*    
*    img1           input multispectral image of colormap model RGB or
*                   HSV.
*    
*    type           1 = conversion RGB to HSV
*    
*                   0 = conversion HSV to RGB
*    
*    type_flag      if set, use the conversion specified by type
*    
*    norm           normalization factor
*    
*    norm_flag      normalization flag.  If set, use norm as the  nor-
*                   malization factor otherwise, default normalization
*                   is set to 255
*    
*    
* Output:
*    
*    img1           converted image of type FLOAT normalized between 0
*                   and 1.
*    
*    
*
* Written By: Tom Sauer
*    
*    
****************************************************************/


/* -library_def */
int
lvcrgbhsv(img1, type, type_flg, norm, norm_flg)
struct xvimage *img1;
int type, type_flg, norm_flg;
float norm;
/* -library_def_end */

/* -library_code */
{
    float *rptr, *gptr, *bptr;
    float *hptr, *sptr, *vptr;
    float *ptr, *hsv, *rgb;
    int i,nc,nr;
    float rc, gc, bc, max, min;
    float fract, new1, new2, new3;
    int sextant;

    char *program = "lvcrgbhsv";
    
    nc = img1->row_size;
    nr = img1->col_size;

    if (img1->data_storage_type != VFF_TYP_FLOAT)
    {
        (void)fprintf(stderr,
                        "lvcrgbhsv: image must be of type float/n");
        return(0);
    }

    if (! (proper_num_images(program,img1,1,FALSE)))
    {
       (void) fprintf(stderr,"\n\n%s:   ",program);
       (void) fprintf(stderr,"Can only work on files with one image\n\n");
       return(0);
    }

    if (! (proper_num_bands(program,img1,3,FALSE)))
    {
       (void) fprintf(stderr,"\n\n%s:   ",program);
       (void) fprintf(stderr,"Can only work on images with 3 data bands\n\n");
       return(0);
    }

    if( ! (proper_map_enable(program, img1, VFF_MAP_OPTIONAL, FALSE))) {
        (void)fprintf(stderr,
                "lvcrgbhsv: ERROR image map enable must be optional./n");
        (void)fprintf(stderr,
                "Use vmapdata to map the data through the map\n");
        return(0);
    }

    if (! norm_flg)
        norm = 255;     /* default normalization factor is 255 */
    if (! type_flg)
        type = 1;       /* default conversion is RGB to HSV */

    switch(type)
    {
      case 1:  /* RGB->HSV  */
            if (img1->color_space_model != VFF_CM_ntscRGB &&
                img1->color_space_model != VFF_CM_genericRGB)
            {
                (void)fprintf(stderr,"input image must be RGB\n");
                return(0);
            }

            ptr = (float *)(img1->imagedata);

            rptr = (float *)(&ptr[0]);
            gptr = (float *)(&ptr[nr*nc]);
            bptr = (float *)(&ptr[2*nr*nc]);

            /* create the HSV image */
            hsv = (float *)malloc(3*nc*nr*sizeof(float));
            if (hsv == NULL) 
            {
                (void)fprintf(stderr,"lvcrgbhsv: insufficient ");
                (void)fprintf(stderr,"memory available\n");
                return(0);
            }

            hptr = (float *)&hsv[0];
            sptr = (float *)&hsv[nr*nc];
            vptr = (float *)&hsv[2*nr*nc];

            for (i=0 ; i<nr*nc ; i++)
            {
 
                *rptr = *rptr / norm;
                *gptr = *gptr / norm;
                *bptr = *bptr / norm;

                *vptr = max = MAX3(*rptr,*gptr,*bptr);
                min = MIN3(*rptr,*gptr,*bptr);

                if ( max == 0.0)
                   *sptr = 0.0;
                else
                   *sptr = (max - min)/max;

                if ( *sptr == 0.0)
                   *hptr = 0.0;
                else
                {
                    /* calculate hue */
                   rc = (max - *rptr) / (max - min);
                   gc = (max - *gptr) / (max - min);
                   bc = (max - *bptr) / (max - min);
                   if ( *rptr == max)
                      *hptr = bc - gc;
                   else if ( *gptr == max )
                      *hptr = 2 + rc - bc;
                   else if ( *bptr == max ) 
                      *hptr = 4 + gc - rc;

                    *hptr /= 6;         /* divide into sextants */
                    if (*hptr < 0.0)
                        *hptr += 1.0;
                }
                rptr++; bptr++; gptr++;
                hptr++; sptr++; vptr++;

            }
            free(img1->imagedata);
            img1->imagedata = (char *)hsv;
            img1->color_space_model = VFF_CM_HSV;
            break;

      case 0:  /* HSV->RGB  */
            if (img1->color_space_model != VFF_CM_HSV)
            {
                (void)fprintf(stderr,"input image must be HSV\n");
                return(0);
            }

            ptr = (float *)(img1->imagedata);

            hptr = (float *)(&ptr[0]);
            sptr = (float *)(&ptr[nr*nc]);
            vptr = (float *)(&ptr[2*nr*nc]);

            /* create RGB image */      
            rgb = (float *)malloc((unsigned int)3*nc*nr*sizeof(float));
            if (rgb == NULL) 
            {
                (void)fprintf(stderr,"lvcrgbhsv: insufficient ");
                (void)fprintf(stderr,"memory available\n");
                return(0);
            }

            rptr = &rgb[0];
            gptr = &rgb[nr*nc];
            bptr = &rgb[2*nr*nc];

            /* use lightness value to determine the intensity of
               the RGB data */

            for (i=0 ; i<nr*nc ; i++)
            {
                     /* normalize the input image to 0.0 - 1.0 */
                *hptr = *hptr / norm;
                *sptr = *sptr / norm;
                *vptr = *vptr / norm;

                 /* if saturation is zero then assume the hue is zero */
                 /* otherwise an error would occur that we would not */
                 /* be able to handle */

                if (*sptr == 0.0 ) { 
                   *rptr = *vptr;
                   *gptr = *vptr;
                   *bptr = *vptr;
                }
                else {
                    if (*hptr < 0.0)
                        *hptr += 1.0;
                    *hptr *= 6.0;
                    sextant = (int)*hptr;
                    fract = *hptr - sextant;
                    
                    new1 = *vptr * (1 - *sptr);
                    new2 = *vptr * (1 - (*sptr * fract));
                    new3 = *vptr * (1 - (*sptr * (1 - fract)));
                    
                    switch (sextant)
                    {
                        case 0: *rptr = *vptr; *gptr = new3; *bptr = new1;
                                break;
                        case 1: *rptr = new2; *gptr = *vptr; *bptr = new1;
                                break;
                        case 2: *rptr = new1; *gptr = *vptr; *bptr = new3;
                                break;
                        case 3: *rptr = new1; *gptr = new2; *bptr = *vptr;
                                break;
                        case 4: *rptr = new3; *gptr = new1; *bptr = *vptr;
                                break;
                        case 5: *rptr = *vptr; *gptr = new1; *bptr = new2;
                                break;
                        default:
                                (void)fprintf(stderr,"lvcrgbhsv: ");
                                (void)fprintf(stderr,"inappropriate ");
                                (void)fprintf(stderr,"hue angle \n");
                                return(0);
                                break;
                    }
                }
                rptr++; bptr++; gptr++;
                hptr++; sptr++; vptr++;
            }
            free(img1->imagedata);
            img1->imagedata = (char *)rgb;
            img1->color_space_model = VFF_CM_ntscRGB;
            break;
        default:
                (void)fprintf(stderr,"lvcrgbhsv: conversion type ");
                (void)fprintf(stderr,"unknown.\n");
                return(0);
                break;
    }
    return(1);
}
/* -library_code_end */
