
#ifndef XSPECT_H
#define XSPECT_H

/* 
 * Author:  Graeme W. Gill
 * Date:    21/6/01
 * Version: 1.00
 *
 * Copyright 2000 - 2010 Graeme W. Gill
 * All rights reserved.
 *
 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
 * see the License2.txt file for licencing details.
 */

/*
 * This class supports converting spectral samples
 * into CIE XYZ or D50 Lab tristimulous values.
 */

/*
 * TTBD:
 *
 */

#ifndef SALONEINSTLIB
#include "icc.h"		/* icclib ICC definitions */ 
#else /* SALONEINSTLIB */
#include "conv.h"		/* fake icclib ICC definitions */ 
#endif /* SALONEINSTLIB */

#ifdef __cplusplus
	extern "C" {
#endif

/* ------------------------------------------------------------------------------ */

/* Structure for conveying spectral information */

/* NOTE :- should ditch norm, and replace it by */
/* "units", ie. reflectance/transmittance 0..1, 0..100%, */
/* W/nm/m^2 or mW/nm/m^2 */
#define XSPECT_MAX_BANDS 601		/* Enought for 1nm from 300 to 900 */

typedef struct {
	int    spec_n;					/* Number of spectral bands, 0 if not valid */
	double spec_wl_short;			/* First reading wavelength in nm (shortest) */
	double spec_wl_long;			/* Last reading wavelength in nm (longest) */
	double norm;					/* Normalising scale value */
	double spec[XSPECT_MAX_BANDS];	/* Spectral value, shortest to longest */
} xspect;

/* Some helpful macro's: */

/* Copy everything except the spectral values */
#define XSPECT_COPY_INFO(PDST, PSRC) 						\
 (PDST)->spec_n = (PSRC)->spec_n,							\
 (PDST)->spec_wl_short = (PSRC)->spec_wl_short,				\
 (PDST)->spec_wl_long = (PSRC)->spec_wl_long,				\
 (PDST)->norm = (PSRC)->norm

/* Given an index and the sampling ranges, compute the sample wavelength */
#define XSPECT_WL(SHORT, LONG, N, IX) \
((SHORT) + (double)(IX) * ((LONG) - (SHORT))/((N)-1.0))

/* Given the address of an xspect and an index, compute the sample wavelegth */
#define XSPECT_XWL(PXSP, IX) \
(((PXSP)->spec_wl_short) + (double)(IX) * (((PXSP)->spec_wl_long) - ((PXSP)->spec_wl_short))/(((PXSP)->spec_n)-1.0))

/* Given a wavelength and the sampling ranges, compute the double index */
#define XSPECT_DIX(SHORT, LONG, N, WL) \
(((N)-1.0) * ((WL) - (SHORT))/((LONG) - (SHORT)))

/* Given the wavelength and address of an xspect, compute the double index */
#define XSPECT_XDIX(PXSP, WL) \
(((PXSP)->spec_n-1.0) * ((WL) - ((PXSP)->spec_wl_short))/(((PXSP)->spec_wl_long) - ((PXSP)->spec_wl_short)))

/* Given a wavelength and the sampling ranges, compute the nearest index */
#define XSPECT_IX(SHORT, LONG, N, WL) \
((int)floor(XSPECT_DIX(SHORT, LONG, N, WL) + 0.5))

/* Given a wavelength and address of an xspect, compute the nearest index */
#define XSPECT_XIX(PXSP, WL) \
((int)floor(XSPECT_DIX(PXSP, WL) + 0.5))

#ifndef SALONEINSTLIB

/* Single spectrum utility functions. Return NZ if error */
int write_xspect(char *fname, xspect *s);
int read_xspect(xspect *sp, char *fname);

/* CMF utility functions. Return NZ if error */
int write_cmf(char *fname, xspect cmf[3]);
int read_cmf(xspect cmf[3], char *fname);

/* Save a set of nspec spectrum to a CGATS file. Return NZ if error */
/* type 0 = SPECT, 1 = CMF */
int write_nxspect(char *fname, xspect *sp, int nspec, int type);

/* Restore a set of up to nspec spectrum from a CGATS file. Return NZ if error */
/* type  = any, 1 = SPECT, 2 = CMF, 3 = both */
int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int type);

#endif /* !SALONEINSTLIB*/

