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

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

 /*
  * $Log: lvcrgbhls.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: lvcrgbhls.c
 >>>>
 >>>>      Program Name: vcrgbhls
 >>>>
 >>>> Date Last Updated: Tue Mar  5 22:22:03 1991 
 >>>>
 >>>>          Routines: lvcrgbhls - the library call for vcrgbhls
 >>>>
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/


#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: lvcrgbhls - library call for vcrgbhls
*
* Purpose:
*    
*    Conversion between NTSC RGB and HLS color spaces
*    
*    
* Input:
*    
*    img1           input multispectral image of colormap model RGB or
*                   HLS.
*    
*    type           1 = conversion RGB to HLS
*    
*                   0 = conversion HLS to RGB
*    
*    type_flg       if set, use the conversion specified by type
*    
*    norm           normalization factor
*    
*    norm_flg       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: Donna Koechner, Tom Sauer
*    
*    
****************************************************************/


/* -library_def */
int
lvcrgbhls(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, *lptr, *sptr;
    float *ptr, *hls, *rgb;
    int i,nc,nr;
    float rc, gc, bc, max, min;
    float v,sv,vsf,fract,mid1,mid2;
    int sextant;

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

    if (img1->data_storage_type != VFF_TYP_FLOAT)
    {
        (void)fprintf(stderr,
                        "lvcrgbhls: 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,
                "lvcrgbhls: 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 HLS */

    switch(type)
    {
      case 1:  /* RGB->HLS  */
            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);
            }
            /* normalize the input image to 0.0 - 1.0 */
            ptr = (float *)(img1->imagedata);
            for (i=0 ; i<3*nr*nc ; i++)
            {
                ptr[i] /= norm;
            }

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

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

            hptr = (float *)&hls[0];
            lptr = (float *)&hls[nr*nc];
            sptr = (float *)&hls[2*nr*nc];

            for (i=0 ; i<nr*nc ; i++)
            {
                max = MAX3(*rptr,*gptr,*bptr);
                min = MIN3(*rptr,*gptr,*bptr);

                /* calculate lightness */
                *lptr = (max + min)/2;

                /* calculate saturation */
                if (max == min)         /* achromatic case */
                {
                    *sptr = 0;
                    *hptr = 0;
                }
                else
                {
                    if (*lptr <= 0.5)
                    {
                        *sptr = (max-min)/(max+min);
                    }
                    else
                    {
                        *sptr = (max-min)/(2-max-min);
                    }

                    /* calculate hue */
                    rc = (max-*rptr)/(max-min);
                    gc = (max-*gptr)/(max-min);
                    bc = (max-*bptr)/(max-min);

                    if (*rptr == max)
                    {
                        *hptr = bc-gc; /* between yellow & magenta */
                    }
                    else
                    {
                        if (*gptr == max)
                        {
                             *hptr = 2+rc-bc; /* between cyan & yellow*/
                        }
                        else
                        {
                            if (*bptr == max)
                            {
                               *hptr = 4+gc-rc; /* between magenta & cyan*/
                            }
                        }
                    }
                    *hptr /= 6;         /* divide into sextants */
                    if (*hptr < 0.0)
                        *hptr += 1.0;
                }
                rptr++; bptr++; gptr++;
                hptr++; lptr++; sptr++;
            }
            free(img1->imagedata);
            img1->imagedata = (char *)hls;
            img1->color_space_model = VFF_CM_HLS;
            break;

      case 0:  /* HLS->RGB  */
            if (img1->color_space_model != VFF_CM_HLS)
            {
                (void)fprintf(stderr,"input image must be HLS\n");
                return(0);
            }
            /* normalize the input image to 0.0 - 1.0 */

            ptr = (float *)(img1->imagedata);
            for (i=0 ; i<3*nr*nc ; i++)
            {
                ptr[i] /= norm;
            }

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

            /* create RGB image */      
            rgb = (float *)malloc((unsigned int)3*nc*nr*sizeof(float));
            if (rgb == NULL) 
            {
                (void)fprintf(stderr,"lvcrgbhls: 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++)
            {
                if (*lptr <= 0.5)
                    v = *lptr*(1.0 + *sptr);
                else
                    v = *lptr + *sptr - *lptr*(*sptr);

                /* if the saturation is zero, set R,G,B equal */

                if (*sptr == 0)
                    *rptr = *gptr = *bptr = *lptr;

                /* if sat. is not zero, look at which sextant the 
                   hue is in to determine RGB values. */

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