summaryrefslogtreecommitdiff
path: root/xicc/xspect.h
blob: caca8b2da40f3541b5afaf8ca3abd9c1adac00cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430

#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 */