/* Get interpolated value at wavelenth (not normalised) */
double value_xspect(xspect *sp, double wl);

/* De-normalize and set normalisation factor to 1.0 */
void xspect_denorm(xspect *sp);

#ifndef SALONEINSTLIB
/* Convert from one xspect type to another */
void xspect2xspect(xspect *dst, xspect *targ, xspect *src);
#endif /* !SALONEINSTLIB*/

/* ------------------------------------------------------------------------------ */
/* Class for converting between spectral and CIE */

/* We build in some useful spectra */

/* Type of illumination */
typedef enum {
    icxIT_default	 = 0,	/* Default illuminant (usually D50) */
    icxIT_none		 = 1,	/* No illuminant - self luminous spectrum */
    icxIT_custom	 = 2,	/* Custom illuminant spectrum */
    icxIT_A			 = 3,	/* Standard Illuminant A */
	icxIT_C          = 4,	/* Standard Illuminant C */
    icxIT_D50		 = 5,	/* Daylight 5000K */
    icxIT_D50M2		 = 6,	/* Daylight 5000K, UV filtered (M2) */
    icxIT_D65		 = 7,	/* Daylight 6500K */
    icxIT_E		     = 8,	/* Equal Energy */
#ifndef SALONEINSTLIB
    icxIT_F5		 = 9,	/* Fluorescent, Standard, 6350K, CRI 72 */
    icxIT_F8		 = 10,	/* Fluorescent, Broad Band 5000K, CRI 95 */
    icxIT_F10		 = 11,	/* Fluorescent Narrow Band 5000K, CRI 81 */
	icxIT_Spectrocam = 12,	/* Spectrocam Xenon Lamp */
    icxIT_Dtemp		 = 13,	/* Daylight at specified temperature */
    icxIT_Ptemp		 = 14	/* Planckian at specified temperature */
#endif /* !SALONEINSTLIB*/
} icxIllumeType;

/* Fill in an xpsect with a standard illuminant spectrum */
/* return 0 on sucecss, nz if not matched */
int standardIlluminant(
xspect *sp,					/* Xspect to fill in */
icxIllumeType ilType,		/* Type of illuminant */
double temp);				/* Optional temperature in degrees kelvin, for Dtemp and Ptemp */

/* Given an emission spectrum, set the UV output to the given level. */
/* The shape of the UV is taken from FWA1_stim, and the level is */
/* with respect to the average of the input spectrum. */
void xsp_setUV(xspect *out, xspect *in, double uvlevel);


/* Type of observer */
typedef enum {
    icxOT_default			= 0,	/* Default observer (usually CIE_1931_2) */
    icxOT_none			    = 1,	/* No observer - (don't compute XYZ) */
    icxOT_custom			= 2,	/* Custom observer type weighting */
    icxOT_CIE_1931_2		= 3,	/* Standard CIE 1931 2 degree */
    icxOT_CIE_1964_10		= 4,	/* Standard CIE 1964 10 degree */
#ifndef SALONEINSTLIB
    icxOT_Stiles_Burch_2	= 5,	/* Stiles & Burch 1955 2 degree */
    icxOT_Judd_Voss_2		= 6,	/* Judd & Voss 1978 2 degree */
    icxOT_CIE_1964_10c		= 7,	/* Standard CIE 1964 10 degree, 2 degree compatible */
    icxOT_Shaw_Fairchild_2	= 8		/* Shaw & Fairchild 1997 2 degree */
#endif /* !SALONEINSTLIB*/
} icxObserverType;

/* Return pointers to three xpsects with a standard observer weighting curves */
/* return 0 on sucecss, nz if not matched */
int standardObserver(xspect *sp[3], icxObserverType obType);

/* Return a string describing the standard observer */
char *standardObserverDescription(icxObserverType obType);

/* Clamping state */
typedef enum {
    icxNoClamp			= 0,	/* Don't clamp XYZ/Lab to +ve */
    icxClamp			= 1,	/* Clamp XYZ/Lab to +ve */
} icxClamping;

/* The conversion object */
struct _xsp2cie {
	/* Private: */
	xspect illuminant;			/* Lookup conversion/observer illuminant */
	int isemis;					/* nz if we are doing an emission conversion */
	xspect observer[3];
	int doLab;					/* Return D50 Lab result */
	icxClamping clamp;			/* Clamp XYZ and Lab to be +ve */

#ifndef SALONEINSTLIB
	/* FWA compensation */
	double bw;		/* Integration bandwidth */
	xspect iillum;	/* Y = 1 Normalised instrument illuminant spectrum */
	xspect imedia;	/* Instrument measured media */
	xspect emits;	/* Estimated FWA emmission spectrum */
	xspect media;	/* Estimated base media (ie. minus FWA) */
	xspect tillum;	/* Y = 1 Normalised target/simulated instrument illuminant spectrum */
	xspect oillum;	/* Y = 1 Normalised observer illuminant spectrum */
	double Sm;		/* FWA Stimulation level for emits contribution */
	double FWAc;	/* FWA content (informational) */
	int    insteqtarget;	/* iillum == tillum, bypass FWA */
#endif /* !SALONEINSTLIB*/

	/* Public: */
	void (*del)(struct _xsp2cie *p);

	/* Convert (and possibly fwa correct) reflectance spectrum */
	/* Note that the input spectrum normalisation value is used. */
	/* Note that the returned XYZ is 0..1 range for reflectanc. */
	/* Emissive spectral values are assumed to be in mW/nm, and sampled */
	/* rather than integrated if they are not at 1nm spacing. */
	void (*convert) (struct _xsp2cie *p,	/* this */
	                 double *out,			/* Return XYZ or D50 Lab value */
	                 xspect *in				/* Spectrum to be converted, normalised by norm */
	                );

	/* Convert and also return (possibly corrected) reflectance spectrum */
	/* Spectrum will be same wlength range and readings as input spectrum */
	/* Note that the returned XYZ is 0..1 range for reflectanc. */
	/* Emissive spectral values are assumed to be in mW/nm, and sampled */
	/* rather than integrated if they are not at 1nm spacing. */
	void (*sconvert) (struct _xsp2cie *p,	/* this */
	                 xspect *sout,			/* Return corrected refl. spectrum (may be NULL) */
	                 double *out,			/* Return XYZ or D50 Lab value (may be NULL) */
	                 xspect *in				/* Spectrum to be converted, normalised by norm */
	                );

#ifndef SALONEINSTLIB
	/* Set Media White. This enables extracting and applying the */
	/* colorant reflectance value from/to the meadia. */
	/* return NZ if error */
	int (*set_mw) (struct _xsp2cie *p,	/* this */
	                xspect *white		/* Spectrum of plain media */
	                );

	/* Set Fluorescent Whitening Agent compensation */
	/* return NZ if error */
	int (*set_fwa) (struct _xsp2cie *p,	/* this */
					xspect *iillum,		/* Spectrum of instrument illuminant */
					xspect *tillum,		/* Spectrum of target/simulated instrument illuminant, */
										/* NULL to use observer illuminant. */
	                xspect *white		/* Spectrum of plain media */
	                );

	/* Set FWA given updated conversion illuminant. */
	/* (We assume that xsp2cie_set_fwa has been called first) */
	/* return NZ if error */
	int (*update_fwa_custillum) (struct _xsp2cie *p,
					xspect *tillum,		/* Spectrum of target/simulated instrument illuminant, */
										/* NULL to use set_fwa() value. */
	                xspect *custIllum	/* Spectrum of observer illuminant */
	                );	

	/* Get Fluorescent Whitening Agent compensation information */
	/* return NZ if error */
	void (*get_fwa_info) (struct _xsp2cie *p,	/* this */
					double *FWAc		/* FWA content as a ratio. */
	                );

	/* Extract the colorant reflectance value from the media. Takes FWA */
	/* into account if set. Media white or FWA must be set. */
	/* return NZ if error */
	int (*extract) (struct _xsp2cie *p,	/* this */
	                 xspect *out,			/* Extracted colorant refl. spectrum */
	                 xspect *in				/* Spectrum to be converted, normalised by norm */
	                );


	/* Apply the colorant reflectance value from the media. Takes FWA */
	/* into account if set. DOESN'T convert to FWA target illumination! */
	/* FWA must be set. */
	int (*apply) (struct _xsp2cie *p,	/* this */
	                 xspect *out,			/* Applied refl. spectrum */
	                 xspect *in				/* Colorant reflectance to be applied */
	                );
#endif /* !SALONEINSTLIB*/

}; typedef struct _xsp2cie xsp2cie;

xsp2cie *new_xsp2cie(
	icxIllumeType ilType,			/* Observer Illuminant to use */
	xspect        *custIllum,

	icxObserverType obType,			/* Observer */
	xspect        custObserver[3],
	icColorSpaceSignature  rcs,		/* Return color space, icSigXYZData or icSigLabData */
									/* ** Must be icSigXYZData if SALONEINSTLIB ** */
	icxClamping clamp				/* NZ to clamp XYZ/Lab to be +ve */
);

#ifndef SALONEINSTLIB
/* --------------------------- */
/* Spectrum locus              */

/* Return the spectrum locus range for the given observer. */
/* return 0 on sucecss, nz if observer not known */
int icx_spectrum_locus_range(double *min_wl, double *max_wl, icxObserverType obType);

/* Return an XYZ that is on the spectrum locus for the given observer. */
/* wl is the input wavelength in the range icx_spectrum_locus_range(), */
/* and return clipped result if outside this range. */
/* Return nz if observer unknown. */
int icx_spectrum_locus(double xyz[3], double in, icxObserverType obType);

/* Determine whether the given XYZ is outside the spectrum locus */
/* Return 0 if within locus */
/* Return 1 if outside locus */
/* Return 2 if unknown (bad observer) */
int icx_outside_spec_locus(double xyz[3], icxObserverType obType);

/* Return an aproximate RGB value for coloring within the spectrum locus */
void icx_spec_locus_color(double rgb[3], double xyz[3], icxObserverType obType);

/* --------------------------- */
/* Density and other functions */

/* Given a reflectance or transmition spectral product, */
/* return status T CMY + V density values */
void xsp_Tdensity(double *out,			/* Return CMYV density */
                 xspect *in				/* Spectral product to be converted */
                );

/* Given a reflectance or transmission XYZ value, */
/* return approximate status T CMYV log10 density values */
void icx_XYZ2Tdens(
double *out,			/* Return aproximate CMYV log10 density */
double *in				/* Input XYZ values */
);

/* Given a reflectance or transmission XYZ value, */
/* return log10 XYZ density values */
void icx_XYZ2dens(
double *out,			/* Return log10 XYZ density */
double *in				/* Input XYZ values */
);

/* Given an XYZ value, */
/* return sRGB values */
void icx_XYZ2sRGB(
double *out,			/* Return sRGB value */
double *wp,				/* Input XYZ white point (may be NULL) */
double *in				/* Input XYZ values */
);



/* Given an illuminant definition and an observer model, return */
/* the normalised XYZ value for that spectrum. */
/* Return 0 on sucess, 1 on error */
int icx_ill_sp2XYZ(
double xyz[3],			/* Return XYZ value with Y == 1 */
icxObserverType obType,	/* Observer */
xspect custObserver[3],	/* Optional custom observer */
icxIllumeType ilType,	/* Type of illuminant */
double ct,				/* Input temperature in degrees K */
xspect *custIllum);		/* Optional custom illuminant */


/* Given a choice of temperature dependent illuminant (icxIT_Dtemp or icxIT_Ptemp), */
/* return the closest correlated color temperature to the given spectrum or XYZ. */
/* An observer type can be chosen for interpretting the spectrum of the input and */
/* the illuminant. */
/* Note we're using CICDE94, rather than the traditional L*u*v* 2/3 space for CCT */
/* Return -1 on erorr */
double icx_XYZ2ill_ct(
double txyz[3],			/* If not NULL, return the XYZ of the black body temperature */
icxIllumeType ilType,	/* Type of illuminant, icxIT_Dtemp or icxIT_Ptemp */
icxObserverType obType,	/* Observer */
xspect custObserver[3],	/* Optional custom observer */
double xyz[3],			/* Input XYZ value, NULL if spectrum intead */
xspect *insp0,			/* Input spectrum value, NULL if xyz[] instead */
int viscct);			/* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */

/* Compute the CIE1995 CRI: Ra */
/* Return < 0.0 on error */
/* If invalid is not NULL, set it to nz if CRI */
/* is invalid because the sample is not white enough. */
double icx_CIE1995_CRI(
int *invalid,			/* if not NULL, set to nz if invalid */
xspect *sample			/* Illuminant sample to compute CRI of */
);
#endif /* !SALONEINSTLIB*/

#ifdef __cplusplus
	}
#endif

#endif /* XSPECTFM_H */






































