From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- xicc/xspect.c | 4714 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4714 insertions(+) create mode 100644 xicc/xspect.c (limited to 'xicc/xspect.c') diff --git a/xicc/xspect.c b/xicc/xspect.c new file mode 100644 index 0000000..a372d60 --- /dev/null +++ b/xicc/xspect.c @@ -0,0 +1,4714 @@ + +/* + * International Color Consortium color transform expanded support + * + * Author: Graeme W. Gill + * Date: 21/6/01 + * Version: 1.00 + * + * Copyright 2000 - 2006 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. + * + * Based on the old iccXfm class. + */ + +/* + * This module supports converting spectral samples + * into CIE XYZ (1.0) or D50 Lab tristimulous values. + */ + +/* + * TTBD: + * + * If needed by ISO 13655-1009: + * fwa_convert() function takes two illuminants: + * first one is the measurement illumination to correct to, + * the second is the assumed illumination spectrum for XYZ conversion. + * so we compute the spectral reflectance as if the instrument had + * one sort of practical illuminant (and taking into account FWA), + * and then convert to D50 equivalent. + * Need to bypass FWA if inst illum == simulated inst. illum. + * Need to modify tools to allow optional param to -f which is + * the simulated instrument illum, then make -i have -M0, -M1, -M2 options. + * + * [Does this make any sense though ? That is what's happening + * for a standard A illuminant instrument emitting D50 XYZ values, + * but doesn't represent actually viewing under a (say) M2 illuminant. + * But is M0 actual A illuminant, or notional D50 measured by an A illuminant ?] + */ + +#include +#include +#include +#include +#include +#ifndef SALONEINSTLIB +# include "numlib.h" +# include "cgats.h" +# include "plot.h" /* For debugging */ +#else +# include "numsup.h" +#endif +#include "xspect.h" + +#define CLAMP_XYZ /* [def] Clamp XYZ to be >= 0.0 */ + +#ifndef SALONEINSTLIB + +#undef STOCKFWA /* [und] Use table shape else compute from flat line estimate*/ + +#undef DEBUG /* [und] Extra printouts + debugging messages */ +#undef DOPLOT /* [und] Plot FWA setup */ +#undef DOPLOT_ALL_FWA /* [und] Plot all FWA corrected conversions */ +#undef WRITE_FWA1_STIM /* [und] Write file "fwa1_stip.sp" when FWA is setup */ + +#endif /* !SALONEINSTLIB */ + +#ifndef CLAMP_XYZ +# pragma message("###### CLAMP_XYZ is not defined ######") +#endif + +#if defined(DEBUG) || defined(DOPLOT) || defined(DOPLOT_ALL_FWA) || defined(WRITE_FWA1_STIM) +# pragma message("###### xspect debugging is on ######") +#endif + +#ifdef DEBUG +# define DBG(xx) a1logd(g_log, 0, xx ) +# define DBGA g_log, 0 /* First argument to DBGF() */ +# define DBGF(xx) a1logd xx +#else +# define DBG(xx) +# define DBGF(xx) +#endif + +/* ======================================================== */ +#if defined(__APPLE__) && defined(__POWERPC__) + +/* Workaround for a PPC gcc 3.3 optimiser bug... */ +/* It seems to cause a segmentation fault instead of */ +/* converting an integer loop index into a float, */ +/* when there are sufficient variables in play. */ +static int gcc_bug_fix(int i) { + static int nn; + nn += i; + return nn; +} +#endif /* APPLE */ + +/* ======================================================== */ +/* Define various standard spectra */ + +/* ------------------ */ +/* Illuminant spectra */ + +/* Dummy "no illuminant" illuminant spectra used to signal an emmission */ +/* or equal energy 'E' illuminant */ +static xspect il_none = { + 54, 300.0, 830.0, /* 54 bands from 300 to 830 in 10nm steps */ + 1.0, /* Scale factor */ + { + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 + } +}; + + +/* CIE 15.2-1986 Table 1.1 */ +/* Part 1: CIE Standard Illuminant A relative spectral power distribution */ +/* This is a 2848K tungsten filament lamp (Acording to the old temperature scale) */ +/* and 2856 according to the newer temerature scale. */ +static xspect il_A = { + 107, 300.0, 830.0, /* 107 bands from 300 to 830 nm in 5nm steps */ + 100.0, /* Arbitrary scale factor */ + { + 0.930483, 1.128210, 1.357690, 1.622190, 1.925080, + 2.269800, 2.659810, 3.098610, 3.589680, 4.136480, + 4.742380, 5.410700, 6.144620, 6.947200, 7.821350, + 8.769800, 9.795100, 10.899600, 12.085300, 13.354300, + 14.708000, 16.148000, 17.675300, 19.290700, 20.995000, + 22.788300, 24.670900, 26.642500, 28.702700, 30.850800, + 33.085900, 35.406800, 37.812100, 40.300200, 42.869300, + 45.517400, 48.242300, 51.041800, 53.913200, 56.853900, + 59.861100, 62.932000, 66.063500, 69.252500, 72.495900, + 75.790300, 79.132600, 82.519300, 85.947000, 89.412400, + 92.912000, 96.442300, 100.000000, 103.582000, 107.184000, + 110.803000, 114.436000, 118.080000, 121.731000, 125.386000, + 129.043000, 132.697000, 136.346000, 139.988000, 143.618000, + 147.235000, 150.836000, 154.418000, 157.979000, 161.516000, + 165.028000, 168.510000, 171.963000, 175.383000, 178.769000, + 182.118000, 185.429000, 188.701000, 191.931000, 195.118000, + 198.261000, 201.359000, 204.409000, 207.411000, 210.365000, + 213.268000, 216.120000, 218.920000, 221.667000, 224.361000, + 227.000000, 229.585000, 232.115000, 234.589000, 237.008000, + 239.370000, 241.675000, 243.924000, 246.116000, 248.251000, + 250.329000, 252.350000, 254.314000, 256.221000, 258.071000, + 259.865000, 261.602000 + } +}; + +/* CIE 15.2-1986 Table 1.1 */ +/* Part 1: CIE Standard Illuminant C relative spectral power distribution */ +/* This is a CIE Illuminant A combined with a filter to simulate daylight. */ +static xspect il_C = { + 93, 320.0, 780.0, /* 107 bands from 300 to 830 nm in 5nm steps */ + 100.0, /* Arbitrary factor */ + { + 0.01, 0.20, 0.40, 1.55, 2.70, 4.85, 7.00, 9.95, 12.90, 17.20, + 21.40, 27.50, 33.00, 39.92, 47.40, 55.17, 63.30, 71.81, 80.60, 89.53, + 98.10, 105.80, 112.40, 117.75, 121.50, 123.45, 124.00, 123.60, 123.10, 123.30, + 123.80, 124.09, 123.90, 122.92, 120.70, 116.90, 112.10, 106.98, 102.30, 98.81, + 96.90, 96.78, 98.00, 99.94, 102.10, 103.95, 105.20, 105.67, 105.30, 104.11, + 102.30, 100.15, 97.80, 95.43, 93.20, 91.22, 89.70, 88.83, 88.40, 88.19, + 88.10, 88.06, 88.00, 87.86, 87.80, 87.99, 88.20, 88.20, 87.90, 87.22, + 86.30, 85.30, 84.00, 82.21, 80.20, 78.24, 76.30, 74.36, 72.40, 70.40, + 68.30, 66.30, 64.40, 62.80, 61.50, 60.20, 59.20, 58.50, 58.10, 58.00, + 58.20, 58.50, 59.10 + } +}; + +/* D50 illuminant spectra */ +static xspect il_D50 = { + 107, 300.0, 830.0, /* 107 bands from 300 to 830 nm in 5nm steps */ + 100.0, /* Arbitrary factor */ + { + 0.02, 1.03, 2.05, 4.91, 7.78, 11.26, 14.75, 16.35, 17.95, 19.48, + 21.01, 22.48, 23.94, 25.45, 26.96, 25.72, 24.49, 27.18, 29.87, 39.59, + 49.31, 52.91, 56.51, 58.27, 60.03, 58.93, 57.82, 66.32, 74.82, 81.04, + 87.25, 88.93, 90.61, 90.99, 91.37, 93.24, 95.11, 93.54, 91.96, 93.84, + 95.72, 96.17, 96.61, 96.87, 97.13, 99.61, 102.10, 101.43, 100.75, 101.54, + 102.32, 101.16, 100.00, 98.87, 97.74, 98.33, 98.92, 96.21, 93.50, 95.59, + 97.69, 98.48, 99.27, 99.16, 99.04, 97.38, 95.72, 97.29, 98.86, 97.26, + 95.67, 96.93, 98.19, 100.60, 103.00, 101.07, 99.13, 93.26, 87.38, 89.49, + 91.60, 92.25, 92.89, 84.87, 76.85, 81.68, 86.51, 89.55, 92.58, 85.40, + 78.23, 67.96, 57.69, 70.31, 82.92, 80.60, 78.27, 78.91, 79.55, 76.48, + 73.40, 68.66, 63.92, 67.35, 70.78, 72.61, 74.44 + } +}; + +/* D50M2 illuminant spectra, UV filtered */ +/* Computed from il_D50 */ +static xspect il_D50M2 = { + 0, 0.0, 0.0, + 0.0 +}; + + +/* CIE 15.2-1986 Table 1.1 */ +/* Part 2: CIE Standard Illuminant D65 relative spectral power distribution */ +static xspect il_D65 = { + 107, 300.0, 830.0, /* 107 bands from 300 to 830 nm in 5nm steps */ + 100.0, /* Arbitrary factor */ + { + 0.03410, 1.66430, 3.29450, 11.76520, 20.23600, + 28.64470, 37.05350, 38.50110, 39.94880, 42.43020, + 44.91170, 45.77500, 46.63830, 49.36370, 52.08910, + 51.03230, 49.97550, 52.31180, 54.64820, 68.70150, + 82.75490, 87.12040, 91.48600, 92.45890, 93.43180, + 90.05700, 86.68230, 95.77360, 104.86500, 110.93600, + 117.00800, 117.41000, 117.81200, 116.33600, 114.86100, + 115.39200, 115.92300, 112.36700, 108.81100, 109.08200, + 109.35400, 108.57800, 107.80200, 106.29600, 104.79000, + 106.23900, 107.68900, 106.04700, 104.40500, 104.22500, + 104.04600, 102.02300, 100.00000, 98.16710, 96.33420, + 96.06110, 95.78800, 92.23680, 88.68560, 89.34590, + 90.00620, 89.80260, 89.59910, 88.64890, 87.69870, + 85.49360, 83.28860, 83.49390, 83.69920, 81.86300, + 80.02680, 80.12070, 80.21460, 81.24620, 82.27780, + 80.28100, 78.28420, 74.00270, 69.72130, 70.66520, + 71.60910, 72.97900, 74.34900, 67.97650, 61.60400, + 65.74480, 69.88560, 72.48630, 75.08700, 69.33980, + 63.59270, 55.00540, 46.41820, 56.61180, 66.80540, + 65.09410, 63.38280, 63.84340, 64.30400, 61.87790, + 59.45190, 55.70540, 51.95900, 54.69980, 57.44060, + 58.87650, 60.31250 + } +}; + +#ifndef SALONEINSTLIB +/* General temperature Daylight spectra (Using CIE 1960 u,v CCT) */ +/* Fill in the given xspect with the specified daylight illuminant */ +/* Return nz if temperature is out of range */ +static int daylight_il(xspect *sp, double ct) { + static double s0[107] = { + 0.04, 3.02, 6.00, 17.80, 29.60, 42.45, 55.30, 56.30, 57.30, 59.55, + 61.80, 61.65, 61.50, 65.15, 68.80, 66.10, 63.40, 64.60, 65.80, 80.30, + 94.80, 99.80, 104.80, 105.35, 105.90, 101.35, 96.80, 105.35, 113.90, 119.75, + 125.60, 125.55, 125.50, 123.40, 121.30, 121.30, 121.30, 117.40, 113.50, 113.30, + 113.10, 111.95, 110.80, 108.65, 106.50, 107.65, 108.80, 107.05, 105.30, 104.85, + 104.40, 102.20, 100.00, 98.00, 96.00, 95.55, 95.10, 92.10, 89.10, 89.80, + 90.50, 90.40, 90.30, 89.35, 88.40, 86.20, 84.00, 84.55, 85.10, 83.50, + 81.90, 82.25, 82.60, 83.75, 84.90, 83.10, 81.30, 76.60, 71.90, 73.10, + 74.30, 75.35, 76.40, 69.85, 63.30, 67.50, 71.70, 74.35, 77.00, 71.10, + 65.20, 56.45, 47.70, 58.15, 68.60, 66.80, 65.00, 65.50, 66.00, 63.50, + 61.00, 57.15, 53.30, 56.10, 58.90, 60.40, 61.90 + }; + static double s1[107] = { + 0.02, 2.26, 4.50, 13.45, 22.40, 32.20, 42.00, 41.30, 40.60, 41.10, + 41.60, 39.80, 38.00, 40.20, 42.40, 40.45, 38.50, 36.75, 35.00, 39.20, + 43.40, 44.85, 46.30, 45.10, 43.90, 40.50, 37.10, 36.90, 36.70, 36.30, + 35.90, 34.25, 32.60, 30.25, 27.90, 26.10, 24.30, 22.20, 20.10, 18.15, + 16.20, 14.70, 13.20, 10.90, 8.60, 7.35, 6.10, 5.15, 4.20, 3.05, + 1.90, 0.95, 0.00, -0.80, -1.60, -2.55, -3.50, -3.50, -3.50, -4.65, + -5.80, -6.50, -7.20, -7.90, -8.60, -9.05, -9.50, -10.20, -10.90, -10.80, + -10.70, -11.35, -12.00, -13.00, -14.00, -13.80, -13.60, -12.80, -12.00, -12.65, + -13.30, -13.10, -12.90, -11.75, -10.60, -11.10, -11.60, -11.90, -12.20, -11.20, + -10.20, -9.00, -7.80, -9.50, -11.20, -10.80, -10.40, -10.50, -10.60, -10.15, + -9.70, -9.00, -8.30, -8.80, -9.30, -9.55, -9.80 + }; + static double s2[107] = { + 0.00, 1.00, 2.00, 3.00, 4.00, 6.25, 8.50, 8.15, 7.80, 7.25, + 6.70, 6.00, 5.30, 5.70, 6.10, 4.55, 3.00, 2.10, 1.20, 0.05, + -1.10, -0.80, -0.50, -0.60, -0.70, -0.95, -1.20, -1.90, -2.60, -2.75, + -2.90, -2.85, -2.80, -2.70, -2.60, -2.60, -2.60, -2.20, -1.80, -1.65, + -1.50, -1.40, -1.30, -1.25, -1.20, -1.10, -1.00, -0.75, -0.50, -0.40, + -0.30, -0.15, 0.00, 0.10, 0.20, 0.35, 0.50, 1.30, 2.10, 2.65, + 3.20, 3.65, 4.10, 4.40, 4.70, 4.90, 5.10, 5.90, 6.70, 7.00, + 7.30, 7.95, 8.60, 9.20, 9.80, 10.00, 10.20, 9.25, 8.30, 8.95, + 9.60, 9.05, 8.50, 7.75, 7.00, 7.30, 7.60, 7.80, 8.00, 7.35, + 6.70, 5.95, 5.20, 6.30, 7.40, 7.10, 6.80, 6.90, 7.00, 6.70, + 6.40, 5.95, 5.50, 5.80, 6.10, 6.30, 6.50 + }; + int i; + double xd, yd; + double m1, m2; + + if (ct < 1000.0 || ct > 35000.0) /* Actually, accuracy is guaranteed from only 4000 - 25000 */ + return 1; + + /* Compute chromaticity coordinates */ + if (ct < 7000.0) { + xd = -4.6070e9/(ct * ct * ct) + 2.9678e6/(ct * ct) + 0.09911e3/ct + 0.244063; + } else { + xd = -2.0064e9/(ct * ct * ct) + 1.9018e6/(ct * ct) + 0.24748e3/ct + 0.237040; + } + yd = -3.000 * xd * xd + 2.870 * xd - 0.275; + + /* Compute m factors */ + m1 = (-1.3515 - 1.7703 * xd + 5.9114 * yd)/(0.0241 + 0.2562 * xd - 0.7341 * yd); + m2 = (0.0300 - 31.4424 * xd + 30.0717 * yd)/(0.0241 + 0.2562 * xd - 0.7341 * yd); + + /* Compute spectral values */ + for (i = 0; i < 107; i++) { + sp->spec[i] = s0[i] + m1 * s1[i] + m2 * s2[i]; + } + sp->spec_n = 107; + sp->spec_wl_short = 300.0; + sp->spec_wl_long = 830; + sp->norm = 100.0; /* Arbitrary */ + + return 0; +} + +/* General temperature Planckian (black body) spectra */ +/* Fill in the given xspect with the specified Planckian illuminant */ +/* Return nz if temperature is out of range */ +static int planckian_il(xspect *sp, double ct) { + int i; + double wl, norm; + + if (ct < 1.0 || ct > 1e6) /* set some arbitrary limits */ + return 1; + + /* Set out targets */ +// sp->spec_n = 107; /* 5nm */ + sp->spec_n = 531; /* 1nm */ + sp->spec_wl_short = 300.0; + sp->spec_wl_long = 830; + + /* Compute spectral values using Plank's radiation law: */ + /* Normalise numbers by energy at 560 nm */ + wl = 1e-9 * 560; + norm = 0.01 * (3.74183e-16 * pow(wl, -5.0)) / (exp(1.4388e-2 / (wl * ct)) - 1.0); + for (i = 0; i < sp->spec_n; i++) { + wl = 1e-9 * XSPECT_XWL(sp, i); /* Wavelength in meters */ + sp->spec[i] = (3.74183e-16 * pow(wl, -5.0)) / (exp(1.4388e-2 / (wl * ct)) - 1.0); + sp->spec[i] /= norm; + + } + sp->norm = 100.0; /* Arbitrary */ + + return 0; +} + +/* CIE F5 */ +/* Fluorescent, Standard, 6350K, CRI 72 */ +static xspect il_F5 = { + 107, 300.0, 830.0, /* 109 bands from 300 to 830 nm in 5nm steps */ + 20.0, /* Arbitrary scale factor */ + { +/* 300 */ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/* 340 */ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/* 380 */ 1.87, 2.35, 2.92, 3.45, 5.10, 18.91, 6.00, 6.11, 6.85, 7.58, +/* 430 */ 8.31, 40.76, 16.06, 10.32, 10.91, 11.40, 11.83, 12.17, 12.40, 12.54, +/* 480 */ 12.58, 12.52, 12.47, 12.20, 11.89, 11.61, 11.33, 11.10, 10.96, 10.97, +/* 530 */ 11.16, 11.54, 12.12, 27.78, 17.73, 14.47, 15.20, 15.77, 16.10, 18.54, +/* 580 */ 19.50, 15.39, 14.64, 13.72, 12.69, 11.57, 10.45, 9.35, 8.29, 7.32, +/* 630 */ 6.41, 5.63, 4.90, 4.26, 3.72, 3.25, 2.83, 2.49, 2.19, 1.93, +/* 680 */ 1.71, 1.52, 1.48, 1.26, 1.13, 1.05, 0.96, 0.85, 0.78, 0.72, +/* 730 */ 0.68, 0.67, 0.65, 0.61, 0.62, 0.59, 0.62, 0.64, 0.55, 0.47, +/* 780 */ 0.40, +/* 785 */ 0.0, 0.0, 0.0, 0.0, 0.0, +/* 810 */ 0.0, 0.0, 0.0, 0.0, 0.0 + } +}; + + +/* CIE F8 */ +/* Fluorescent, Wide band 5000K, CRI 95 */ +static xspect il_F8 = { + 107, 300.0, 830.0, /* 109 bands from 300 to 830 nm in 5nm steps */ + 20.0, /* Arbitrary scale factor */ + { +/* 300 */ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/* 340 */ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, +/* 380 */ 1.21, 1.5, 1.81, 2.13, 3.17, 13.08, 3.83, 3.45, 3.86, 4.42, +/* 430 */ 5.09, 34.10, 12.42, 7.68, 8.6, 9.46, 10.24, 10.84, 11.33, 11.71, +/* 480 */ 11.98, 12.17, 12.28, 12.32, 12.35, 12.44, 12.55, 12.68, 12.77, 12.72, +/* 530 */ 12.60, 12.43, 12.22, 28.96, 16.51, 11.79, 11.76, 11.77, 11.84, 14.61, +/* 580 */ 16.11, 12.34, 12.53, 12.72, 12.92, 13.12, 13.34, 13.61, 13.87, 14.07, +/* 630 */ 14.20, 14.16, 14.13, 14.34, 14.50, 14.46, 14.00, 12.58, 10.99, 9.98, +/* 680 */ 9.22, 8.62, 8.07, 7.39, 6.71, 6.16, 5.63, 5.03, 4.46, 4.02, +/* 730 */ 3.66, 3.36, 3.09, 2.85, 2.65, 2.51, 2.37, 2.15, 1.89, 1.61, +/* 780 */ 1.32, +/* 785 */ 0.0, 0.0, 0.0, 0.0, 0.0, +/* 810 */ 0.0, 0.0, 0.0, 0.0, 0.0 + } +}; + + + +/* CIE F10 */ +/* Fluorescent, Narrow band 5000K, CRI 81 */ +static xspect il_F10 = { + 107, 300.0, 830.0, /* 109 bands from 300 to 830 nm in 5nm steps */ + 20.0, /* Arbitrary scale factor */ + { + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 1.11, 0.80, 0.62, 0.57, 1.48, 12.16, 2.12, 2.70, 3.74, 5.14, + 6.75, 34.39, 14.86, 10.40, 10.76, 10.67, 10.11, 9.27, 8.29, 7.29, + 7.91, 16.64, 16.73, 10.44, 5.94, 3.34, 2.35, 1.88, 1.59, 1.47, + 1.80, 5.71, 40.98, 73.69, 33.61, 8.24, 3.38, 2.47, 2.14, 4.86, + 11.45, 14.79, 12.16, 8.97, 6.53, 8.31, 44.12, 34.55, 12.09, 12.15, + 10.52, 4.43, 1.95, 2.19, 3.19, 2.77, 2.29, 2.00, 1.52, 1.35, + 1.47, 1.79, 1.74, 1.02, 1.14, 3.32, 4.49, 2.05, 0.49, 0.24, + 0.21, 0.21, 0.24, 0.24, 0.21, 0.17, 0.21, 0.22, 0.17, 0.12, + 0.09, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0 + } +}; + +/* Spectrocam Xenon Lamp */ +static xspect il_Spectrocam = { + 95, 325.0, 795.0, /* 95 bands from 325 to 795 nm in 5nm steps */ + 1.0, /* Arbitrary scale factor */ + { + 0.220794, 0.240550, 0.281212, 0.363042, 0.493282, + 0.582279, 0.657489, 0.715563, 0.797559, 0.916343, + 1.066625, 1.228461, 1.298467, 1.373143, 1.457366, + 1.496117, 1.509290, 1.573544, 1.596359, 1.495740, + 1.477898, 1.521371, 1.479780, 1.453196, 1.532119, + 1.548128, 1.503433, 1.428481, 1.357290, 1.354425, + 1.317263, 1.237048, 1.169737, 1.109248, 1.085784, + 1.080186, 1.104001, 1.131713, 1.161153, 1.158589, + 1.148998, 1.123934, 1.077395, 1.017907, 1.026532, + 1.045921, 1.083780, 1.081868, 1.048489, 1.021549, + 0.993572, 0.956559, 0.942657, 0.952544, 0.957087, + 0.958472, 0.945666, 0.923988, 0.890418, 0.852044, + 0.812935, 0.792055, 0.791141, 0.825459, 0.829230, + 0.818171, 0.851752, 0.913113, 1.038844, 1.116913, + 1.164211, 1.133376, 1.109062, 1.129427, 1.086885, + 0.991213, 0.924226, 0.875499, 0.894231, 0.922219, + 0.960372, 0.896142, 0.819477, 0.879305, 0.912777, + 0.908489, 0.775942, 0.598118, 0.532988, 0.484102, + 0.465986, 0.414848, 0.346473, 0.324622, 0.309978 + } +}; + +#endif /* !SALONEINSTLIB */ + +/* Apply ISO 13655:2009 UV filter to the given spectrum. */ +/* The filter is applied point by point. */ +static void uv_filter(xspect *dst, xspect *src) { + int i; + + XSPECT_COPY_INFO(dst, src); + for (i = 0; i < src->spec_n; i++) { + double wl = XSPECT_XWL(src, i); + double ff = 1.0; + + if (wl <= 395.0) { + ff = 0.0; + } else if (wl < 425.0) { + ff = (wl - 395.0)/(425.0 - 395.0); + ff = ff * ff * (3.0 - 2.0 * ff); /* Cubic spline */ + } + dst->spec[i] = ff * src->spec[i]; + } +} + +/* 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 */ +) { + switch (ilType) { + case icxIT_none: + return 1; + case icxIT_custom: + return 1; + case icxIT_A: + *sp = il_A; /* Struct copy */ + return 0; + case icxIT_C: + *sp = il_C; /* " */ + return 0; + case icxIT_default: + case icxIT_D50: + *sp = il_D50; /* etc */ + return 0; + case icxIT_D50M2: + if (il_D50M2.spec_n == 0) + uv_filter(&il_D50M2, &il_D50); + *sp = il_D50M2; + return 0; + case icxIT_D65: + *sp = il_D65; + return 0; + case icxIT_E: + *sp = il_none; + return 0; +#ifndef SALONEINSTLIB + case icxIT_F5: + *sp = il_F5; + return 0; + case icxIT_F8: + *sp = il_F8; + return 0; + case icxIT_F10: + *sp = il_F10; + return 0; + case icxIT_Spectrocam: + *sp = il_Spectrocam; + return 0; + case icxIT_Dtemp: + return daylight_il(sp, temp); + case icxIT_Ptemp: + return planckian_il(sp, temp); +#endif + } + return 1; +} + +/* ------------- */ + +/* Spectral locus poligon cache */ +typedef struct { + int n; /* Number of spectral vertexes, 0 if uninit */ + double xmin, xmax, ymin, ymax; /* Boundint box */ + double tx[3], ty[3]; /* Fast inner triangle test, RGB */ + double be[3][3]; /* baricentric equations of triangle */ +// double eed[3]; /* Distance of triangle points to 0.3, 0.3 */ + double x[XSPECT_MAX_BANDS]; /* x value of vertex */ + double y[XSPECT_MAX_BANDS]; /* y value of vertex */ +} xslpoly; + +/* ------------- */ +/* Observer Data and locus poligon cache */ + +/* Standard CIE 1931 2 degree */ +static xspect ob_CIE_1931_2[3] = { + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000129900000, 0.000145847000, 0.000163802100, 0.000184003700, 0.000206690200, + 0.000232100000, 0.000260728000, 0.000293075000, 0.000329388000, 0.000369914000, + 0.000414900000, 0.000464158700, 0.000518986000, 0.000581854000, 0.000655234700, + 0.000741600000, 0.000845029600, 0.000964526800, 0.001094949000, 0.001231154000, + 0.001368000000, 0.001502050000, 0.001642328000, 0.001802382000, 0.001995757000, + 0.002236000000, 0.002535385000, 0.002892603000, 0.003300829000, 0.003753236000, + 0.004243000000, 0.004762389000, 0.005330048000, 0.005978712000, 0.006741117000, + 0.007650000000, 0.008751373000, 0.010028880000, 0.011421700000, 0.012869010000, + 0.014310000000, 0.015704430000, 0.017147440000, 0.018781220000, 0.020748010000, + 0.023190000000, 0.026207360000, 0.029782480000, 0.033880920000, 0.038468240000, + 0.043510000000, 0.048995600000, 0.055022600000, 0.061718800000, 0.069212000000, + 0.077630000000, 0.086958110000, 0.097176720000, 0.108406300000, 0.120767200000, + 0.134380000000, 0.149358200000, 0.165395700000, 0.181983100000, 0.198611000000, + 0.214770000000, 0.230186800000, 0.244879700000, 0.258777300000, 0.271807900000, + 0.283900000000, 0.294943800000, 0.304896500000, 0.313787300000, 0.321645400000, + 0.328500000000, 0.334351300000, 0.339210100000, 0.343121300000, 0.346129600000, + 0.348280000000, 0.349599900000, 0.350147400000, 0.350013000000, 0.349287000000, + 0.348060000000, 0.346373300000, 0.344262400000, 0.341808800000, 0.339094100000, + 0.336200000000, 0.333197700000, 0.330041100000, 0.326635700000, 0.322886800000, + 0.318700000000, 0.314025100000, 0.308884000000, 0.303290400000, 0.297257900000, + 0.290800000000, 0.283970100000, 0.276721400000, 0.268917800000, 0.260422700000, + 0.251100000000, 0.240847500000, 0.229851200000, 0.218407200000, 0.206811500000, + 0.195360000000, 0.184213600000, 0.173327300000, 0.162688100000, 0.152283300000, + 0.142100000000, 0.132178600000, 0.122569600000, 0.113275200000, 0.104297900000, + 0.095640000000, 0.087299550000, 0.079308040000, 0.071717760000, 0.064580990000, + 0.057950010000, 0.051862110000, 0.046281520000, 0.041150880000, 0.036412830000, + 0.032010000000, 0.027917200000, 0.024144400000, 0.020687000000, 0.017540400000, + 0.014700000000, 0.012161790000, 0.009919960000, 0.007967240000, 0.006296346000, + 0.004900000000, 0.003777173000, 0.002945320000, 0.002424880000, 0.002236293000, + 0.002400000000, 0.002925520000, 0.003836560000, 0.005174840000, 0.006982080000, + 0.009300000000, 0.012149490000, 0.015535880000, 0.019477520000, 0.023992770000, + 0.029100000000, 0.034814850000, 0.041120160000, 0.047985040000, 0.055378610000, + 0.063270000000, 0.071635010000, 0.080462240000, 0.089739960000, 0.099456450000, + 0.109600000000, 0.120167400000, 0.131114500000, 0.142367900000, 0.153854200000, + 0.165500000000, 0.177257100000, 0.189140000000, 0.201169400000, 0.213365800000, + 0.225749900000, 0.238320900000, 0.251066800000, 0.263992200000, 0.277101700000, + 0.290400000000, 0.303891200000, 0.317572600000, 0.331438400000, 0.345482800000, + 0.359700000000, 0.374083900000, 0.388639600000, 0.403378400000, 0.418311500000, + 0.433449900000, 0.448795300000, 0.464336000000, 0.480064000000, 0.495971300000, + 0.512050100000, 0.528295900000, 0.544691600000, 0.561209400000, 0.577821500000, + 0.594500000000, 0.611220900000, 0.627975800000, 0.644760200000, 0.661569700000, + 0.678400000000, 0.695239200000, 0.712058600000, 0.728828400000, 0.745518800000, + 0.762100000000, 0.778543200000, 0.794825600000, 0.810926400000, 0.826824800000, + 0.842500000000, 0.857932500000, 0.873081600000, 0.887894400000, 0.902318100000, + 0.916300000000, 0.929799500000, 0.942798400000, 0.955277600000, 0.967217900000, + 0.978600000000, 0.989385600000, 0.999548800000, 1.009089200000, 1.018006400000, + 1.026300000000, 1.033982700000, 1.040986000000, 1.047188000000, 1.052466700000, + 1.056700000000, 1.059794400000, 1.061799200000, 1.062806800000, 1.062909600000, + 1.062200000000, 1.060735200000, 1.058443600000, 1.055224400000, 1.050976800000, + 1.045600000000, 1.039036900000, 1.031360800000, 1.022666200000, 1.013047700000, + 1.002600000000, 0.991367500000, 0.979331400000, 0.966491600000, 0.952847900000, + 0.938400000000, 0.923194000000, 0.907244000000, 0.890502000000, 0.872920000000, + 0.854449900000, 0.835084000000, 0.814946000000, 0.794186000000, 0.772954000000, + 0.751400000000, 0.729583600000, 0.707588800000, 0.685602200000, 0.663810400000, + 0.642400000000, 0.621514900000, 0.601113800000, 0.581105200000, 0.561397700000, + 0.541900000000, 0.522599500000, 0.503546400000, 0.484743600000, 0.466193900000, + 0.447900000000, 0.429861300000, 0.412098000000, 0.394644000000, 0.377533300000, + 0.360800000000, 0.344456300000, 0.328516800000, 0.313019200000, 0.298001100000, + 0.283500000000, 0.269544800000, 0.256118400000, 0.243189600000, 0.230727200000, + 0.218700000000, 0.207097100000, 0.195923200000, 0.185170800000, 0.174832300000, + 0.164900000000, 0.155366700000, 0.146230000000, 0.137490000000, 0.129146700000, + 0.121200000000, 0.113639700000, 0.106465000000, 0.099690440000, 0.093330610000, + 0.087400000000, 0.081900960000, 0.076804280000, 0.072077120000, 0.067686640000, + 0.063600000000, 0.059806850000, 0.056282160000, 0.052971040000, 0.049818610000, + 0.046770000000, 0.043784050000, 0.040875360000, 0.038072640000, 0.035404610000, + 0.032900000000, 0.030564190000, 0.028380560000, 0.026344840000, 0.024452750000, + 0.022700000000, 0.021084290000, 0.019599880000, 0.018237320000, 0.016987170000, + 0.015840000000, 0.014790640000, 0.013831320000, 0.012948680000, 0.012129200000, + 0.011359160000, 0.010629350000, 0.009938846000, 0.009288422000, 0.008678854000, + 0.008110916000, 0.007582388000, 0.007088746000, 0.006627313000, 0.006195408000, + 0.005790346000, 0.005409826000, 0.005052583000, 0.004717512000, 0.004403507000, + 0.004109457000, 0.003833913000, 0.003575748000, 0.003334342000, 0.003109075000, + 0.002899327000, 0.002704348000, 0.002523020000, 0.002354168000, 0.002196616000, + 0.002049190000, 0.001910960000, 0.001781438000, 0.001660110000, 0.001546459000, + 0.001439971000, 0.001340042000, 0.001246275000, 0.001158471000, 0.001076430000, + 0.000999949300, 0.000928735800, 0.000862433200, 0.000800750300, 0.000743396000, + 0.000690078600, 0.000640515600, 0.000594502100, 0.000551864600, 0.000512429000, + 0.000476021300, 0.000442453600, 0.000411511700, 0.000382981400, 0.000356649100, + 0.000332301100, 0.000309758600, 0.000288887100, 0.000269539400, 0.000251568200, + 0.000234826100, 0.000219171000, 0.000204525800, 0.000190840500, 0.000178065400, + 0.000166150500, 0.000155023600, 0.000144621900, 0.000134909800, 0.000125852000, + 0.000117413000, 0.000109551500, 0.000102224500, 0.000095394450, 0.000089023900, + 0.000083075270, 0.000077512690, 0.000072313040, 0.000067457780, 0.000062928440, + 0.000058706520, 0.000054770280, 0.000051099180, 0.000047676540, 0.000044485670, + 0.000041509940, 0.000038733240, 0.000036142030, 0.000033723520, 0.000031464870, + 0.000029353260, 0.000027375730, 0.000025524330, 0.000023793760, 0.000022178700, + 0.000020673830, 0.000019272260, 0.000017966400, 0.000016749910, 0.000015616480, + 0.000014559770, 0.000013573870, 0.000012654360, 0.000011797230, 0.000010998440, + 0.000010253980, 0.000009559646, 0.000008912044, 0.000008308358, 0.000007745769, + 0.000007221456, 0.000006732475, 0.000006276423, 0.000005851304, 0.000005455118, + 0.000005085868, 0.000004741466, 0.000004420236, 0.000004120783, 0.000003841716, + 0.000003581652, 0.000003339127, 0.000003112949, 0.000002902121, 0.000002705645, + 0.000002522525, 0.000002351726, 0.000002192415, 0.000002043902, 0.000001905497, + 0.000001776509, 0.000001656215, 0.000001544022, 0.000001439440, 0.000001341977, + 0.000001251141 + } + }, + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000003917000, 0.000004393581, 0.000004929604, 0.000005532136, 0.000006208245, + 0.000006965000, 0.000007813219, 0.000008767336, 0.000009839844, 0.000011043230, + 0.000012390000, 0.000013886410, 0.000015557280, 0.000017442960, 0.000019583750, + 0.000022020000, 0.000024839650, 0.000028041260, 0.000031531040, 0.000035215210, + 0.000039000000, 0.000042826400, 0.000046914600, 0.000051589600, 0.000057176400, + 0.000064000000, 0.000072344210, 0.000082212240, 0.000093508160, 0.000106136100, + 0.000120000000, 0.000134984000, 0.000151492000, 0.000170208000, 0.000191816000, + 0.000217000000, 0.000246906700, 0.000281240000, 0.000318520000, 0.000357266700, + 0.000396000000, 0.000433714700, 0.000473024000, 0.000517876000, 0.000572218700, + 0.000640000000, 0.000724560000, 0.000825500000, 0.000941160000, 0.001069880000, + 0.001210000000, 0.001362091000, 0.001530752000, 0.001720368000, 0.001935323000, + 0.002180000000, 0.002454800000, 0.002764000000, 0.003117800000, 0.003526400000, + 0.004000000000, 0.004546240000, 0.005159320000, 0.005829280000, 0.006546160000, + 0.007300000000, 0.008086507000, 0.008908720000, 0.009767680000, 0.010664430000, + 0.011600000000, 0.012573170000, 0.013582720000, 0.014629680000, 0.015715090000, + 0.016840000000, 0.018007360000, 0.019214480000, 0.020453920000, 0.021718240000, + 0.023000000000, 0.024294610000, 0.025610240000, 0.026958570000, 0.028351250000, + 0.029800000000, 0.031310830000, 0.032883680000, 0.034521120000, 0.036225710000, + 0.038000000000, 0.039846670000, 0.041768000000, 0.043766000000, 0.045842670000, + 0.048000000000, 0.050243680000, 0.052573040000, 0.054980560000, 0.057458720000, + 0.060000000000, 0.062601970000, 0.065277520000, 0.068042080000, 0.070911090000, + 0.073900000000, 0.077016000000, 0.080266400000, 0.083666800000, 0.087232800000, + 0.090980000000, 0.094917550000, 0.099045840000, 0.103367400000, 0.107884600000, + 0.112600000000, 0.117532000000, 0.122674400000, 0.127992800000, 0.133452800000, + 0.139020000000, 0.144676400000, 0.150469300000, 0.156461900000, 0.162717700000, + 0.169300000000, 0.176243100000, 0.183558100000, 0.191273500000, 0.199418000000, + 0.208020000000, 0.217119900000, 0.226734500000, 0.236857100000, 0.247481200000, + 0.258600000000, 0.270184900000, 0.282293900000, 0.295050500000, 0.308578000000, + 0.323000000000, 0.338402100000, 0.354685800000, 0.371698600000, 0.389287500000, + 0.407300000000, 0.425629900000, 0.444309600000, 0.463394400000, 0.482939500000, + 0.503000000000, 0.523569300000, 0.544512000000, 0.565690000000, 0.586965300000, + 0.608200000000, 0.629345600000, 0.650306800000, 0.670875200000, 0.690842400000, + 0.710000000000, 0.728185200000, 0.745463600000, 0.761969400000, 0.777836800000, + 0.793200000000, 0.808110400000, 0.822496200000, 0.836306800000, 0.849491600000, + 0.862000000000, 0.873810800000, 0.884962400000, 0.895493600000, 0.905443200000, + 0.914850100000, 0.923734800000, 0.932092400000, 0.939922600000, 0.947225200000, + 0.954000000000, 0.960256100000, 0.966007400000, 0.971260600000, 0.976022500000, + 0.980300000000, 0.984092400000, 0.987418200000, 0.990312800000, 0.992811600000, + 0.994950100000, 0.996710800000, 0.998098300000, 0.999112000000, 0.999748200000, + 1.000000000000, 0.999856700000, 0.999304600000, 0.998325500000, 0.996898700000, + 0.995000000000, 0.992600500000, 0.989742600000, 0.986444400000, 0.982724100000, + 0.978600000000, 0.974083700000, 0.969171200000, 0.963856800000, 0.958134900000, + 0.952000000000, 0.945450400000, 0.938499200000, 0.931162800000, 0.923457600000, + 0.915400000000, 0.907006400000, 0.898277200000, 0.889204800000, 0.879781600000, + 0.870000000000, 0.859861300000, 0.849392000000, 0.838622000000, 0.827581300000, + 0.816300000000, 0.804794700000, 0.793082000000, 0.781192000000, 0.769154700000, + 0.757000000000, 0.744754100000, 0.732422400000, 0.720003600000, 0.707496500000, + 0.694900000000, 0.682219200000, 0.669471600000, 0.656674400000, 0.643844800000, + 0.631000000000, 0.618155500000, 0.605314400000, 0.592475600000, 0.579637900000, + 0.566800000000, 0.553961100000, 0.541137200000, 0.528352800000, 0.515632300000, + 0.503000000000, 0.490468800000, 0.478030400000, 0.465677600000, 0.453403200000, + 0.441200000000, 0.429080000000, 0.417036000000, 0.405032000000, 0.393032000000, + 0.381000000000, 0.368918400000, 0.356827200000, 0.344776800000, 0.332817600000, + 0.321000000000, 0.309338100000, 0.297850400000, 0.286593600000, 0.275624500000, + 0.265000000000, 0.254763200000, 0.244889600000, 0.235334400000, 0.226052800000, + 0.217000000000, 0.208161600000, 0.199548800000, 0.191155200000, 0.182974400000, + 0.175000000000, 0.167223500000, 0.159646400000, 0.152277600000, 0.145125900000, + 0.138200000000, 0.131500300000, 0.125024800000, 0.118779200000, 0.112769100000, + 0.107000000000, 0.101476200000, 0.096188640000, 0.091122960000, 0.086264850000, + 0.081600000000, 0.077120640000, 0.072825520000, 0.068710080000, 0.064769760000, + 0.061000000000, 0.057396210000, 0.053955040000, 0.050673760000, 0.047549650000, + 0.044580000000, 0.041758720000, 0.039084960000, 0.036563840000, 0.034200480000, + 0.032000000000, 0.029962610000, 0.028076640000, 0.026329360000, 0.024708050000, + 0.023200000000, 0.021800770000, 0.020501120000, 0.019281080000, 0.018120690000, + 0.017000000000, 0.015903790000, 0.014837180000, 0.013810680000, 0.012834780000, + 0.011920000000, 0.011068310000, 0.010273390000, 0.009533311000, 0.008846157000, + 0.008210000000, 0.007623781000, 0.007085424000, 0.006591476000, 0.006138485000, + 0.005723000000, 0.005343059000, 0.004995796000, 0.004676404000, 0.004380075000, + 0.004102000000, 0.003838453000, 0.003589099000, 0.003354219000, 0.003134093000, + 0.002929000000, 0.002738139000, 0.002559876000, 0.002393244000, 0.002237275000, + 0.002091000000, 0.001953587000, 0.001824580000, 0.001703580000, 0.001590187000, + 0.001484000000, 0.001384496000, 0.001291268000, 0.001204092000, 0.001122744000, + 0.001047000000, 0.000976589600, 0.000911108800, 0.000850133200, 0.000793238400, + 0.000740000000, 0.000690082700, 0.000643310000, 0.000599496000, 0.000558454700, + 0.000520000000, 0.000483913600, 0.000450052800, 0.000418345200, 0.000388718400, + 0.000361100000, 0.000335383500, 0.000311440400, 0.000289165600, 0.000268453900, + 0.000249200000, 0.000231301900, 0.000214685600, 0.000199288400, 0.000185047500, + 0.000171900000, 0.000159778100, 0.000148604400, 0.000138301600, 0.000128792500, + 0.000120000000, 0.000111859500, 0.000104322400, 0.000097335600, 0.000090845870, + 0.000084800000, 0.000079146670, 0.000073858000, 0.000068916000, 0.000064302670, + 0.000060000000, 0.000055981870, 0.000052225600, 0.000048718400, 0.000045447470, + 0.000042400000, 0.000039561040, 0.000036915120, 0.000034448680, 0.000032148160, + 0.000030000000, 0.000027991250, 0.000026113560, 0.000024360240, 0.000022724610, + 0.000021200000, 0.000019778550, 0.000018452850, 0.000017216870, 0.000016064590, + 0.000014990000, 0.000013987280, 0.000013051550, 0.000012178180, 0.000011362540, + 0.000010600000, 0.000009885877, 0.000009217304, 0.000008592362, 0.000008009133, + 0.000007465700, 0.000006959567, 0.000006487995, 0.000006048699, 0.000005639396, + 0.000005257800, 0.000004901771, 0.000004569720, 0.000004260194, 0.000003971739, + 0.000003702900, 0.000003452163, 0.000003218302, 0.000003000300, 0.000002797139, + 0.000002607800, 0.000002431220, 0.000002266531, 0.000002113013, 0.000001969943, + 0.000001836600, 0.000001712230, 0.000001596228, 0.000001488090, 0.000001387314, + 0.000001293400, 0.000001205820, 0.000001124143, 0.000001048009, 0.000000977058, + 0.000000910930, 0.000000849251, 0.000000791721, 0.000000738090, 0.000000688110, + 0.000000641530, 0.000000598090, 0.000000557575, 0.000000519808, 0.000000484612, + 0.000000451810 + } + }, + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000606100000, 0.000680879200, 0.000765145600, 0.000860012400, 0.000966592800, + 0.001086000000, 0.001220586000, 0.001372729000, 0.001543579000, 0.001734286000, + 0.001946000000, 0.002177777000, 0.002435809000, 0.002731953000, 0.003078064000, + 0.003486000000, 0.003975227000, 0.004540880000, 0.005158320000, 0.005802907000, + 0.006450001000, 0.007083216000, 0.007745488000, 0.008501152000, 0.009414544000, + 0.010549990000, 0.011965800000, 0.013655870000, 0.015588050000, 0.017730150000, + 0.020050010000, 0.022511360000, 0.025202880000, 0.028279720000, 0.031897040000, + 0.036210000000, 0.041437710000, 0.047503720000, 0.054119880000, 0.060998030000, + 0.067850010000, 0.074486320000, 0.081361560000, 0.089153640000, 0.098540480000, + 0.110200000000, 0.124613300000, 0.141701700000, 0.161303500000, 0.183256800000, + 0.207400000000, 0.233692100000, 0.262611400000, 0.294774600000, 0.330798500000, + 0.371300000000, 0.416209100000, 0.465464200000, 0.519694800000, 0.579530300000, + 0.645600000000, 0.718483800000, 0.796713300000, 0.877845900000, 0.959439000000, + 1.039050100000, 1.115367300000, 1.188497100000, 1.258123300000, 1.323929600000, + 1.385600000000, 1.442635200000, 1.494803500000, 1.542190300000, 1.584880700000, + 1.622960000000, 1.656404800000, 1.685295900000, 1.709874500000, 1.730382100000, + 1.747060000000, 1.760044600000, 1.769623300000, 1.776263700000, 1.780433400000, + 1.782600000000, 1.782968200000, 1.781699800000, 1.779198200000, 1.775867100000, + 1.772110000000, 1.768258900000, 1.764039000000, 1.758943800000, 1.752466300000, + 1.744100000000, 1.733559500000, 1.720858100000, 1.705936900000, 1.688737200000, + 1.669200000000, 1.647528700000, 1.623412700000, 1.596022300000, 1.564528000000, + 1.528100000000, 1.486111400000, 1.439521500000, 1.389879900000, 1.338736200000, + 1.287640000000, 1.237422300000, 1.187824300000, 1.138761100000, 1.090148000000, + 1.041900000000, 0.994197600000, 0.947347300000, 0.901453100000, 0.856619300000, + 0.812950100000, 0.770517300000, 0.729444800000, 0.689913600000, 0.652104900000, + 0.616200000000, 0.582328600000, 0.550416200000, 0.520337600000, 0.491967300000, + 0.465180000000, 0.439924600000, 0.416183600000, 0.393882200000, 0.372945900000, + 0.353300000000, 0.334857800000, 0.317552100000, 0.301337500000, 0.286168600000, + 0.272000000000, 0.258817100000, 0.246483800000, 0.234771800000, 0.223453300000, + 0.212300000000, 0.201169200000, 0.190119600000, 0.179225400000, 0.168560800000, + 0.158200000000, 0.148138300000, 0.138375800000, 0.128994200000, 0.120075100000, + 0.111700000000, 0.103904800000, 0.096667480000, 0.089982720000, 0.083845310000, + 0.078249990000, 0.073208990000, 0.068678160000, 0.064567840000, 0.060788350000, + 0.057250010000, 0.053904350000, 0.050746640000, 0.047752760000, 0.044898590000, + 0.042160000000, 0.039507280000, 0.036935640000, 0.034458360000, 0.032088720000, + 0.029840000000, 0.027711810000, 0.025694440000, 0.023787160000, 0.021989250000, + 0.020300000000, 0.018718050000, 0.017240360000, 0.015863640000, 0.014584610000, + 0.013400000000, 0.012307230000, 0.011301880000, 0.010377920000, 0.009529306000, + 0.008749999000, 0.008035200000, 0.007381600000, 0.006785400000, 0.006242800000, + 0.005749999000, 0.005303600000, 0.004899800000, 0.004534200000, 0.004202400000, + 0.003900000000, 0.003623200000, 0.003370600000, 0.003141400000, 0.002934800000, + 0.002749999000, 0.002585200000, 0.002438600000, 0.002309400000, 0.002196800000, + 0.002100000000, 0.002017733000, 0.001948200000, 0.001889800000, 0.001840933000, + 0.001800000000, 0.001766267000, 0.001737800000, 0.001711200000, 0.001683067000, + 0.001650001000, 0.001610133000, 0.001564400000, 0.001513600000, 0.001458533000, + 0.001400000000, 0.001336667000, 0.001270000000, 0.001205000000, 0.001146667000, + 0.001100000000, 0.001068800000, 0.001049400000, 0.001035600000, 0.001021200000, + 0.001000000000, 0.000968640000, 0.000929920000, 0.000886880000, 0.000842560000, + 0.000800000000, 0.000760960000, 0.000723680000, 0.000685920000, 0.000645440000, + 0.000600000000, 0.000547866700, 0.000491600000, 0.000435400000, 0.000383466700, + 0.000340000000, 0.000307253300, 0.000283160000, 0.000265440000, 0.000251813300, + 0.000240000000, 0.000229546700, 0.000220640000, 0.000211960000, 0.000202186700, + 0.000190000000, 0.000174213300, 0.000155640000, 0.000135960000, 0.000116853300, + 0.000100000000, 0.000086133330, 0.000074600000, 0.000065000000, 0.000056933330, + 0.000049999990, 0.000044160000, 0.000039480000, 0.000035720000, 0.000032640000, + 0.000030000000, 0.000027653330, 0.000025560000, 0.000023640000, 0.000021813330, + 0.000020000000, 0.000018133330, 0.000016200000, 0.000014200000, 0.000012133330, + 0.000010000000, 0.000007733333, 0.000005400000, 0.000003200000, 0.000001333333, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000 + } + } +}; + +static xslpoly poly_CIE_1931_2 = { 0 }; + +/* Standard CIE 1964 10 degree */ +static xspect ob_CIE_1964_10[3] = { + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000000122200, 0.000000185138, 0.000000278830, 0.000000417470, 0.000000621330, + 0.000000919270, 0.000001351980, 0.000001976540, 0.000002872500, 0.000004149500, + 0.000005958600, 0.000008505600, 0.000012068600, 0.000017022600, 0.000023868000, + 0.000033266000, 0.000046087000, 0.000063472000, 0.000086892000, 0.000118246000, + 0.000159952000, 0.000215080000, 0.000287490000, 0.000381990000, 0.000504550000, + 0.000662440000, 0.000864500000, 0.001121500000, 0.001446160000, 0.001853590000, + 0.002361600000, 0.002990600000, 0.003764500000, 0.004710200000, 0.005858100000, + 0.007242300000, 0.008899600000, 0.010870900000, 0.013198900000, 0.015929200000, + 0.019109700000, 0.022788000000, 0.027011000000, 0.031829000000, 0.037278000000, + 0.043400000000, 0.050223000000, 0.057764000000, 0.066038000000, 0.075033000000, + 0.084736000000, 0.095041000000, 0.105836000000, 0.117066000000, 0.128682000000, + 0.140638000000, 0.152893000000, 0.165416000000, 0.178191000000, 0.191214000000, + 0.204492000000, 0.217650000000, 0.230267000000, 0.242311000000, 0.253793000000, + 0.264737000000, 0.275195000000, 0.285301000000, 0.295143000000, 0.304869000000, + 0.314679000000, 0.324355000000, 0.333570000000, 0.342243000000, 0.350312000000, + 0.357719000000, 0.364482000000, 0.370493000000, 0.375727000000, 0.380158000000, + 0.383734000000, 0.386327000000, 0.387858000000, 0.388396000000, 0.387978000000, + 0.386726000000, 0.384696000000, 0.382006000000, 0.378709000000, 0.374915000000, + 0.370702000000, 0.366089000000, 0.361045000000, 0.355518000000, 0.349486000000, + 0.342957000000, 0.335893000000, 0.328284000000, 0.320150000000, 0.311475000000, + 0.302273000000, 0.292858000000, 0.283502000000, 0.274044000000, 0.264263000000, + 0.254085000000, 0.243392000000, 0.232187000000, 0.220488000000, 0.208198000000, + 0.195618000000, 0.183034000000, 0.170222000000, 0.157348000000, 0.144650000000, + 0.132349000000, 0.120584000000, 0.109456000000, 0.099042000000, 0.089388000000, + 0.080507000000, 0.072034000000, 0.063710000000, 0.055694000000, 0.048117000000, + 0.041072000000, 0.034642000000, 0.028896000000, 0.023876000000, 0.019628000000, + 0.016172000000, 0.013300000000, 0.010759000000, 0.008542000000, 0.006661000000, + 0.005132000000, 0.003982000000, 0.003239000000, 0.002934000000, 0.003114000000, + 0.003816000000, 0.005095000000, 0.006936000000, 0.009299000000, 0.012147000000, + 0.015444000000, 0.019156000000, 0.023250000000, 0.027690000000, 0.032444000000, + 0.037465000000, 0.042956000000, 0.049114000000, 0.055920000000, 0.063349000000, + 0.071358000000, 0.079901000000, 0.088909000000, 0.098293000000, 0.107949000000, + 0.117749000000, 0.127839000000, 0.138450000000, 0.149516000000, 0.161041000000, + 0.172953000000, 0.185209000000, 0.197755000000, 0.210538000000, 0.223460000000, + 0.236491000000, 0.249633000000, 0.262972000000, 0.276515000000, 0.290269000000, + 0.304213000000, 0.318361000000, 0.332705000000, 0.347232000000, 0.361926000000, + 0.376772000000, 0.391683000000, 0.406594000000, 0.421539000000, 0.436517000000, + 0.451584000000, 0.466782000000, 0.482147000000, 0.497738000000, 0.513606000000, + 0.529826000000, 0.546440000000, 0.563426000000, 0.580726000000, 0.598290000000, + 0.616053000000, 0.633948000000, 0.651901000000, 0.669824000000, 0.687632000000, + 0.705224000000, 0.722773000000, 0.740483000000, 0.758273000000, 0.776083000000, + 0.793832000000, 0.811436000000, 0.828822000000, 0.845879000000, 0.862525000000, + 0.878655000000, 0.894208000000, 0.909206000000, 0.923672000000, 0.937638000000, + 0.951162000000, 0.964283000000, 0.977068000000, 0.989590000000, 1.001910000000, + 1.014160000000, 1.026500000000, 1.038800000000, 1.051000000000, 1.062900000000, + 1.074300000000, 1.085200000000, 1.095200000000, 1.104200000000, 1.112000000000, + 1.118520000000, 1.123800000000, 1.128000000000, 1.131100000000, 1.133200000000, + 1.134300000000, 1.134300000000, 1.133300000000, 1.131200000000, 1.128100000000, + 1.123990000000, 1.118900000000, 1.112900000000, 1.105900000000, 1.098000000000, + 1.089100000000, 1.079200000000, 1.068400000000, 1.056700000000, 1.044000000000, + 1.030480000000, 1.016000000000, 1.000800000000, 0.984790000000, 0.968080000000, + 0.950740000000, 0.932800000000, 0.914340000000, 0.895390000000, 0.876030000000, + 0.856297000000, 0.836350000000, 0.816290000000, 0.796050000000, 0.775610000000, + 0.754930000000, 0.733990000000, 0.712780000000, 0.691290000000, 0.669520000000, + 0.647467000000, 0.625110000000, 0.602520000000, 0.579890000000, 0.557370000000, + 0.535110000000, 0.513240000000, 0.491860000000, 0.471080000000, 0.450960000000, + 0.431567000000, 0.412870000000, 0.394750000000, 0.377210000000, 0.360190000000, + 0.343690000000, 0.327690000000, 0.312170000000, 0.297110000000, 0.282500000000, + 0.268329000000, 0.254590000000, 0.241300000000, 0.228480000000, 0.216140000000, + 0.204300000000, 0.192950000000, 0.182110000000, 0.171770000000, 0.161920000000, + 0.152568000000, 0.143670000000, 0.135200000000, 0.127130000000, 0.119480000000, + 0.112210000000, 0.105310000000, 0.098786000000, 0.092610000000, 0.086773000000, + 0.081260600000, 0.076048000000, 0.071114000000, 0.066454000000, 0.062062000000, + 0.057930000000, 0.054050000000, 0.050412000000, 0.047006000000, 0.043823000000, + 0.040850800000, 0.038072000000, 0.035468000000, 0.033031000000, 0.030753000000, + 0.028623000000, 0.026635000000, 0.024781000000, 0.023052000000, 0.021441000000, + 0.019941300000, 0.018544000000, 0.017241000000, 0.016027000000, 0.014896000000, + 0.013842000000, 0.012862000000, 0.011949000000, 0.011100000000, 0.010311000000, + 0.009576880000, 0.008894000000, 0.008258100000, 0.007666400000, 0.007116300000, + 0.006605200000, 0.006130600000, 0.005690300000, 0.005281900000, 0.004903300000, + 0.004552630000, 0.004227500000, 0.003925800000, 0.003645700000, 0.003385900000, + 0.003144700000, 0.002920800000, 0.002713000000, 0.002520200000, 0.002341100000, + 0.002174960000, 0.002020600000, 0.001877300000, 0.001744100000, 0.001620500000, + 0.001505700000, 0.001399200000, 0.001300400000, 0.001208700000, 0.001123600000, + 0.001044760000, 0.000971560000, 0.000903600000, 0.000840480000, 0.000781870000, + 0.000727450000, 0.000676900000, 0.000629960000, 0.000586370000, 0.000545870000, + 0.000508258000, 0.000473300000, 0.000440800000, 0.000410580000, 0.000382490000, + 0.000356380000, 0.000332110000, 0.000309550000, 0.000288580000, 0.000269090000, + 0.000250969000, 0.000234130000, 0.000218470000, 0.000203910000, 0.000190350000, + 0.000177730000, 0.000165970000, 0.000155020000, 0.000144800000, 0.000135280000, + 0.000126390000, 0.000118100000, 0.000110370000, 0.000103150000, 0.000096427000, + 0.000090151000, 0.000084294000, 0.000078830000, 0.000073729000, 0.000068969000, + 0.000064525800, 0.000060376000, 0.000056500000, 0.000052880000, 0.000049498000, + 0.000046339000, 0.000043389000, 0.000040634000, 0.000038060000, 0.000035657000, + 0.000033411700, 0.000031315000, 0.000029355000, 0.000027524000, 0.000025811000, + 0.000024209000, 0.000022711000, 0.000021308000, 0.000019994000, 0.000018764000, + 0.000017611500, 0.000016532000, 0.000015521000, 0.000014574000, 0.000013686000, + 0.000012855000, 0.000012075000, 0.000011345000, 0.000010659000, 0.000010017000, + 0.000009413630, 0.000008847900, 0.000008317100, 0.000007819000, 0.000007351600, + 0.000006913000, 0.000006501500, 0.000006115300, 0.000005752900, 0.000005412700, + 0.000005093470, 0.000004793800, 0.000004512500, 0.000004248300, 0.000004000200, + 0.000003767100, 0.000003548000, 0.000003342100, 0.000003148500, 0.000002966500, + 0.000002795310, 0.000002634500, 0.000002483400, 0.000002341400, 0.000002207800, + 0.000002082000, 0.000001963600, 0.000001851900, 0.000001746500, 0.000001647100, + 0.000001553140 + } + }, + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000000013398, 0.000000020294, 0.000000030560, 0.000000045740, 0.000000068050, + 0.000000100650, 0.000000147980, 0.000000216270, 0.000000314200, 0.000000453700, + 0.000000651100, 0.000000928800, 0.000001317500, 0.000001857200, 0.000002602000, + 0.000003625000, 0.000005019000, 0.000006907000, 0.000009449000, 0.000012848000, + 0.000017364000, 0.000023327000, 0.000031150000, 0.000041350000, 0.000054560000, + 0.000071560000, 0.000093300000, 0.000120870000, 0.000155640000, 0.000199200000, + 0.000253400000, 0.000320200000, 0.000402400000, 0.000502300000, 0.000623200000, + 0.000768500000, 0.000941700000, 0.001147800000, 0.001390300000, 0.001674000000, + 0.002004400000, 0.002386000000, 0.002822000000, 0.003319000000, 0.003880000000, + 0.004509000000, 0.005209000000, 0.005985000000, 0.006833000000, 0.007757000000, + 0.008756000000, 0.009816000000, 0.010918000000, 0.012058000000, 0.013237000000, + 0.014456000000, 0.015717000000, 0.017025000000, 0.018399000000, 0.019848000000, + 0.021391000000, 0.022992000000, 0.024598000000, 0.026213000000, 0.027841000000, + 0.029497000000, 0.031195000000, 0.032927000000, 0.034738000000, 0.036654000000, + 0.038676000000, 0.040792000000, 0.042946000000, 0.045114000000, 0.047333000000, + 0.049602000000, 0.051934000000, 0.054337000000, 0.056822000000, 0.059399000000, + 0.062077000000, 0.064737000000, 0.067285000000, 0.069764000000, 0.072218000000, + 0.074704000000, 0.077272000000, 0.079979000000, 0.082874000000, 0.086000000000, + 0.089456000000, 0.092947000000, 0.096275000000, 0.099535000000, 0.102829000000, + 0.106256000000, 0.109901000000, 0.113835000000, 0.118167000000, 0.122932000000, + 0.128201000000, 0.133457000000, 0.138323000000, 0.143042000000, 0.147787000000, + 0.152761000000, 0.158102000000, 0.163941000000, 0.170362000000, 0.177425000000, + 0.185190000000, 0.193025000000, 0.200313000000, 0.207156000000, 0.213644000000, + 0.219940000000, 0.226170000000, 0.232467000000, 0.239025000000, 0.245997000000, + 0.253589000000, 0.261876000000, 0.270643000000, 0.279645000000, 0.288694000000, + 0.297665000000, 0.306469000000, 0.315035000000, 0.323335000000, 0.331366000000, + 0.339133000000, 0.347860000000, 0.358326000000, 0.370001000000, 0.382464000000, + 0.395379000000, 0.408482000000, 0.421588000000, 0.434619000000, 0.447601000000, + 0.460777000000, 0.474340000000, 0.488200000000, 0.502340000000, 0.516740000000, + 0.531360000000, 0.546190000000, 0.561180000000, 0.576290000000, 0.591500000000, + 0.606741000000, 0.622150000000, 0.637830000000, 0.653710000000, 0.669680000000, + 0.685660000000, 0.701550000000, 0.717230000000, 0.732570000000, 0.747460000000, + 0.761757000000, 0.775340000000, 0.788220000000, 0.800460000000, 0.812140000000, + 0.823330000000, 0.834120000000, 0.844600000000, 0.854870000000, 0.865040000000, + 0.875211000000, 0.885370000000, 0.895370000000, 0.905150000000, 0.914650000000, + 0.923810000000, 0.932550000000, 0.940810000000, 0.948520000000, 0.955600000000, + 0.961988000000, 0.967540000000, 0.972230000000, 0.976170000000, 0.979460000000, + 0.982200000000, 0.984520000000, 0.986520000000, 0.988320000000, 0.990020000000, + 0.991761000000, 0.993530000000, 0.995230000000, 0.996770000000, 0.998090000000, + 0.999110000000, 0.999770000000, 1.000000000000, 0.999710000000, 0.998850000000, + 0.997340000000, 0.995260000000, 0.992740000000, 0.989750000000, 0.986300000000, + 0.982380000000, 0.977980000000, 0.973110000000, 0.967740000000, 0.961890000000, + 0.955552000000, 0.948601000000, 0.940981000000, 0.932798000000, 0.924158000000, + 0.915175000000, 0.905954000000, 0.896608000000, 0.887249000000, 0.877986000000, + 0.868934000000, 0.860164000000, 0.851519000000, 0.842963000000, 0.834393000000, + 0.825623000000, 0.816764000000, 0.807544000000, 0.797947000000, 0.787893000000, + 0.777405000000, 0.766490000000, 0.755309000000, 0.743845000000, 0.732190000000, + 0.720353000000, 0.708281000000, 0.696055000000, 0.683621000000, 0.671048000000, + 0.658341000000, 0.645545000000, 0.632718000000, 0.619815000000, 0.606887000000, + 0.593878000000, 0.580781000000, 0.567653000000, 0.554490000000, 0.541228000000, + 0.527963000000, 0.514634000000, 0.501363000000, 0.488124000000, 0.474935000000, + 0.461834000000, 0.448823000000, 0.435917000000, 0.423153000000, 0.410526000000, + 0.398057000000, 0.385835000000, 0.373951000000, 0.362311000000, 0.350863000000, + 0.339554000000, 0.328309000000, 0.317118000000, 0.305936000000, 0.294737000000, + 0.283493000000, 0.272222000000, 0.260990000000, 0.249877000000, 0.238946000000, + 0.228254000000, 0.217853000000, 0.207780000000, 0.198072000000, 0.188748000000, + 0.179828000000, 0.171285000000, 0.163059000000, 0.155151000000, 0.147535000000, + 0.140211000000, 0.133170000000, 0.126400000000, 0.119892000000, 0.113640000000, + 0.107633000000, 0.101870000000, 0.096347000000, 0.091063000000, 0.086010000000, + 0.081187000000, 0.076583000000, 0.072198000000, 0.068024000000, 0.064052000000, + 0.060281000000, 0.056697000000, 0.053292000000, 0.050059000000, 0.046998000000, + 0.044096000000, 0.041345000000, 0.038750700000, 0.036297800000, 0.033983200000, + 0.031800400000, 0.029739500000, 0.027791800000, 0.025955100000, 0.024226300000, + 0.022601700000, 0.021077900000, 0.019650500000, 0.018315300000, 0.017068600000, + 0.015905100000, 0.014818300000, 0.013800800000, 0.012849500000, 0.011960700000, + 0.011130300000, 0.010355500000, 0.009633200000, 0.008959900000, 0.008332400000, + 0.007748800000, 0.007204600000, 0.006697500000, 0.006225100000, 0.005785000000, + 0.005375100000, 0.004994100000, 0.004639200000, 0.004309300000, 0.004002800000, + 0.003717740000, 0.003452620000, 0.003205830000, 0.002976230000, 0.002762810000, + 0.002564560000, 0.002380480000, 0.002209710000, 0.002051320000, 0.001904490000, + 0.001768470000, 0.001642360000, 0.001525350000, 0.001416720000, 0.001315950000, + 0.001222390000, 0.001135550000, 0.001054940000, 0.000980140000, 0.000910660000, + 0.000846190000, 0.000786290000, 0.000730680000, 0.000678990000, 0.000631010000, + 0.000586440000, 0.000545110000, 0.000506720000, 0.000471110000, 0.000438050000, + 0.000407410000, 0.000378962000, 0.000352543000, 0.000328001000, 0.000305208000, + 0.000284041000, 0.000264375000, 0.000246109000, 0.000229143000, 0.000213376000, + 0.000198730000, 0.000185115000, 0.000172454000, 0.000160678000, 0.000149730000, + 0.000139550000, 0.000130086000, 0.000121290000, 0.000113106000, 0.000105501000, + 0.000098428000, 0.000091853000, 0.000085738000, 0.000080048000, 0.000074751000, + 0.000069819000, 0.000065222000, 0.000060939000, 0.000056942000, 0.000053217000, + 0.000049737000, 0.000046491000, 0.000043464000, 0.000040635000, 0.000038000000, + 0.000035540500, 0.000033244800, 0.000031100600, 0.000029099000, 0.000027230700, + 0.000025486000, 0.000023856100, 0.000022333200, 0.000020910400, 0.000019580800, + 0.000018338400, 0.000017177700, 0.000016093400, 0.000015080000, 0.000014133600, + 0.000013249000, 0.000012422600, 0.000011649900, 0.000010927700, 0.000010251900, + 0.000009619600, 0.000009028100, 0.000008474000, 0.000007954800, 0.000007468600, + 0.000007012800, 0.000006585800, 0.000006185700, 0.000005810700, 0.000005459000, + 0.000005129800, 0.000004820600, 0.000004531200, 0.000004259100, 0.000004004200, + 0.000003764730, 0.000003539950, 0.000003329140, 0.000003131150, 0.000002945290, + 0.000002770810, 0.000002607050, 0.000002453290, 0.000002308940, 0.000002173380, + 0.000002046130, 0.000001926620, 0.000001814400, 0.000001708950, 0.000001609880, + 0.000001516770, 0.000001429210, 0.000001346860, 0.000001269450, 0.000001196620, + 0.000001128090, 0.000001063680, 0.000001003130, 0.000000946220, 0.000000892630, + 0.000000842160, 0.000000794640, 0.000000749780, 0.000000707440, 0.000000667480, + 0.000000629700 + } + }, + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000000535027, 0.000000810720, 0.000001221200, 0.000001828700, 0.000002722200, + 0.000004028300, 0.000005925700, 0.000008665100, 0.000012596000, 0.000018201000, + 0.000026143700, 0.000037330000, 0.000052987000, 0.000074764000, 0.000104870000, + 0.000146220000, 0.000202660000, 0.000279230000, 0.000382450000, 0.000520720000, + 0.000704776000, 0.000948230000, 0.001268200000, 0.001686100000, 0.002228500000, + 0.002927800000, 0.003823700000, 0.004964200000, 0.006406700000, 0.008219300000, + 0.010482200000, 0.013289000000, 0.016747000000, 0.020980000000, 0.026127000000, + 0.032344000000, 0.039802000000, 0.048691000000, 0.059210000000, 0.071576000000, + 0.086010900000, 0.102740000000, 0.122000000000, 0.144020000000, 0.168990000000, + 0.197120000000, 0.228570000000, 0.263470000000, 0.301900000000, 0.343870000000, + 0.389366000000, 0.437970000000, 0.489220000000, 0.542900000000, 0.598810000000, + 0.656760000000, 0.716580000000, 0.778120000000, 0.841310000000, 0.906110000000, + 0.972542000000, 1.038900000000, 1.103100000000, 1.165100000000, 1.224900000000, + 1.282500000000, 1.338200000000, 1.392600000000, 1.446100000000, 1.499400000000, + 1.553480000000, 1.607200000000, 1.658900000000, 1.708200000000, 1.754800000000, + 1.798500000000, 1.839200000000, 1.876600000000, 1.910500000000, 1.940800000000, + 1.967280000000, 1.989100000000, 2.005700000000, 2.017400000000, 2.024400000000, + 2.027300000000, 2.026400000000, 2.022300000000, 2.015300000000, 2.006000000000, + 1.994800000000, 1.981400000000, 1.965300000000, 1.946400000000, 1.924800000000, + 1.900700000000, 1.874100000000, 1.845100000000, 1.813900000000, 1.780600000000, + 1.745370000000, 1.709100000000, 1.672300000000, 1.634700000000, 1.595600000000, + 1.554900000000, 1.512200000000, 1.467300000000, 1.419900000000, 1.370000000000, + 1.317560000000, 1.262400000000, 1.205000000000, 1.146600000000, 1.088000000000, + 1.030200000000, 0.973830000000, 0.919430000000, 0.867460000000, 0.818280000000, + 0.772125000000, 0.728290000000, 0.686040000000, 0.645530000000, 0.606850000000, + 0.570060000000, 0.535220000000, 0.502340000000, 0.471400000000, 0.442390000000, + 0.415254000000, 0.390024000000, 0.366399000000, 0.344015000000, 0.322689000000, + 0.302356000000, 0.283036000000, 0.264816000000, 0.247848000000, 0.232318000000, + 0.218502000000, 0.205851000000, 0.193596000000, 0.181736000000, 0.170281000000, + 0.159249000000, 0.148673000000, 0.138609000000, 0.129096000000, 0.120215000000, + 0.112044000000, 0.104710000000, 0.098196000000, 0.092361000000, 0.087088000000, + 0.082248000000, 0.077744000000, 0.073456000000, 0.069268000000, 0.065060000000, + 0.060709000000, 0.056457000000, 0.052609000000, 0.049122000000, 0.045954000000, + 0.043050000000, 0.040368000000, 0.037839000000, 0.035384000000, 0.032949000000, + 0.030451000000, 0.028029000000, 0.025862000000, 0.023920000000, 0.022174000000, + 0.020584000000, 0.019127000000, 0.017740000000, 0.016403000000, 0.015064000000, + 0.013676000000, 0.012308000000, 0.011056000000, 0.009915000000, 0.008872000000, + 0.007918000000, 0.007030000000, 0.006223000000, 0.005453000000, 0.004714000000, + 0.003988000000, 0.003289000000, 0.002646000000, 0.002063000000, 0.001533000000, + 0.001091000000, 0.000711000000, 0.000407000000, 0.000184000000, 0.000047000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000, + 0.000000000000 + } + } +}; + +static xslpoly poly_CIE_1964_10 = { 0 }; + +#ifndef SALONEINSTLIB +/* Standard CIE 1964 10 degree observer, */ +/* adjusted for compatibility with 2 degree observer. */ +/* This has a problem in that it will return -ve XYZ values !! */ +static xspect ob_CIE_1964_10c[3] = { + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000000, 0.000000, 0.000000, 0.000000, 0.000001, + 0.000001, 0.000001, 0.000002, 0.000003, 0.000004, + 0.000006, 0.000008, 0.000012, 0.000016, 0.000023, + 0.000032, 0.000044, 0.000061, 0.000083, 0.000114, + 0.000154, 0.000207, 0.000276, 0.000367, 0.000485, + 0.000636, 0.000830, 0.001077, 0.001389, 0.001780, + 0.002268, 0.002872, 0.003616, 0.004524, 0.005626, + 0.006956, 0.008547, 0.010440, 0.012675, 0.015297, + 0.018351, 0.021882, 0.025937, 0.030562, 0.035793, + 0.041670, 0.048218, 0.055455, 0.063395, 0.072026, + 0.081334, 0.091218, 0.101571, 0.112339, 0.123475, + 0.134934, 0.146676, 0.158671, 0.170901, 0.183362, + 0.196059, 0.208632, 0.220678, 0.232166, 0.243106, + 0.253521, 0.263459, 0.273052, 0.282379, 0.291584, + 0.300858, 0.309993, 0.318677, 0.326834, 0.334402, + 0.341323, 0.347615, 0.353171, 0.357967, 0.361976, + 0.365148, 0.367370, 0.368575, 0.368826, 0.368155, + 0.366675, 0.364435, 0.361545, 0.358052, 0.354057, + 0.349627, 0.344810, 0.339593, 0.333917, 0.327752, + 0.321095, 0.313902, 0.306154, 0.297863, 0.289008, + 0.279595, 0.269978, 0.260456, 0.250851, 0.240930, + 0.230604, 0.219745, 0.208345, 0.196419, 0.183863, + 0.170973, 0.158090, 0.145040, 0.131973, 0.119113, + 0.106660, 0.094739, 0.083435, 0.072806, 0.062881, + 0.053655, 0.044761, 0.035965, 0.027445, 0.019347, + 0.011775, 0.004818, -0.001448, -0.006980, -0.011730, + -0.015680, -0.019142, -0.022434, -0.025515, -0.028335, + -0.030850, -0.033009, -0.034770, -0.036095, -0.036941, + -0.037294, -0.037113, -0.036399, -0.035191, -0.033525, + -0.031433, -0.028948, -0.026100, -0.022920, -0.019440, + -0.015700, -0.011512, -0.006687, -0.001238, 0.004822, + 0.011455, 0.018627, 0.026279, 0.034335, 0.042701, + 0.051262, 0.060174, 0.069663, 0.079658, 0.090158, + 0.101084, 0.112386, 0.124002, 0.135873, 0.147891, + 0.160018, 0.172256, 0.184702, 0.197370, 0.210271, + 0.223389, 0.236747, 0.250340, 0.264164, 0.278208, + 0.292463, 0.306856, 0.321322, 0.335886, 0.350539, + 0.365327, 0.380281, 0.395429, 0.410820, 0.426495, + 0.442517, 0.458930, 0.475719, 0.492834, 0.510231, + 0.527852, 0.545635, 0.563512, 0.581404, 0.599231, + 0.616897, 0.634569, 0.652440, 0.670431, 0.688482, + 0.706512, 0.724440, 0.742190, 0.759656, 0.776754, + 0.793379, 0.809482, 0.825090, 0.840216, 0.854883, + 0.869139, 0.883013, 0.896564, 0.909854, 0.922934, + 0.935926, 0.948983, 0.961990, 0.974889, 0.987491, + 0.999611, 1.011241, 1.022004, 1.031804, 1.040446, + 1.047851, 1.054056, 1.059207, 1.063287, 1.066386, + 1.068504, 1.069546, 1.069604, 1.068584, 1.066579, + 1.063578, 1.059609, 1.054735, 1.048871, 1.042112, + 1.034363, 1.025625, 1.015993, 1.005466, 0.993951, + 0.981619, 0.968336, 0.954330, 0.939514, 0.923995, + 0.907841, 0.891082, 0.873794, 0.856007, 0.837798, + 0.819205, 0.800376, 0.781406, 0.762235, 0.742849, + 0.723211, 0.703308, 0.683132, 0.662676, 0.641942, + 0.620930, 0.599617, 0.578068, 0.556470, 0.534965, + 0.513699, 0.492797, 0.472355, 0.452479, 0.433228, + 0.414667, 0.396767, 0.379415, 0.362613, 0.346305, + 0.330490, 0.315149, 0.300263, 0.285813, 0.271789, + 0.258182, 0.244984, 0.232213, 0.219890, 0.208026, + 0.196640, 0.185723, 0.175297, 0.165350, 0.155874, + 0.146878, 0.138317, 0.130168, 0.122403, 0.115042, + 0.108045, 0.101405, 0.095126, 0.089181, 0.083563, + 0.078256, 0.073238, 0.068488, 0.064001, 0.059773, + 0.055794, 0.052058, 0.048555, 0.045275, 0.042210, + 0.039347, 0.036671, 0.034163, 0.031816, 0.029622, + 0.027571, 0.025656, 0.023870, 0.022205, 0.020653, + 0.019209, 0.017863, 0.016608, 0.015438, 0.014349, + 0.013334, 0.012390, 0.011510, 0.010693, 0.009933, + 0.009225, 0.008568, 0.007955, 0.007385, 0.006855, + 0.006363, 0.005906, 0.005481, 0.005088, 0.004723, + 0.004385, 0.004072, 0.003782, 0.003512, 0.003261, + 0.003029, 0.002813, 0.002613, 0.002428, 0.002255, + 0.002095, 0.001946, 0.001808, 0.001680, 0.001561, + 0.001450, 0.001348, 0.001253, 0.001164, 0.001082, + 0.001006, 0.000936, 0.000870, 0.000809, 0.000753, + 0.000701, 0.000652, 0.000607, 0.000565, 0.000526, + 0.000489, 0.000456, 0.000425, 0.000395, 0.000368, + 0.000343, 0.000320, 0.000298, 0.000278, 0.000259, + 0.000242, 0.000225, 0.000210, 0.000196, 0.000183, + 0.000171, 0.000160, 0.000149, 0.000139, 0.000130, + 0.000122, 0.000114, 0.000106, 0.000099, 0.000093, + 0.000087, 0.000081, 0.000076, 0.000071, 0.000066, + 0.000062, 0.000058, 0.000054, 0.000051, 0.000048, + 0.000045, 0.000042, 0.000039, 0.000037, 0.000034, + 0.000032, 0.000030, 0.000028, 0.000026, 0.000025, + 0.000023, 0.000022, 0.000021, 0.000019, 0.000018, + 0.000017, 0.000016, 0.000015, 0.000014, 0.000013, + 0.000012, 0.000012, 0.000011, 0.000010, 0.000010, + 0.000009, 0.000009, 0.000008, 0.000008, 0.000007, + 0.000007, 0.000006, 0.000006, 0.000006, 0.000005, + 0.000005, 0.000005, 0.000004, 0.000004, 0.000004, + 0.000004, 0.000003, 0.000003, 0.000003, 0.000003, + 0.000003, 0.000003, 0.000002, 0.000002, 0.000002, + 0.000002, 0.000002, 0.000002, 0.000002, 0.000002, + 0.000001 + } + }, + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000001, -0.000001, -0.000001, + -0.000002, -0.000003, -0.000004, -0.000005, -0.000007, + -0.000009, -0.000012, -0.000017, -0.000022, -0.000029, + -0.000039, -0.000051, -0.000066, -0.000086, -0.000111, + -0.000142, -0.000181, -0.000229, -0.000289, -0.000362, + -0.000452, -0.000560, -0.000690, -0.000845, -0.001028, + -0.001243, -0.001494, -0.001786, -0.002121, -0.002505, + -0.002940, -0.003430, -0.003976, -0.004584, -0.005251, + -0.005978, -0.006763, -0.007608, -0.008509, -0.009457, + -0.010445, -0.011465, -0.012506, -0.013548, -0.014581, + -0.015588, -0.016540, -0.017410, -0.018194, -0.018887, + -0.019474, -0.019954, -0.020356, -0.020652, -0.020844, + -0.020966, -0.020987, -0.020899, -0.020710, -0.020374, + -0.019885, -0.019227, -0.018381, -0.017329, -0.016059, + -0.014554, -0.012894, -0.011147, -0.009284, -0.007270, + -0.005073, -0.002659, 0.000006, 0.002956, 0.006209, + 0.009845, 0.013596, 0.017297, 0.021039, 0.024915, + 0.029010, 0.033403, 0.038157, 0.043368, 0.049064, + 0.055306, 0.061573, 0.067495, 0.073309, 0.079203, + 0.085372, 0.091959, 0.099098, 0.106881, 0.115363, + 0.124611, 0.134041, 0.143043, 0.151664, 0.159962, + 0.168055, 0.176039, 0.184021, 0.192166, 0.200605, + 0.209524, 0.219014, 0.228895, 0.238933, 0.248950, + 0.258827, 0.268480, 0.277842, 0.286890, 0.295622, + 0.304046, 0.313314, 0.324171, 0.336130, 0.348800, + 0.361865, 0.375076, 0.388254, 0.401320, 0.414291, + 0.427388, 0.440818, 0.454528, 0.468501, 0.482717, + 0.497136, 0.511747, 0.526497, 0.541348, 0.556275, + 0.571209, 0.586279, 0.601590, 0.617079, 0.632648, + 0.648223, 0.663715, 0.679010, 0.693991, 0.708553, + 0.722563, 0.735901, 0.748570, 0.760630, 0.772158, + 0.783222, 0.793907, 0.804300, 0.814498, 0.824603, + 0.834716, 0.844817, 0.854762, 0.864494, 0.873959, + 0.883100, 0.891844, 0.900135, 0.907907, 0.915088, + 0.921619, 0.927360, 0.932278, 0.936482, 0.940066, + 0.943128, 0.945794, 0.948157, 0.950335, 0.952425, + 0.954563, 0.956738, 0.958855, 0.960827, 0.962596, + 0.964083, 0.965229, 0.965966, 0.966206, 0.965899, + 0.964967, 0.963491, 0.961602, 0.959270, 0.956503, + 0.953289, 0.949616, 0.945492, 0.940884, 0.935810, + 0.930259, 0.924111, 0.917313, 0.909967, 0.902174, + 0.894043, 0.885675, 0.877179, 0.868664, 0.860233, + 0.852001, 0.844039, 0.836194, 0.828430, 0.820645, + 0.812656, 0.804568, 0.796113, 0.787273, 0.777966, + 0.768211, 0.758017, 0.747540, 0.736765, 0.725779, + 0.714594, 0.703154, 0.691541, 0.679699, 0.667698, + 0.655540, 0.643271, 0.630947, 0.618523, 0.606050, + 0.593471, 0.580782, 0.568037, 0.555234, 0.542308, + 0.529357, 0.516318, 0.503314, 0.490317, 0.477348, + 0.464445, 0.451610, 0.438859, 0.426230, 0.413718, + 0.401346, 0.389202, 0.377375, 0.365775, 0.354350, + 0.343050, 0.331804, 0.320601, 0.309399, 0.298173, + 0.286897, 0.275586, 0.264305, 0.253136, 0.242143, + 0.231384, 0.220911, 0.210763, 0.200977, 0.191575, + 0.182575, 0.173953, 0.165647, 0.157658, 0.149962, + 0.142556, 0.135433, 0.128580, 0.121988, 0.115652, + 0.109560, 0.103711, 0.098103, 0.092734, 0.087598, + 0.082694, 0.078011, 0.073550, 0.069303, 0.065262, + 0.061425, 0.057778, 0.054312, 0.051021, 0.047905, + 0.044950, 0.042148, 0.039506, 0.037007, 0.034649, + 0.032426, 0.030326, 0.028341, 0.026469, 0.024707, + 0.023051, 0.021498, 0.020042, 0.018681, 0.017410, + 0.016224, 0.015115, 0.014078, 0.013108, 0.012201, + 0.011354, 0.010564, 0.009827, 0.009140, 0.008500, + 0.007905, 0.007350, 0.006833, 0.006351, 0.005902, + 0.005484, 0.005095, 0.004733, 0.004397, 0.004084, + 0.003793, 0.003523, 0.003271, 0.003036, 0.002819, + 0.002616, 0.002429, 0.002254, 0.002093, 0.001943, + 0.001804, 0.001676, 0.001556, 0.001445, 0.001342, + 0.001247, 0.001158, 0.001076, 0.001000, 0.000929, + 0.000863, 0.000802, 0.000745, 0.000693, 0.000644, + 0.000598, 0.000556, 0.000517, 0.000481, 0.000447, + 0.000416, 0.000387, 0.000360, 0.000335, 0.000311, + 0.000290, 0.000270, 0.000251, 0.000234, 0.000218, + 0.000203, 0.000189, 0.000176, 0.000164, 0.000153, + 0.000142, 0.000133, 0.000124, 0.000115, 0.000108, + 0.000100, 0.000094, 0.000087, 0.000082, 0.000076, + 0.000071, 0.000066, 0.000062, 0.000058, 0.000054, + 0.000051, 0.000047, 0.000044, 0.000041, 0.000039, + 0.000036, 0.000034, 0.000032, 0.000030, 0.000028, + 0.000026, 0.000024, 0.000023, 0.000021, 0.000020, + 0.000019, 0.000018, 0.000016, 0.000015, 0.000014, + 0.000013, 0.000013, 0.000012, 0.000011, 0.000010, + 0.000010, 0.000009, 0.000009, 0.000008, 0.000008, + 0.000007, 0.000007, 0.000006, 0.000006, 0.000006, + 0.000005, 0.000005, 0.000005, 0.000004, 0.000004, + 0.000004, 0.000004, 0.000003, 0.000003, 0.000003, + 0.000003, 0.000003, 0.000002, 0.000002, 0.000002, + 0.000002, 0.000002, 0.000002, 0.000002, 0.000002, + 0.000002, 0.000001, 0.000001, 0.000001, 0.000001, + 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, + 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, + 0.000001 + } + }, + { + 471, 360.0, 830.0, /* 471 bands from 360 to 830 nm in 1nm steps */ + 1.0, /* Scale factor */ + { + 0.000000, 0.000001, 0.000001, 0.000002, 0.000002, + 0.000004, 0.000005, 0.000008, 0.000011, 0.000017, + 0.000024, 0.000034, 0.000048, 0.000068, 0.000095, + 0.000133, 0.000184, 0.000253, 0.000347, 0.000473, + 0.000640, 0.000861, 0.001151, 0.001530, 0.002023, + 0.002657, 0.003470, 0.004505, 0.005815, 0.007460, + 0.009514, 0.012061, 0.015200, 0.019042, 0.023714, + 0.029357, 0.036126, 0.044195, 0.053743, 0.064968, + 0.078071, 0.093257, 0.110741, 0.130730, 0.153398, + 0.178935, 0.207487, 0.239172, 0.274063, 0.312169, + 0.353478, 0.397611, 0.444150, 0.492897, 0.543671, + 0.596302, 0.650635, 0.706534, 0.763936, 0.822806, + 0.883165, 0.943463, 1.001808, 1.058162, 1.112525, + 1.164898, 1.215552, 1.265032, 1.313703, 1.362201, + 1.411414, 1.460308, 1.507373, 1.552264, 1.594709, + 1.634529, 1.671632, 1.705747, 1.736693, 1.764380, + 1.788610, 1.808612, 1.823873, 1.834684, 1.841230, + 1.844058, 1.843442, 1.839930, 1.833797, 1.825590, + 1.815677, 1.803767, 1.789396, 1.772476, 1.753107, + 1.731476, 1.707589, 1.681541, 1.653519, 1.623619, + 1.591997, 1.559431, 1.526359, 1.492551, 1.457384, + 1.420779, 1.382383, 1.342017, 1.299412, 1.254573, + 1.207456, 1.157853, 1.106181, 1.053574, 1.000759, + 0.948651, 0.897828, 0.848786, 0.801953, 0.757664, + 0.716144, 0.676765, 0.638856, 0.602538, 0.567881, + 0.534929, 0.503728, 0.474283, 0.446572, 0.420583, + 0.396265, 0.373716, 0.352709, 0.332886, 0.314056, + 0.296143, 0.279152, 0.263152, 0.248277, 0.234696, + 0.222673, 0.211713, 0.201107, 0.190856, 0.180969, + 0.171463, 0.162367, 0.153733, 0.145596, 0.138030, + 0.131104, 0.124934, 0.119502, 0.114675, 0.110342, + 0.106383, 0.102706, 0.099197, 0.095748, 0.092248, + 0.088582, 0.084959, 0.081651, 0.078623, 0.075842, + 0.073262, 0.070852, 0.068555, 0.066306, 0.064065, + 0.061762, 0.059523, 0.057502, 0.055668, 0.053992, + 0.052433, 0.050967, 0.049534, 0.048112, 0.046651, + 0.045104, 0.043532, 0.042023, 0.040577, 0.039188, + 0.037849, 0.036544, 0.035292, 0.034054, 0.032828, + 0.031603, 0.030388, 0.029205, 0.028058, 0.026937, + 0.025874, 0.024845, 0.023862, 0.022928, 0.022050, + 0.021230, 0.020427, 0.019595, 0.018738, 0.017856, + 0.016954, 0.016034, 0.015099, 0.014153, 0.013199, + 0.012241, 0.011277, 0.010301, 0.009319, 0.008334, + 0.007350, 0.006370, 0.005397, 0.004434, 0.003484, + 0.002547, 0.001621, 0.000702, -0.000208, -0.001107, + -0.001996, -0.002870, -0.003725, -0.004559, -0.005368, + -0.006147, -0.006898, -0.007619, -0.008311, -0.008971, + -0.009601, -0.010199, -0.010764, -0.011296, -0.011794, + -0.012259, -0.012690, -0.013085, -0.013445, -0.013769, + -0.014058, -0.014311, -0.014530, -0.014715, -0.014865, + -0.014982, -0.015064, -0.015114, -0.015131, -0.015117, + -0.015074, -0.015002, -0.014904, -0.014780, -0.014633, + -0.014462, -0.014272, -0.014059, -0.013828, -0.013579, + -0.013314, -0.013035, -0.012743, -0.012439, -0.012124, + -0.011801, -0.011467, -0.011122, -0.010769, -0.010411, + -0.010052, -0.009695, -0.009340, -0.008992, -0.008650, + -0.008318, -0.007995, -0.007679, -0.007371, -0.007069, + -0.006773, -0.006483, -0.006199, -0.005920, -0.005647, + -0.005378, -0.005116, -0.004859, -0.004609, -0.004366, + -0.004133, -0.003908, -0.003692, -0.003486, -0.003290, + -0.003104, -0.002926, -0.002757, -0.002595, -0.002441, + -0.002294, -0.002155, -0.002023, -0.001898, -0.001780, + -0.001668, -0.001562, -0.001462, -0.001367, -0.001277, + -0.001193, -0.001113, -0.001039, -0.000969, -0.000904, + -0.000843, -0.000786, -0.000732, -0.000682, -0.000635, + -0.000591, -0.000550, -0.000512, -0.000476, -0.000443, + -0.000412, -0.000383, -0.000356, -0.000331, -0.000308, + -0.000286, -0.000266, -0.000247, -0.000230, -0.000213, + -0.000198, -0.000184, -0.000171, -0.000159, -0.000147, + -0.000137, -0.000127, -0.000118, -0.000109, -0.000101, + -0.000094, -0.000087, -0.000081, -0.000075, -0.000070, + -0.000065, -0.000060, -0.000056, -0.000052, -0.000048, + -0.000045, -0.000042, -0.000039, -0.000036, -0.000033, + -0.000031, -0.000029, -0.000027, -0.000025, -0.000023, + -0.000022, -0.000020, -0.000019, -0.000017, -0.000016, + -0.000015, -0.000014, -0.000013, -0.000012, -0.000011, + -0.000010, -0.000010, -0.000009, -0.000008, -0.000008, + -0.000007, -0.000007, -0.000006, -0.000006, -0.000006, + -0.000005, -0.000005, -0.000004, -0.000004, -0.000004, + -0.000004, -0.000003, -0.000003, -0.000003, -0.000003, + -0.000003, -0.000002, -0.000002, -0.000002, -0.000002, + -0.000002, -0.000002, -0.000002, -0.000002, -0.000001, + -0.000001, -0.000001, -0.000001, -0.000001, -0.000001, + -0.000001, -0.000001, -0.000001, -0.000001, -0.000001, + -0.000001, -0.000001, -0.000001, -0.000001, -0.000001, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, + -0.000000 + } + } +}; + +static xslpoly poly_CIE_1964_10c = { 0 }; + +/* Judd & Voss 1978 2 degree */ +static xspect ob_Judd_Voss_2[3] = { + { + 90, 380.0, 825.0, /* 90 bands from 380 to 825 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 2.689900e-003, 5.310500e-003, 1.078100e-002, 2.079200e-002, 3.798100e-002, + 6.315700e-002, 9.994100e-002, 1.582400e-001, 2.294800e-001, 2.810800e-001, + 3.109500e-001, 3.307200e-001, 3.333600e-001, 3.167200e-001, 2.888200e-001, + 2.596900e-001, 2.327600e-001, 2.099900e-001, 1.747600e-001, 1.328700e-001, + 9.194400e-002, 5.698500e-002, 3.173100e-002, 1.461300e-002, 4.849100e-003, + 2.321500e-003, 9.289900e-003, 2.927800e-002, 6.379100e-002, 1.108100e-001, + 1.669200e-001, 2.276800e-001, 2.926900e-001, 3.622500e-001, 4.363500e-001, + 5.151300e-001, 5.974800e-001, 6.812100e-001, 7.642500e-001, 8.439400e-001, + 9.163500e-001, 9.770300e-001, 1.023000e+000, 1.051300e+000, 1.055000e+000, + 1.036200e+000, 9.923900e-001, 9.286100e-001, 8.434600e-001, 7.398300e-001, + 6.328900e-001, 5.335100e-001, 4.406200e-001, 3.545300e-001, 2.786200e-001, + 2.148500e-001, 1.616100e-001, 1.182000e-001, 8.575300e-002, 6.307700e-002, + 4.583400e-002, 3.205700e-002, 2.218700e-002, 1.561200e-002, 1.109800e-002, + 7.923300e-003, 5.653100e-003, 4.003900e-003, 2.825300e-003, 1.994700e-003, + 1.399400e-003, 9.698000e-004, 6.684700e-004, 4.614100e-004, 3.207300e-004, + 2.257300e-004, 1.597300e-004, 1.127500e-004, 7.951300e-005, 5.608700e-005, + 3.954100e-005, 2.785200e-005, 1.959700e-005, 1.377000e-005, 9.670000e-006, + 6.791800e-006, 4.770600e-006, 3.355000e-006, 2.353400e-006, 1.637700e-006 + } + }, + { + 90, 380.0, 825.0, /* 90 bands from 380 to 825 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 2.000000e-004, 3.955600e-004, 8.000000e-004, 1.545700e-003, 2.800000e-003, + 4.656200e-003, 7.400000e-003, 1.177900e-002, 1.750000e-002, 2.267800e-002, + 2.730000e-002, 3.258400e-002, 3.790000e-002, 4.239100e-002, 4.680000e-002, + 5.212200e-002, 6.000000e-002, 7.294200e-002, 9.098000e-002, 1.128400e-001, + 1.390200e-001, 1.698700e-001, 2.080200e-001, 2.580800e-001, 3.230000e-001, + 4.054000e-001, 5.030000e-001, 6.081100e-001, 7.100000e-001, 7.951000e-001, + 8.620000e-001, 9.150500e-001, 9.540000e-001, 9.800400e-001, 9.949500e-001, + 1.000100e+000, 9.950000e-001, 9.787500e-001, 9.520000e-001, 9.155800e-001, + 8.700000e-001, 8.162300e-001, 7.570000e-001, 6.948300e-001, 6.310000e-001, + 5.665400e-001, 5.030000e-001, 4.417200e-001, 3.810000e-001, 3.205200e-001, + 2.650000e-001, 2.170200e-001, 1.750000e-001, 1.381200e-001, 1.070000e-001, + 8.165200e-002, 6.100000e-002, 4.432700e-002, 3.200000e-002, 2.345400e-002, + 1.700000e-002, 1.187200e-002, 8.210000e-003, 5.772300e-003, 4.102000e-003, + 2.929100e-003, 2.091000e-003, 1.482200e-003, 1.047000e-003, 7.401500e-004, + 5.200000e-004, 3.609300e-004, 2.492000e-004, 1.723100e-004, 1.200000e-004, + 8.462000e-005, 6.000000e-005, 4.244600e-005, 3.000000e-005, 2.121000e-005, + 1.498900e-005, 1.058400e-005, 7.465600e-006, 5.259200e-006, 3.702800e-006, + 2.607600e-006, 1.836500e-006, 1.295000e-006, 9.109200e-007, 6.356400e-007 + } + }, + { + 90, 380.0, 825.0, /* 90 bands from 380 to 825 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 1.226000e-002, 2.422200e-002, 4.925000e-002, 9.513500e-002, 1.740900e-001, + 2.901300e-001, 4.605300e-001, 7.316600e-001, 1.065800e+000, 1.314600e+000, + 1.467200e+000, 1.579600e+000, 1.616600e+000, 1.568200e+000, 1.471700e+000, + 1.374000e+000, 1.291700e+000, 1.235600e+000, 1.113800e+000, 9.422000e-001, + 7.559600e-001, 5.864000e-001, 4.466900e-001, 3.411600e-001, 2.643700e-001, + 2.059400e-001, 1.544500e-001, 1.091800e-001, 7.658500e-002, 5.622700e-002, + 4.136600e-002, 2.935300e-002, 2.004200e-002, 1.331200e-002, 8.782300e-003, + 5.857300e-003, 4.049300e-003, 2.921700e-003, 2.277100e-003, 1.970600e-003, + 1.806600e-003, 1.544900e-003, 1.234800e-003, 1.117700e-003, 9.056400e-004, + 6.946700e-004, 4.288500e-004, 3.181700e-004, 2.559800e-004, 1.567900e-004, + 9.769400e-005, 6.894400e-005, 5.116500e-005, 3.601600e-005, 2.423800e-005, + 1.691500e-005, 1.190600e-005, 8.148900e-006, 5.600600e-006, 3.954400e-006, + 2.791200e-006, 1.917600e-006, 1.313500e-006, 9.151900e-007, 6.476700e-007, + 4.635200e-007, 3.330400e-007, 2.382300e-007, 1.702600e-007, 1.220700e-007, + 8.710700e-008, 6.145500e-008, 4.316200e-008, 3.037900e-008, 2.155400e-008, + 1.549300e-008, 1.120400e-008, 8.087300e-009, 5.834000e-009, 4.211000e-009, + 3.038300e-009, 2.190700e-009, 1.577800e-009, 1.134800e-009, 8.156500e-010, + 5.862600e-010, 4.213800e-010, 3.031900e-010, 2.175300e-010, 1.547600e-010 + } + } +}; + +static xslpoly poly_Judd_Voss_2 = { 0 }; + + +/* Stiles & Burch 1955 2 degree, */ +/* rotated to align with 1931 XYZ space, */ +/* using Mark Shaw's matrix. */ +static xspect ob_Stiles_Burch_2[3] = { + { + 69, 390.0, 730.0, /* 69 bands from 390 to 730 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 0.005035, 0.012873, 0.025933, 0.054264, 0.093147, + 0.144597, 0.207609, 0.266538, 0.303933, 0.336185, + 0.356549, 0.364180, 0.328209, 0.286053, 0.262928, + 0.210562, 0.182549, 0.131014, 0.081974, 0.045980, + 0.020673, 0.008302, 0.004814, 0.008248, 0.024412, + 0.050113, 0.084255, 0.131255, 0.186757, 0.243224, + 0.298768, 0.359848, 0.428510, 0.500880, 0.571271, + 0.650846, 0.742250, 0.829040, 0.905369, 0.971275, + 1.024797, 1.060952, 1.071632, 1.054762, 1.012750, + 0.947501, 0.861487, 0.761200, 0.654122, 0.548338, + 0.450269, 0.361237, 0.281687, 0.213565, 0.158588, + 0.115934, 0.083874, 0.060355, 0.043191, 0.030661, + 0.021532, 0.014822, 0.010047, 0.006832, 0.004755, + 0.003363, 0.002330, 0.001623, 0.001136 + } + }, + { + 69, 390.0, 730.0, /* 69 bands from 390 to 730 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 0.000021, 0.000137, 0.000267, 0.000499, 0.000959, + -0.000352, -0.000535, -0.002306, -0.001139, 0.001507, + 0.007142, 0.012389, 0.022879, 0.037200, 0.054616, + 0.080087, 0.108008, 0.140411, 0.170719, 0.199791, + 0.240641, 0.297681, 0.367645, 0.455184, 0.546333, + 0.641762, 0.736259, 0.813393, 0.873858, 0.911828, + 0.931983, 0.954960, 0.971754, 0.970171, 0.950790, + 0.937240, 0.932444, 0.903026, 0.857070, 0.815886, + 0.769666, 0.712437, 0.651257, 0.588631, 0.523557, + 0.457801, 0.393963, 0.332964, 0.275541, 0.223722, + 0.179091, 0.140943, 0.108310, 0.081218, 0.059780, + 0.043398, 0.031238, 0.022406, 0.016003, 0.011340, + 0.007953, 0.005473, 0.003713, 0.002527, 0.001759, + 0.001244, 0.000863, 0.000602, 0.000422 + } + }, + { + 69, 390.0, 730.0, /* 69 bands from 390 to 730 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 0.023163, 0.059308, 0.118897, 0.250907, 0.433765, + 0.684409, 0.999185, 1.308097, 1.518706, 1.707302, + 1.840521, 1.906560, 1.756384, 1.578949, 1.505983, + 1.283002, 1.178612, 0.947751, 0.709500, 0.529689, + 0.398963, 0.310980, 0.240664, 0.188969, 0.145151, + 0.110796, 0.087421, 0.069953, 0.059951, 0.051960, + 0.042905, 0.037710, 0.033821, 0.028764, 0.023371, + 0.018909, 0.015103, 0.009656, 0.003595, -0.001221, + -0.005978, -0.010905, -0.014270, -0.016302, -0.018412, + -0.019889, -0.019510, -0.017854, -0.015815, -0.013632, + -0.011388, -0.009171, -0.007076, -0.005238, -0.003775, + -0.002692, -0.001946, -0.001447, -0.001074, -0.000766, + -0.000527, -0.000357, -0.000242, -0.000164, -0.000113, + -0.000077, -0.000051, -0.000034, -0.000023 + } + } +}; + +static xslpoly poly_Stiles_Burch_2 = { 0 }; + +/* Shaw & Fairchild 1997 2 degree observer. */ +/* From Mark Shaw's Masters thesis: */ +/* "Evaluating the 1931 CIE Color Matching Functions" */ +static xspect ob_Shaw_Fairchild_2[3] = { + { + 61, 400.0, 700.0, /* 61 bands from 400 to 700 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 0.050035085, 0.10599540, 0.17570524, 0.26369069, 0.34385256, + 0.36314044, 0.35022338, 0.35921696, 0.37057582, 0.37027683, + 0.31092719, 0.24467905, 0.21495057, 0.16408854, 0.15609086, + 0.10496585, 0.053550350, 0.016029866, -0.010473666, -0.020635411, + -0.020599591, -0.010774255, 0.013507015, 0.045305699, 0.082609321, + 0.13244251, 0.18966495, 0.24710489, 0.30272442, 0.36362744, + 0.43329425, 0.50816654, 0.57883305, 0.66085495, 0.75701632, + 0.84336950, 0.91608703, 0.98348582, 1.0386456, 1.0699974, + 1.0751974, 1.0492333, 1.0010173, 0.92955516, 0.83907524, + 0.74332961, 0.64179542, 0.53031723, 0.42814376, 0.34048130, + 0.26368241, 0.19677558, 0.14402031, 0.10472063, 0.076398767, + 0.054311075, 0.037000030, 0.026240015, 0.018750015, 0.012892199, + 0.0081198003 + } + }, + { + 61, 400.0, 700.0, /* 61 bands from 400 to 700 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 0.00073439190, 0.0010295739, 0.0011050375, -0.00057123313, -0.0015421159, + -0.0050492258, -0.0060441241, -0.0050340813, -0.00046015829, 0.0043453053, + 0.014594307, 0.028653705, 0.047841334, 0.078039315, 0.11339641, + 0.15326829, 0.18931877, 0.22596008, 0.26950734, 0.32894461, + 0.39924943, 0.48161678, 0.56603317, 0.65284913, 0.73864212, + 0.80870955, 0.86388621, 0.90168525, 0.92331427, 0.94508269, + 0.96035974, 0.96084837, 0.94474215, 0.93248079, 0.92686312, + 0.89683591, 0.85193527, 0.81446493, 0.77206051, 0.71732417, + 0.65749412, 0.59544590, 0.53033945, 0.46382662, 0.39929743, + 0.33905951, 0.28179144, 0.22775797, 0.18104592, 0.14195030, + 0.10887196, 0.081491712, 0.059933233, 0.043516174, 0.031399280, + 0.022402933, 0.015787610, 0.011178068, 0.0078482427, 0.0054361119, + 0.0036285556 + } + }, + { + 61, 400.0, 700.0, /* 61 bands from 400 to 700 nm in 5nm steps */ + 1.0, /* Scale factor */ + { + 0.19346810, 0.37355444, 0.62641781, 0.98559734, 1.3578634, + 1.5413908, 1.6258281, 1.7422823, 1.8184109, 1.7982693, + 1.6624945, 1.4917210, 1.3537111, 1.2543216, 1.1444894, + 0.94078221, 0.73058355, 0.55774101, 0.42026628, 0.31970216, + 0.24388223, 0.18951860, 0.14567319, 0.11603887, 0.094972125, + 0.077803903, 0.065288720, 0.055235267, 0.046945157, 0.039405440, + 0.033042398, 0.026944585, 0.021626844, 0.016807242, 0.011642648, + 0.0061489002, 0.00061480026, -0.0044002687, -0.0090514735, -0.013048603, + -0.016063598, -0.018035874, -0.019053770, -0.019163305, -0.018371066, + -0.016810570, -0.014805852, -0.012650736, -0.010513405, -0.0085481723, + -0.0067628649, -0.0051813692, -0.0038792081, -0.0028536093, -0.0020731313, + -0.0014924891, -0.0010704383, -0.00075273042, -0.00052400943, -0.00036054897, + -0.00025295701 + } + } +}; + +static xslpoly poly_Shaw_Fairchild_2 = { 0 }; + +#endif /* !SALONEINSTLIB */ + +/* Return pointers to three xpsects with a standard observer weighting curves */ +/* return 0 on sucecss, nz if not matched */ +int standardObserver( +xspect *sp[3], /* Return 3 pointers */ +icxObserverType obType /* Type of observer */ +) { + switch (obType) { + case icxOT_custom: + return 1; + case icxOT_none: + return 1; + case icxOT_default: + case icxOT_CIE_1931_2: + sp[0] = &ob_CIE_1931_2[0]; + sp[1] = &ob_CIE_1931_2[1]; + sp[2] = &ob_CIE_1931_2[2]; + return 0; + case icxOT_CIE_1964_10: + sp[0] = &ob_CIE_1964_10[0]; + sp[1] = &ob_CIE_1964_10[1]; + sp[2] = &ob_CIE_1964_10[2]; + return 0; +#ifndef SALONEINSTLIB + case icxOT_Stiles_Burch_2: + sp[0] = &ob_Stiles_Burch_2[0]; + sp[1] = &ob_Stiles_Burch_2[1]; + sp[2] = &ob_Stiles_Burch_2[2]; + return 0; + case icxOT_Judd_Voss_2: + sp[0] = &ob_Judd_Voss_2[0]; + sp[1] = &ob_Judd_Voss_2[1]; + sp[2] = &ob_Judd_Voss_2[2]; + return 0; + case icxOT_CIE_1964_10c: + sp[0] = &ob_CIE_1964_10c[0]; + sp[1] = &ob_CIE_1964_10c[1]; + sp[2] = &ob_CIE_1964_10c[2]; + return 0; + case icxOT_Shaw_Fairchild_2: + sp[0] = &ob_Shaw_Fairchild_2[0]; + sp[1] = &ob_Shaw_Fairchild_2[1]; + sp[2] = &ob_Shaw_Fairchild_2[2]; + return 0; +#endif /* !SALONEINSTLIB */ + default: + return 1; + } +} + +/* Return a string describing the standard observer */ +char *standardObserverDescription(icxObserverType obType) { + switch (obType) { + case icxOT_custom: + return "Custom observer"; + case icxOT_none: + return "No observer"; + case icxOT_default: + case icxOT_CIE_1931_2: + return "CIE 1931 2 degree observer"; + case icxOT_CIE_1964_10: + return "CIE 1964 10 degree observer"; +#ifndef SALONEINSTLIB + case icxOT_Stiles_Burch_2: + return "Stiles & Burch 1955 2 degree observer (aligned)"; + case icxOT_Judd_Voss_2: + return "Judd & Voss 1978 2 degree observer"; + case icxOT_CIE_1964_10c: + return "CIE 1964 10 degree observer (aligned)"; + case icxOT_Shaw_Fairchild_2: + return "Shaw & Fairchild 1997 2 degree observer"; +#endif /* !SALONEINSTLIB */ + } + return "Unknown observer"; +} + +/* Return a pointer to the spectral locus poligon */ +/* return NULL on failure. */ +static xslpoly *spectral_locus_poligon( +icxObserverType obType /* Type of observer */ +) { + switch (obType) { + case icxOT_custom: + return NULL; + case icxOT_none: + return NULL; + case icxOT_default: + case icxOT_CIE_1931_2: + return &poly_CIE_1931_2; + case icxOT_CIE_1964_10: + return &poly_CIE_1964_10; +#ifndef SALONEINSTLIB + case icxOT_Stiles_Burch_2: + return &poly_Stiles_Burch_2; + case icxOT_Judd_Voss_2: + return &poly_Judd_Voss_2; + case icxOT_CIE_1964_10c: + return &poly_CIE_1964_10c; + case icxOT_Shaw_Fairchild_2: + return &poly_Shaw_Fairchild_2; +#endif /* !SALONEINSTLIB */ + default: + return NULL; + } +} + + +#ifndef SALONEINSTLIB +/* ----------------------------------- */ +/* Standard refelective sample spectra */ + +/* Ra CRI Test Color Samples */ +static xspect CIE1995_TCS[] = { + + /* TCS01 7.5 R 6/4 Light greyish red */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.12, 0.14, 0.16, 0.19, 0.22, 0.24, 0.25, 0.26, 0.26, 0.25, + 0.25, 0.25, 0.24, 0.24, 0.24, 0.23, 0.23, 0.23, 0.23, 0.22, + 0.22, 0.22, 0.22, 0.21, 0.21, 0.21, 0.22, 0.22, 0.22, 0.23, + 0.23, 0.23, 0.23, 0.23, 0.23, 0.23, 0.24, 0.25, 0.25, 0.26, + 0.27, 0.28, 0.3, 0.32, 0.34, 0.37, 0.39, 0.41, 0.42, 0.44, + 0.44, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, + 0.45, 0.45, 0.45, 0.45, 0.46, 0.46, 0.46, 0.46, 0.46, 0.46, + 0.46, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, + 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, 0.47, + 0.47, 0.47, 0.47, 0.46, 0.46 + } + }, + /* TCS02 5 Y6/4 Dark greyish yellow */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.05, 0.06, 0.06, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.13, 0.13, + 0.13, 0.13, 0.14, 0.14, 0.15, 0.16, 0.17, 0.19, 0.21, 0.23, + 0.24, 0.25, 0.26, 0.26, 0.27, 0.27, 0.27, 0.28, 0.28, 0.29, + 0.3, 0.31, 0.32, 0.33, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, + 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, + 0.33, 0.33, 0.33, 0.33, 0.33, 0.33, 0.33, 0.33, 0.33, 0.33, + 0.33, 0.33, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, + 0.32, 0.32, 0.32, 0.31, 0.31, 0.31, 0.31, 0.31, 0.31, 0.31, + 0.31, 0.31, 0.31, 0.31, 0.31 + } + }, + /* TCS03 5 GY 6/8 Strong yellow green */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.06, 0.06, 0.06, 0.06, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, + 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.08, + 0.08, 0.08, 0.09, 0.09, 0.11, 0.13, 0.15, 0.17, 0.2, 0.22, + 0.24, 0.26, 0.28, 0.3, 0.34, 0.37, 0.39, 0.4, 0.4, 0.39, + 0.38, 0.37, 0.35, 0.33, 0.32, 0.3, 0.29, 0.27, 0.26, 0.26, + 0.25, 0.25, 0.24, 0.24, 0.23, 0.22, 0.22, 0.22, 0.22, 0.22, + 0.22, 0.22, 0.23, 0.24, 0.25, 0.27, 0.29, 0.31, 0.34, 0.37, + 0.39, 0.41, 0.43, 0.45, 0.46, 0.47, 0.48, 0.49, 0.49, 0.5, + 0.5, 0.5, 0.51, 0.51, 0.52, 0.52, 0.52, 0.53, 0.53, 0.54, + 0.54, 0.54, 0.55, 0.55, 0.56 + } + }, + /* TCS04 2.5 G 6/6 Moderate yellowish green */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.06, 0.06, 0.06, 0.07, 0.07, 0.08, 0.09, 0.11, 0.12, 0.12, + 0.12, 0.13, 0.13, 0.13, 0.14, 0.14, 0.14, 0.15, 0.16, 0.17, + 0.19, 0.21, 0.23, 0.25, 0.28, 0.31, 0.33, 0.35, 0.37, 0.38, + 0.39, 0.39, 0.4, 0.39, 0.39, 0.38, 0.37, 0.35, 0.34, 0.33, + 0.31, 0.3, 0.28, 0.26, 0.25, 0.23, 0.21, 0.2, 0.19, 0.18, + 0.17, 0.16, 0.16, 0.16, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, + 0.15, 0.15, 0.15, 0.15, 0.16, 0.16, 0.17, 0.17, 0.17, 0.17, + 0.17, 0.17, 0.17, 0.16, 0.16, 0.17, 0.17, 0.17, 0.18, 0.18, + 0.19, 0.19, 0.19, 0.19, 0.2, 0.2, 0.2, 0.21, 0.22, 0.23, + 0.23, 0.24, 0.25, 0.26, 0.27 + } + }, + /* TCS05 10 BG 6/4 Light bluish green */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.14, 0.19, 0.23, 0.27, 0.3, 0.31, 0.31, 0.31, 0.31, 0.32, + 0.32, 0.32, 0.33, 0.33, 0.33, 0.34, 0.35, 0.35, 0.36, 0.37, + 0.38, 0.39, 0.4, 0.41, 0.42, 0.42, 0.42, 0.42, 0.41, 0.41, + 0.4, 0.4, 0.39, 0.38, 0.37, 0.36, 0.35, 0.34, 0.33, 0.32, + 0.31, 0.3, 0.28, 0.27, 0.26, 0.25, 0.23, 0.22, 0.21, 0.2, + 0.19, 0.19, 0.19, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, + 0.18, 0.18, 0.18, 0.18, 0.19, 0.19, 0.19, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.21, + 0.21, 0.21, 0.22, 0.22, 0.22, 0.22, 0.23, 0.23, 0.24, 0.24, + 0.25, 0.26, 0.27, 0.27, 0.28 + } + }, + /* TCS06 5 PB 6/8 Light blue */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.08, 0.08, 0.09, 0.11, 0.15, 0.2, 0.27, 0.34, 0.41, 0.46, + 0.49, 0.51, 0.52, 0.52, 0.53, 0.54, 0.54, 0.55, 0.56, 0.56, + 0.55, 0.55, 0.54, 0.53, 0.52, 0.5, 0.49, 0.47, 0.45, 0.43, + 0.41, 0.4, 0.38, 0.36, 0.34, 0.33, 0.31, 0.29, 0.28, 0.27, + 0.25, 0.24, 0.23, 0.23, 0.23, 0.22, 0.22, 0.22, 0.22, 0.22, + 0.22, 0.22, 0.22, 0.23, 0.23, 0.24, 0.24, 0.25, 0.26, 0.26, + 0.27, 0.27, 0.28, 0.28, 0.28, 0.29, 0.29, 0.3, 0.3, 0.31, + 0.33, 0.34, 0.35, 0.36, 0.38, 0.39, 0.4, 0.41, 0.43, 0.44, + 0.45, 0.46, 0.47, 0.48, 0.49, 0.49, 0.5, 0.51, 0.51, 0.52, + 0.52, 0.53, 0.53, 0.53, 0.54 + } + }, + /* TCS07 2.5 P 6/8 Y6/4 Light violet */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.15, 0.18, 0.22, 0.29, 0.38, 0.46, 0.52, 0.55, 0.55, 0.56, + 0.56, 0.56, 0.56, 0.56, 0.56, 0.55, 0.54, 0.54, 0.52, 0.51, + 0.49, 0.47, 0.45, 0.43, 0.41, 0.39, 0.36, 0.34, 0.32, 0.31, + 0.3, 0.29, 0.28, 0.27, 0.27, 0.26, 0.26, 0.26, 0.26, 0.26, + 0.26, 0.26, 0.26, 0.25, 0.25, 0.26, 0.27, 0.28, 0.3, 0.32, + 0.34, 0.36, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, + 0.45, 0.46, 0.46, 0.47, 0.47, 0.47, 0.47, 0.48, 0.48, 0.49, + 0.5, 0.5, 0.51, 0.52, 0.53, 0.53, 0.54, 0.55, 0.55, 0.56, + 0.57, 0.57, 0.58, 0.58, 0.58, 0.58, 0.59, 0.59, 0.59, 0.59, + 0.59, 0.59, 0.59, 0.59, 0.59 + } + }, + /* TCS08 10 P 6/8 Light reddish purple */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.08, 0.08, 0.08, 0.09, 0.1, 0.13, 0.17, 0.24, 0.32, 0.42, + 0.46, 0.48, 0.49, 0.49, 0.48, 0.47, 0.46, 0.45, 0.44, 0.43, + 0.41, 0.4, 0.38, 0.37, 0.35, 0.34, 0.33, 0.31, 0.3, 0.29, + 0.28, 0.28, 0.27, 0.26, 0.26, 0.25, 0.25, 0.25, 0.25, 0.26, + 0.26, 0.27, 0.27, 0.27, 0.28, 0.28, 0.3, 0.32, 0.35, 0.38, + 0.43, 0.48, 0.53, 0.57, 0.6, 0.63, 0.65, 0.66, 0.68, 0.69, + 0.69, 0.7, 0.71, 0.71, 0.71, 0.72, 0.72, 0.72, 0.72, 0.72, + 0.72, 0.72, 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, + 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, 0.73, + 0.73, 0.73, 0.73, 0.73, 0.73 + } + }, + /* TCS09 4.5 R 4/13 Strong red */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.069, 0.072, 0.073, 0.070, 0.066, 0.062, 0.058, 0.055, 0.052, 0.052, + 0.051, 0.050, 0.050, 0.049, 0.048, 0.047, 0.046, 0.044, 0.042, 0.041, + 0.038, 0.035, 0.033, 0.031, 0.030, 0.029, 0.028, 0.028, 0.028, 0.029, + 0.030, 0.030, 0.031, 0.031, 0.032, 0.032, 0.033, 0.034, 0.035, 0.037, + 0.041, 0.044, 0.048, 0.052, 0.060, 0.076, 0.102, 0.136, 0.190, 0.256, + 0.336, 0.418, 0.505, 0.581, 0.641, 0.682, 0.717, 0.740, 0.758, 0.770, + 0.781, 0.790, 0.797, 0.803, 0.809, 0.814, 0.819, 0.824, 0.828, 0.830, + 0.831, 0.833, 0.835, 0.836, 0.836, 0.837, 0.838, 0.839, 0.839, 0.839, + 0.839, 0.839, 0.839, 0.839, 0.839, 0.839, 0.839, 0.839, 0.839, 0.839, + 0.838, 0.837, 0.837, 0.836, 0.836 + } + }, + /* TCS10 5 Y 8/10 Strong yellow */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.04, 0.04, 0.05, 0.05, 0.05, 0.05, 0.06, 0.06, 0.07, 0.07, + 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.08, 0.08, 0.08, 0.09, + 0.1, 0.1, 0.11, 0.13, 0.14, 0.16, 0.19, 0.22, 0.26, 0.31, + 0.37, 0.42, 0.47, 0.51, 0.55, 0.58, 0.61, 0.63, 0.65, 0.67, + 0.68, 0.69, 0.69, 0.7, 0.7, 0.7, 0.71, 0.71, 0.71, 0.71, + 0.71, 0.71, 0.71, 0.71, 0.71, 0.71, 0.71, 0.71, 0.72, 0.72, + 0.72, 0.72, 0.73, 0.73, 0.73, 0.74, 0.74, 0.74, 0.75, 0.75, + 0.75, 0.75, 0.75, 0.75, 0.76, 0.76, 0.76, 0.76, 0.76, 0.76, + 0.76, 0.76, 0.76, 0.76, 0.76, 0.76, 0.76, 0.76, 0.76, 0.76, + 0.76, 0.76, 0.76, 0.76, 0.76 + } + }, + /* TCS11 4.5 G 5/8 Strong green */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.13, 0.13, 0.12, + 0.12, 0.11, 0.11, 0.11, 0.1, 0.1, 0.11, 0.11, 0.11, 0.12, + 0.12, 0.13, 0.15, 0.17, 0.19, 0.22, 0.25, 0.29, 0.33, 0.35, + 0.36, 0.35, 0.35, 0.33, 0.31, 0.29, 0.27, 0.25, 0.23, 0.21, + 0.19, 0.17, 0.15, 0.14, 0.13, 0.11, 0.11, 0.1, 0.1, 0.09, + 0.09, 0.09, 0.09, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, + 0.08, 0.08, 0.09, 0.09, 0.1, 0.11, 0.13, 0.14, 0.16, 0.18, + 0.2, 0.22, 0.24, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.31, + 0.32, 0.32, 0.33, 0.33, 0.34, 0.34, 0.35, 0.35, 0.36, 0.37, + 0.37, 0.38, 0.39, 0.4, 0.4 + } + }, + /* TCS12 3 PB 3/11 Strong blue */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.19, 0.18, 0.16, 0.14, 0.12, 0.1, 0.09, 0.08, 0.08, 0.07, + 0.06, 0.07, 0.08, 0.09, 0.12, 0.16, 0.21, 0.26, 0.3, 0.33, + 0.35, 0.35, 0.34, 0.33, 0.31, 0.28, 0.26, 0.23, 0.2, 0.18, + 0.15, 0.13, 0.11, 0.09, 0.08, 0.06, 0.05, 0.04, 0.04, 0.03, + 0.03, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, + 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, + 0.02, 0.02, 0.03, 0.03, 0.04, 0.04, 0.06, 0.07, 0.1, 0.13, + 0.17, 0.21, 0.26, 0.31, 0.35, 0.4, 0.45, 0.49, 0.52, 0.55, + 0.58, 0.6, 0.62, 0.63, 0.65, 0.66, 0.67, 0.67, 0.68, 0.69, + 0.69, 0.69, 0.7, 0.7, 0.7 + } + }, + /* TCS13 5 YR 8/4 Light yellowish pink */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.07, 0.08, 0.08, 0.09, 0.1, 0.13, 0.16, 0.21, 0.26, 0.31, + 0.34, 0.35, 0.36, 0.36, 0.36, 0.37, 0.37, 0.37, 0.37, 0.37, + 0.38, 0.38, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, + 0.46, 0.47, 0.47, 0.47, 0.47, 0.48, 0.48, 0.49, 0.51, 0.53, + 0.55, 0.58, 0.62, 0.65, 0.68, 0.7, 0.72, 0.73, 0.74, 0.74, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, + 0.75, 0.74, 0.74, 0.74, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, + 0.75, 0.75, 0.75, 0.75, 0.75 + } + }, + /* TCS14 5 GY 4/4 Moderate olive green */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.05, 0.05, 0.05, + 0.05, 0.05, 0.05, 0.05, 0.06, 0.06, 0.06, 0.07, 0.08, 0.08, + 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.15, 0.16, 0.15, + 0.15, 0.14, 0.13, 0.13, 0.12, 0.11, 0.11, 0.1, 0.1, 0.1, + 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.08, 0.08, 0.08, 0.08, + 0.09, 0.09, 0.09, 0.1, 0.1, 0.11, 0.12, 0.14, 0.15, 0.17, + 0.19, 0.21, 0.23, 0.24, 0.26, 0.28, 0.29, 0.31, 0.33, 0.34, + 0.35, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.42, 0.43, 0.43, + 0.44, 0.44, 0.45, 0.45, 0.45 + } + }, + /* TCS15 1 YR 6/4 Asian skin */ + { + 95, 360.0, 830.0, /* 95 bands from 360 to 830 nm in 5nm steps */ + 1.0, /* Scale factor */ + + { + 0, 0, 0, 0, 0.13, 0.14, 0.15, 0.15, 0.16, 0.16, + 0.16, 0.17, 0.17, 0.18, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, + 0.24, 0.24, 0.25, 0.25, 0.26, 0.26, 0.27, 0.28, 0.28, 0.29, + 0.3, 0.3, 0.3, 0.29, 0.28, 0.28, 0.27, 0.28, 0.28, 0.29, + 0.29, 0.29, 0.29, 0.28, 0.29, 0.31, 0.35, 0.4, 0.44, 0.47, + 0.49, 0.51, 0.52, 0.54, 0.54, 0.55, 0.56, 0.57, 0.57, 0.58, + 0.58, 0.59, 0.59, 0.59, 0.6, 0.6, 0.61, 0.61, 0.61, 0.61, + 0.62, 0.62, 0.62, 0.62, 0.62, 0.61, 0.61, 0.61, 0.61, 0.61, + 0.61, 0.61, 0.61, 0.61, 0.61, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + } + } +}; + + +/* -------------------------------- */ +/* Fluorescent Whitening Agent Data */ + +/* Generic stimulation/exitation spectrum, used in FWA. */ +/* This is also used to estimate the UV content of an illuminant, */ +/* by its FWA effect (illumread) */ +static xspect FWA1_stim = { + 14, 290.0, 420.0, /* 14 bands from 290 to 420 nm in 10nm steps */ + 1.0, /* Scale factor */ + { +/* 290 */ 0.000000, +/* 300 */ 0.075000, 0.158000, 0.228000, 0.318638, 0.393663, +/* 350 */ 0.460003, 0.524409, 0.550955, 0.540374, 0.497947, +/* 400 */ 0.412503, 0.265935, 0.000000 + } +}; + +/* !!! This is not normally used !!! */ +#ifdef STOCKFWA /* Use table shape as FWA basis, rather than estimating from spectrum. */ + +/* Generic emmission spectrum */ +static xspect FWA1_emit = { + 17, 390.0, 550.0, /* 17 bands from 390 to 550 nm in 10nm steps */ + 1.0, /* Scale factor */ + { +#ifdef NEVER +/* 390 */ 0.00000, +/* 400 */ 0.08989, 0.27831, 0.45278, 0.494, 0.496, +/* 450 */ 0.36872, 0.30495, 0.226, 0.1676, 0.1216, +/* 500 */ 0.08515, 0.06877, 0.04930, 0.0246, 0.0123, +/* 550 */ 0.00000 +#else + /* Hacked */ +/* 390 */ 0.00000, +/* 400 */ 0.01089, 0.15000, 0.20278, 0.374, 0.496, +/* 450 */ 0.38000, 0.27495, 0.186, 0.1376, 0.1016, +/* 500 */ 0.08515, 0.06877, 0.04930, 0.0246, 0.0123, +/* 550 */ 0.00000 +#endif + } +}; + +#endif /* STOCKFWA */ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* save a set of spectrum to a CGATS file */ +/* type 0 = SPECT, 1 = CMF */ +/* Return NZ on error */ +int write_nxspect(char *fname, xspect *sp, int nspec, int type) { + char buf[100]; + time_t clk = time(0); + struct tm *tsp = localtime(&clk); + char *atm = asctime(tsp); /* Ascii time */ + cgats *ocg; /* output cgats structure */ + cgats_set_elem *setel; /* Array of set value elements */ + int i, j; + + /* Setup output cgats file */ + ocg = new_cgats(); /* Create a CGATS structure */ + if (type != 0) + ocg->add_other(ocg, "CMF"); /* our special type is spectral color matching func */ + else + ocg->add_other(ocg, "SPECT"); /* our special type is spectral power or reflectance */ + ocg->add_table(ocg, tt_other, 0); /* Start the first table */ + + ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Spectral power/reflectance information",NULL); + ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll CMS", NULL); + atm[strlen(atm)-1] = '\000'; /* Remove \n from end */ + ocg->add_kword(ocg, 0, "CREATED",atm, NULL); + + sprintf(buf,"%d", sp->spec_n); + ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL); + sprintf(buf,"%f", sp->spec_wl_short); + ocg->add_kword(ocg, 0, "SPECTRAL_START_NM",buf, NULL); + sprintf(buf,"%f", sp->spec_wl_long); + ocg->add_kword(ocg, 0, "SPECTRAL_END_NM",buf, NULL); + sprintf(buf,"%f", sp->norm); + ocg->add_kword(ocg, 0, "SPECTRAL_NORM",buf, NULL); + + /* Generate fields for spectral values */ + for (i = 0; i < sp->spec_n; i++) { + int nm; + + /* Compute nearest integer wavelength */ + nm = (int)(XSPECT_XWL(sp, i) + 0.5); + sprintf(buf,"SPEC_%03d",nm); + ocg->add_field(ocg, 0, buf, r_t); + } + + if ((setel = (cgats_set_elem *)malloc(sizeof(cgats_set_elem) * sp->spec_n)) == NULL) { + ocg->del(ocg); + return 1; + } + + for (j = 0; j < nspec; j++) { + for (i = 0; i < sp[j].spec_n; i++) { + setel[i].d = sp[j].spec[i]; + } + ocg->add_setarr(ocg, 0, setel); + } + + if (ocg->write_name(ocg, fname)) { + DBGF((DBGA,"CGATS file write error : %s\n",ocg->err)); + return 1; + } + + free(setel); + ocg->del(ocg); /* Clean up */ + + return 0; +} + +/* restore a set of spectrum from a CGATS file. */ +/* Up to nspec will be restored starting at offset off.. */ +/* The number restored from the file will be written to *nret */ +/* type: 0 = any, mask: 1 = SPECT, 2 = CMF, 4 = ccss */ +/* Return NZ on error */ +/* (Would be nice to return an error message!) */ +int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int type) { + cgats *icg; /* input cgats structure */ + char buf[100]; + int sflds[XSPECT_MAX_BANDS]; + int i, j, ii; + xspect proto; + + /* Open and look at the spectrum file */ + if ((icg = new_cgats()) == NULL) { /* Create a CGATS structure */ + DBG("new_cgats() failed"); + icg->del(icg); + return 1; + } + if (type == 0) { + icg->add_other(icg, ""); /* Allow any signature file */ + } else { + if (type & 1) + icg->add_other(icg, "SPECT"); /* Spectrum file */ + if (type & 2) + icg->add_other(icg, "CMF"); /* Color Matching Functions */ + if (type & 4) + icg->add_other(icg, "CCSS"); /* Color Correction Spectral Samples */ + } + + if (icg->read_name(icg, fname)) { + DBGF((DBGA,"CGATS file read error : %s\n",icg->err)); + icg->del(icg); + return 1; + } + + if (icg->ntables != 1) { + DBG("Input file doesn't contain exactly one table\n"); + icg->del(icg); + return 1; + } + + if ((ii = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) < 0) { + DBG ("Input file doesn't contain keyword SPECTRAL_BANDS\n"); + icg->del(icg); + return 1; + } + proto.spec_n = atoi(icg->t[0].kdata[ii]); + if ((ii = icg->find_kword(icg, 0, "SPECTRAL_START_NM")) < 0) { + DBG("Input file doesn't contain keyword SPECTRAL_START_NM\n"); + icg->del(icg); + return 1; + } + proto.spec_wl_short = atof(icg->t[0].kdata[ii]); + if ((ii = icg->find_kword(icg, 0, "SPECTRAL_END_NM")) < 0) { + DBG("Input file doesn't contain keyword SPECTRAL_END_NM\n"); + icg->del(icg); + return 1; + } + proto.spec_wl_long = atof(icg->t[0].kdata[ii]); + + if ((ii = icg->find_kword(icg, 0, "SPECTRAL_NORM")) < 0) { + DBG("Input file doesn't contain keyword SPECTRAL_NORM - assuming 1.0\n"); + proto.norm = 1.0; + } else { + proto.norm = atof(icg->t[0].kdata[ii]); + } + + /* Find the fields for spectral values */ + for (i = 0; i < proto.spec_n; i++) { + int nm, fi; + + /* Compute nearest integer wavelength */ + nm = (int)(XSPECT_XWL(&proto, i) + 0.5); + sprintf(buf,"SPEC_%03d",nm); + + if ((fi = icg->find_field(icg, 0, buf)) < 0) { + DBGF((DBGA,"Input file doesn't contain field %s\n",buf)); + icg->del(icg); + return 1; + } + + if (icg->t[0].ftype[fi] != r_t) { + DBGF((DBGA,"Field %s in specrum is wrong type - should be a float\n",buf)); + icg->del(icg); + return 1; + } + sflds[i] = fi; + } + + /* Read all the spectra */ + for (j = off; j < nspec && j < icg->t[0].nsets; j++) { + + XSPECT_COPY_INFO(&sp[j], &proto); + + for (i = 0; i < proto.spec_n; i++) { + sp[j].spec[i] = *((double *)icg->t[0].fdata[j][sflds[i]]); + } + } + if (nret != NULL) + *nret = j - off; + + icg->del(icg); + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* save a spectrum to a CGATS file */ +/* Return NZ on error */ +int write_xspect(char *fname, xspect *sp) { + return write_nxspect(fname, sp, 1, 0); +} + +/* restore a spectrum from a CGATS file */ +/* Return NZ on error */ +/* (Would be nice to return an error message!) */ +int read_xspect(xspect *sp, char *fname) { + int rv, nret; + + if ((rv = read_nxspect(sp, fname, &nret, 0, 1, 1)) != 0) + return rv; + if (nret != 1) { + DBG("Didn't read one spectra\n"); + return 1; + } + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* save a set of 3 spectrum to a CGATS CMF file */ +/* Return NZ on error */ +int write_cmf(char *fname, xspect sp[3]) { + return write_nxspect(fname, sp, 3, 1); +} + +/* restore a spectrum from a CGATS file */ +/* Return NZ on error */ +/* (Would be nice to return an error message!) */ +int read_cmf(xspect sp[3], char *fname) { + int rv, nret; + + if ((rv = read_nxspect(sp, fname, &nret, 0, 3, 2)) != 0) + return rv; + if (nret != 3) { + DBG("Didn't read three spectra\n"); + return 1; + } + + return 0; +} + +/* ------------- */ +#endif /* !SALONEINSTLIB */ + + +/* Get a raw 3rd order polinomial interpolated spectrum value. */ +/* Return NZ if value is valid, Z and last valid value */ +/* if outside the range */ +/* NOTE: Returned value isn't normalised by sp->norm */ +static int getval_raw_xspec_poly3(xspect *sp, double *rv, double xw) { + int i, rc = 1; + double spcing, f; +#ifdef NEVER + double w1, w2, w3; /* For Hermite curves */ +#endif + double y[4], yw; + double x[4]; + + if (xw < sp->spec_wl_short) { + xw = sp->spec_wl_short; + rc = 0; + } + + if (xw > sp->spec_wl_long) { + xw = sp->spec_wl_long; + rc = 0; + } + + /* Compute fraction 0.0 - 1.0 out of known spectrum */ + spcing = (sp->spec_wl_long - sp->spec_wl_short)/(sp->spec_n-1.0); + f = (xw - sp->spec_wl_short) / (sp->spec_wl_long - sp->spec_wl_short); + f *= (sp->spec_n - 1.0); + i = (int)floor(f); /* Base grid coordinate */ + + if (i < 0) /* Limit to valid cube base index range */ + i = 0; + else if (i > (sp->spec_n - 2)) + i = (sp->spec_n - 2); + + /* Setup the surrounding values */ + x[0] = sp->spec_wl_short + (i-1) * spcing; + if (i == 0) + y[0] = sp->spec[i]; + else + y[0] = sp->spec[i-1]; + x[1] = sp->spec_wl_short + i * spcing; + y[1] = sp->spec[i]; + x[2] = sp->spec_wl_short + (i+1) * spcing; + y[2] = sp->spec[i+1]; + x[3] = sp->spec_wl_short + (i+2) * spcing; + if ((i+2) < sp->spec_n) + y[3] = sp->spec[i+2]; + else + y[3] = sp->spec[i+1]; + +#ifndef NEVER + /* Compute interpolated value using Lagrange: */ + yw = y[0] * (xw-x[1]) * (xw-x[2]) * (xw-x[3])/((x[0]-x[1]) * (x[0]-x[2]) * (x[0]-x[3])) + + y[1] * (xw-x[0]) * (xw-x[2]) * (xw-x[3])/((x[1]-x[0]) * (x[1]-x[2]) * (x[1]-x[3])) + + y[2] * (xw-x[0]) * (xw-x[1]) * (xw-x[3])/((x[2]-x[0]) * (x[2]-x[1]) * (x[2]-x[3])) + + y[3] * (xw-x[0]) * (xw-x[1]) * (xw-x[2])/((x[3]-x[0]) * (x[3]-x[1]) * (x[3]-x[2])); +#else + /* Use Hermite curves */ + y[0] = 0.5 * (y[2] - y[0]); /* Convert to tangent */ + y[3] = 0.5 * (y[3] - y[1]); /* Not sure about the best weighting here ... */ + + w1 = f - (double)i; /* Interpolation weighting factor, 0.0 - 1.0 */ + w2 = w1 * w1; + w3 = w2 * w1; + yw = y[0] * (w3 - 2.0 * w2 + w1) + + y[1] * (2.0 * w3 - 3.0 * w2 + 1.0) + + y[2] * (-2.0 * w3 + 3.0 * w2) + + y[3] * (w3 - w2); +#endif + +#ifdef NEVER // ~~99 + /* Calibration issues or interpolation overshoot can give -ve values, */ + /* so protect against this. */ + /* On the other hand, not allowing -ve values wrecks black level */ + /* by not averaging out the noise. */ + if (yw < 0.0) + yw = 0.0; +#endif /* NEVER */ + + *rv = yw; + return rc; +} + +/* Get a raw linearly interpolated spectrum value. */ +/* Return NZ if value is valid, Z and last valid value */ +/* if outside the range */ +/* NOTE: Returned value isn't normalised by sp->norm */ +static int getval_raw_xspec_lin(xspect *sp, double *rv, double wl) { + int i, rc = 1; + double f, w; + + if (wl < sp->spec_wl_short) { + wl = sp->spec_wl_short; + rc = 0; + } + + if (wl > sp->spec_wl_long) { + wl = sp->spec_wl_long; + rc = 0; + } + + /* Compute fraction 0.0 - 1.0 out of known spectrum */ + f = (wl - sp->spec_wl_short) / (sp->spec_wl_long - sp->spec_wl_short); + f *= (sp->spec_n - 1.0); + i = (int)floor(f); /* Base grid coordinate */ + + if (i < 0) /* Limit to valid cube base index range */ + i = 0; + else if (i > (sp->spec_n - 2)) + i = (sp->spec_n - 2); + + w = f - (double)i; /* Interpolation weighting factor */ + + /* Compute interpolated value */ + *rv = (1.0 - w) * sp->spec[i] + w * sp->spec[i+1]; + +#ifdef NEVER + /* Calibration issues or interpolation overshoot can give -ve values, */ + /* so protect against this. */ + /* On the other hand, not allowing -ve values wrecks black level */ + /* by not averaging out the noise. */ + if (*rv < 0.0) + *rv = 0.0; +#endif /* NEVER */ + + return rc; +} + +#ifdef NEVER /* Nearest neighbor resampler, for testing */ +/* Get a raw nearest-neighbor interpolated spectrum value. */ +/* Return NZ if value is valid, Z and last valid value */ +/* if outside the range */ +/* NOTE: Returned value isn't normalised by sp->norm */ +static int getval_raw_xspec_nn(xspect *sp, double *rv, double wl) { + int i, rc = 1; + double f; + + if (wl < sp->spec_wl_short) { + wl = sp->spec_wl_short; + rc = 0; + } + + if (wl > sp->spec_wl_long) { + wl = sp->spec_wl_long; + rc = 0; + } + + /* Compute fraction 0.0 - 1.0 out of known spectrum */ + f = (wl - sp->spec_wl_short) / (sp->spec_wl_long - sp->spec_wl_short); + f *= (sp->spec_n - 1.0); + i = (int)floor(f + 0.5); /* Base grid coordinate */ + + if (i < 0) /* Limit to valid cube base index range */ + i = 0; + else if (i > (sp->spec_n - 1)) + i = (sp->spec_n - 1); + + /* Compute interpolated value */ + *rv = sp->spec[i]; + +#ifdef NEVER + /* Calibration issues or interpolation overshoot can give -ve values, */ + /* so protect against this. */ + /* On the other hand, not allowing -ve values wrecks black level */ + /* by not averaging out the noise. */ + if (*rv < 0.0) + *rv = 0.0; +#endif /* NEVER */ + + return rc; +} +#endif /* NEVER */ + +/* Call the appropriate interpolation routine */ +/* Return NZ if value is valid, Z and last valid value */ +/* if outside the range */ +/* NOTE: Returned value isn't normalised by sp->norm */ +static int getval_raw_xspec(xspect *sp, double *rv, double wl) { + double spcg = (sp->spec_wl_long - sp->spec_wl_short)/(sp->spec_n-1.0); + + if (spcg < 5.01) { + return getval_raw_xspec_lin(sp, rv, wl); + } else { + return getval_raw_xspec_poly3(sp, rv, wl); + } +} + +/* Get a (normalised) linearly or poly interpolated spectrum value. */ +/* Return NZ if value is valid, Z and last valid value */ +/* if outside the range */ +static int getval_xspec(xspect *sp, double *rv, double wl) { + int sv = getval_raw_xspec(sp, rv, wl); + *rv /= sp->norm; + return sv; +} + +/* Public function to get a spectrum value. */ +/* Return a spectrum value at the given wavelenth. It */ +/* may have been interpolated or extrapolated. */ +/* Returned value isn't normalised by sp->norm */ +double value_xspect(xspect *sp, double wl) { + double rv; + getval_raw_xspec(sp, &rv, wl); + return rv; +} + +/* Get a (normalised) linearly interpolated spectrum value. */ +/* Return NZ if value is valid, Z and last valid value */ +/* if outside the range */ +static int getval_lxspec(xspect *sp, double *rv, double wl) { + int sv = getval_raw_xspec_lin(sp, rv, wl); + *rv /= sp->norm; + return sv; +} + +/* De-noramlize and set normalisation factor to 1.0 */ +void xspect_denorm(xspect *sp) { + int i; + + for (i = 0; i < sp->spec_n; i++) { + sp->spec[i] /= sp->norm; + } + sp->norm = 1.0; +} + +#ifndef SALONEINSTLIB +/* Convert from one xspect type to another (targ type) */ +/* Linear or polinomial interpolation will be used as appropriate */ +/* (converted to targ norm too) */ +void xspect2xspect(xspect *dst, xspect *targ, xspect *src) { + xspect dd; + int i; + + dd.spec_n = targ->spec_n; + dd.spec_wl_short = targ->spec_wl_short; + dd.spec_wl_long = targ->spec_wl_long; + dd.norm = targ->norm; + + if (targ->spec_n != src->spec_n + || targ->spec_wl_short != src->spec_wl_short + || targ->spec_wl_long != src->spec_wl_long) { + for (i = 0; i < targ->spec_n; i++) { + double ww = XSPECT_XWL(targ, i); + getval_raw_xspec(src, &dd.spec[i], ww); + } + } else { + for (i = 0; i < targ->spec_n; i++) + dd.spec[i] = src->spec[i]; + } + if (targ->norm != src->norm) { + for (i = 0; i < targ->spec_n; i++) + dd.spec[i] *= targ->norm/src->norm; + } + *dst = dd; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* 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 Y of the input spectrum. */ +void xsp_setUV(xspect *out, xspect *in, double uvlevel) { + int i, xs, xe; + double ww, avg; + xspect cin; /* Copy of in */ + + cin = *in; + + /* Compute the average of the input spetrum */ + for (avg = 0.0, i = 0; i < cin.spec_n; i++) + avg += cin.spec[i]; + avg /= cin.spec_n; + + if (avg < 1e-5) /* Make it do something with 0.0 */ + avg = 1e-5; + + /* Copy and Extend the range */ + *out = cin; + i = (int)floor(XSPECT_XDIX(out, FWA1_stim.spec_wl_short)); + ww = XSPECT_XWL(out, i); + if (i < 0) + out->spec_n -= i; + out->spec_wl_short = ww; + + /* Copy from input and merge in the UV */ + for (i = 0; i < out->spec_n; i++) { + double inv, uvv, bl, nbl, outv; + + ww = XSPECT_XWL(out, i); + getval_raw_xspec_lin(&cin, &inv, ww); + getval_raw_xspec_lin(&FWA1_stim, &uvv, ww); + + /* Taper measured illum out */ + bl = (ww - FWA1_stim.spec_wl_short)/(FWA1_stim.spec_wl_long - FWA1_stim.spec_wl_short); + bl = bl < 0.0 ? 0.0 : (bl > 1.0 ? 1.0 : bl); + inv *= bl; + + /* Add/subtract UV in */ + outv = inv + uvv * uvlevel * avg;; + + /* Protect against creating negative output */ + if (outv >= out->spec[i]) + out->spec[i] = outv; + } +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Set Media White. This enables extracting and applying the */ +/* colorant reflectance value from/to the meadia. */ +// ~~99 this is confused. ->media is set from ->imedia in fwa setup. +// ~~99 what's going on here ? The API needs fixing. +static int xsp2cie_set_mw(xsp2cie *p, /* this */ +xspect *media /* Spectrum of plain media measured under that instrument */ +) { + p->media = *media; /* Take copy of media white */ + return 0; +} + +/* Extract the colorant reflectance value from the media. Takes FWA */ +/* into account if set. Media white or FWA must be set. */ +static int xsp2cie_extract(xsp2cie *p, /* this */ +xspect *out, /* Extracted colorant refl. spectrum */ +xspect *in /* Spectrum to be converted, normalised by norm */ +) { + int j; + + if (p->media.spec_n == 0) + return 1; + + if (p->media.spec_n != in->spec_n + || p->media.spec_wl_short != in->spec_wl_short + || p->media.spec_wl_long != in->spec_wl_long) + return 1; + + *out = *in; + + /* Divide out the media */ + for (j = 0; j < p->media.spec_n; j++) { + if (p->media.spec[j] < 0.01) + out->spec[j] = in->spec[j] / 0.01; + else + out->spec[j] = in->spec[j] / p->media.spec[j]; + } + + out->norm = in->norm / p->media.norm; + return 0; +} + + +/* Apply the colorant reflectance value from the media. Takes FWA */ +/* into account if set. Media white or FWA must be set. */ +static int xsp2cie_apply(xsp2cie *p, /* this */ +xspect *out, /* Applied refl. spectrum */ +xspect *in /* Colorant reflectance to be applied */ +) { + int j; + + if (p->media.spec_n == 0) + return 1; + + if (p->media.spec_n != in->spec_n + || p->media.spec_wl_short != in->spec_wl_short + || p->media.spec_wl_long != in->spec_wl_long) + return 1; + + *out = *in; + + /* Multiply in the media */ + for (j = 0; j < p->media.spec_n; j++) { + if (p->media.spec[j] < 0.01) + out->spec[j] = in->spec[j] * 0.01; + else + out->spec[j] = in->spec[j] * p->media.spec[j]; + } + + out->norm = in->norm * p->media.norm; + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +static void xsp2cie_fwa_convert(xsp2cie *p, double *out, xspect *in); +static void xsp2cie_fwa_sconvert(xsp2cie *p, xspect *sout, double *out, xspect *in); +static int xsp2cie_fwa_extract(xsp2cie *p, xspect *out, xspect *in); +static int xsp2cie_fwa_apply(xsp2cie *p, xspect *out, xspect *in); + +/* Set Fluorescent Whitening Agent compensation. */ +/* This attempts to compensate for the presense of */ +/* Fluorescent whitner in the media, under the possibly */ +/* different level of UV radiation of the illuminant being */ +/* simulated in the conversion from spectral absorbition */ +/* to CIE values, from the illuminant that the media was */ +/* measured under by the spectrometer. */ +/* Note that the media input spectrum normalisation value is used. */ +/* return nz if error */ + +/* + + Limitations of current FWA model: + + Scattering: The inking model assumes that the inks are purely + absorbtive. If instead they have a significant scattering + component, then the FWA effect will be over estimated, + as it will be assumed that more UV is reaching the substrate + and not being filtered by the colorant. + + Colorant UV transparency: The current model assumes that + the filtering behaviour of the ink can be extrapolated + from the blue reflectance. It could be that inks behave + quite differently, filtering more or less in UV than + they do in blue. Different inks might have different characteristics. + + Solution: A solution would be to add a colorant correction model, + that takes as input the colorant levels. To create the model, + illumread would be augmented to read (say) 50% colorant swatches + as well as white, and use the discrepancy between the non-corrected + FWA spectrum and the actual spectrum under the illuminant to + create the correction model. This could be fine tuned by doing + similar measurements of neutral patches. + */ + +/* + See page 248 of the Proceedings of the IS&T/SID + 11th Color Imaging Conference, November 2003: + "A Practical Approach to Measuring and Modelling Paper Fluorescense + for Improved Colorimetric Characterisation of Printing Processes" + ISBN: 0-89208-248-8 + for more information about the fwa compensation approach. + */ + +static int xsp2cie_set_fwa_imp(xsp2cie *p) { + double ww; + int i, j; + int flag; + double aw = 0.0, bw = 0.0; /* Points wavelength */ + double ar, br; /* Points reflection */ +#ifdef STOCKFWA /* Use table shape */ + double Em; /* Emmision multiplier */ +#endif +#ifdef DOPLOT + double xx[XSPECT_MAX_BANDS]; + double y1[XSPECT_MAX_BANDS]; + double y2[XSPECT_MAX_BANDS]; + double y3[XSPECT_MAX_BANDS]; + double y4[XSPECT_MAX_BANDS]; +#endif /* DOPLOT */ + +#ifdef WRITE_FWA1_STIM + write_xspect("fwa1_stip.sp", &FWA1_stim); +#endif + + DBG("set_fwa started\n"); + + p->bw = 1.0; /* Intergrate over 1nm bands */ + p->oillum = p->illuminant; /* Take copy of observer illuminant */ + xspect_denorm(&p->oillum); + if (p->tillum.spec_n == 0) { /* If not set by set_fwa(), use observer illuminant */ + p->tillum = p->oillum; /* as target/simulated instrument illuminant. */ + } + + /* Compute Y = 1 normalised instrument illuminant spectrum */ + { + double scale = 0.0; + double Iim; /* illuminant multiplier */ + + Iim = 0.0; + for (ww = p->observer[1].spec_wl_short; ww <= p->observer[1].spec_wl_long; ww += p->bw) { + double O, I; + getval_lxspec(&p->iillum, &I, ww); + getval_lxspec(&p->observer[1], &O, ww); + scale += O; /* Integrate Y observer values */ + Iim += O * I; + } + Iim /= scale; /* Scale Y observer to unity */ + + Iim = 1.0/Iim; /* Scale factor to make illuminant integral 1.0 */ + + for (j = 0; j < p->iillum.spec_n; j++) + p->iillum.spec[j] *= Iim; + DBGF((DBGA,"Instrument Illum normal multiplier Iim = %f\n",Iim)); + } + + /* Compute Y = 1 normalised target illuminant spectrum */ + { + double scale; + double Itm; /* illuminant multiplier */ + + scale = 0.0; + Itm = 0.0; + for (ww = p->observer[1].spec_wl_short; ww <= p->observer[1].spec_wl_long; ww += p->bw) { + double O, I; + getval_lxspec(&p->tillum, &I, ww); + getval_lxspec(&p->observer[1], &O, ww); + scale += O; /* Integrate Y observer values */ + Itm += O * I; + } + Itm /= scale; /* Scale Y observer to unity */ + Itm = 1.0/Itm; /* Scale factor to make illuminant integral 1.0 */ + + for (j = 0; j < p->tillum.spec_n; j++) + p->tillum.spec[j] *= Itm; + } + + /* Check if the instrument and target/simulated illuminant are the same. */ + /* If they are, FWA compensation can be bypassed. */ + /* (We check for an almost exact matcg on the assumption that these will */ + /* both be xspect presets) */ +#define DEQ(A, B) (fabs(A - B) < 1e-6) + p->insteqtarget = 0; + if (p->iillum.spec_n == p->tillum.spec_n + && DEQ(p->iillum.spec_wl_short, p->tillum.spec_wl_short) + && DEQ(p->iillum.spec_wl_long, p->tillum.spec_wl_long)) { + for (i = 0; i < p->iillum.spec_n; i++) { + if (!DEQ(p->tillum.spec[i], p->iillum.spec[i])) + break; + } + if (i >= p->iillum.spec_n) { + p->insteqtarget = 1; + DBGF((DBGA,"###### inst equals target illuminant #####\n")); + } + } +#undef DEQ + + /* Compute Y = 1 normalised observer illuminant spectrum */ + { + double scale; + double Itm; /* Target illuminant multiplier */ + + scale = 0.0; + Itm = 0.0; + for (ww = p->observer[1].spec_wl_short; ww <= p->observer[1].spec_wl_long; ww += p->bw) { + double O, I; + getval_lxspec(&p->oillum, &I, ww); + getval_lxspec(&p->observer[1], &O, ww); + scale += O; /* Integrate Y observer values */ + Itm += O * I; + } + Itm /= scale; /* Scale Y observer to unity */ + Itm = 1.0/Itm; /* Scale factor to make illuminant integral 1.0 */ + + for (j = 0; j < p->oillum.spec_n; j++) + p->oillum.spec[j] *= Itm; + } + + /* Estimate the amount of generic FWA in the media. */ + /* and also compute an estimated media minus FWA spectrum */ + /* by creating a target white line from the media spectrum */ + + /* This is quite good for "normal" media, which has a fairly */ + /* flat underlying (non FWA) response, but doesn't work so */ + /* well for meadia that rolls off at short wavelengths and uses */ + /* FWA to compensate for this. */ + + /* Find darkest point between 450 and 510nm */ + ar = 1e6; + for (ww = 450.0; ww <= 510.0; ww += p->bw) { + double rr; + getval_lxspec(&p->imedia, &rr, ww); + DBGF((DBGA,"media %f = %f\n",ww,rr)); + + if (rr < ar) { + aw = ww; + ar = rr; + } + } + + /* Find lightest point between A point+70 and 650 */ + br = -1.0; + for (ww = aw+70.0; ww <= 630.0; ww += p->bw) { + double rr; + getval_lxspec(&p->imedia, &rr, ww); + DBGF((DBGA,"media %f = %f\n",ww,rr)); + if (rr > br) { + bw = ww; + br = rr; + } + } + if (br < ar) + br = ar; /* Make flat rather than slope to the right */ + + DBGF((DBGA,"Cuttoff line params: A = %f %f, B = %f %f\n", aw, ar, bw, br)); + +#ifdef STOCKFWA /* Use table shape as FWA basis */ + + /* Compute an Em that explains the bump over the flat line */ + Em = 0.0; + for (ww = FWA1_emit.spec_wl_short; ww <= (FWA1_emit.spec_wl_long - 100.0); ww += p->bw) { + double Rl, rr; + + /* Compute value of line at this wavelength */ + Rl = (ww - aw)/(bw - aw) * (br - ar) + ar; + + getval_lxspec(&p->imedia, &rr, ww); /* Media at this point */ + + if (rr > Rl) { /* Media is over the line */ + double Ii; + double Eu; + double mm; + + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised illuminant at this wavelength */ + if (Ii < 1e-9) + Ii = 1e-9; + getval_lxspec(&FWA1_emit, &Eu, ww); /* FWA emission at this wavelength */ + mm = ((rr - Rl) * Ii)/Eu; + if (mm > Em) { + DBGF((DBGA,"Update Em to %f at %fnm for target %f\n",mm,ww,rr-Rl)); + Em = mm; /* Greater multiplier to explain bump */ + } + } + } + DBGF((DBGA,"Em = %f\n",Em)); + + /* Setup spectrum to hold result over exected range */ + /* and base media reflectance */ + p->media = p->imedia; /* Take copy of media white */ + p->emits = p->imedia; /* Structure copy */ + xspect_denorm(&p->media); /* Set norm to 1.0 */ + xspect_denorm(&p->emits); + + /* Copy emission spectra that explains bump over line */ + /* plus estimated media without FWA spectrum. */ + for (i = 0; i < p->media.spec_n; i++) { + double Eu, Ii; + double Rm, Rmb; + +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(i); +#endif + + ww = (p->media.spec_wl_long - p->media.spec_wl_short) + * ((double)i/(p->media.spec_n-1.0)) + p->media.spec_wl_short; + + getval_lxspec(&FWA1_emit, &Eu, ww); /* FWA emission at this wavelength */ + Eu *= Em; + p->emits.spec[i] = p->emits.norm * Eu; /* Remember FWA spectrum */ + + Rm = p->media.spec[i]/p->media.norm; /* Media at this point */ + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised illuminant at this wavelength */ + if (Ii < 1e-9) + Ii = 1e-9; + Rm *= Ii; /* Light reflected from media */ + + Rmb = Rm - Eu; /* Convert media to base media */ + if (Rmb < 0.01) + Rmb = 0.01; /* This would be silly */ + p->media.spec[i] = p->media.norm * Rmb/Ii; /* Convert media to base media */ + DBGF((DBGA,"ww %f, Eu %f, Rm %f, Rmb %f\n",ww, Eu, Rm, Rmb)); + + } + /* Prevent silliness */ + p->emits.spec[0] = 0.0; + p->emits.spec[p->emits.spec_n-1] = 0.0; + +#else /* Not STOCK_FWA */ + /* Setup spectrum to hold result over exected range */ + /* and base media reflectance */ + p->media = p->imedia; /* Take copy of media white */ + p->emits = p->imedia; /* Structure copy */ + xspect_denorm(&p->media); /* Set norm to 1.0 */ + xspect_denorm(&p->emits); + + /* Compute emission spectra that explains bump over line */ + /* plus estimated media without FWA spectrum. */ + /* Do this from long to short to allow for "filter off" trigger */ + flag = 1; /* Filter is active */ + for (i = (p->media.spec_n-1); i >= 0; i--) { + double Rl, Rm, Rmb; + double fwi = 25.0; /* Smoothing filter width +/- */ + int fres = 5; /* Smoothing filter resolution */ + double tweight; + +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(i); +#endif + /* Wavelength we're generating */ + ww = (p->media.spec_wl_long - p->media.spec_wl_short) + * ((double)i/(p->media.spec_n-1.0)) + p->media.spec_wl_short; + + /* Compute the base media estimate at this point from */ + /* the triangular smoothed filter of the smaller of the */ + /* measured media and the line */ + tweight = 0.0; + Rmb = 0.0; + for (j = -fres; j <= fres; j++) { + double fww, weight; + +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(j); +#endif + fww = ww + (double)j/(double)fres * fwi; + weight = 1.0 - fabs((double)j/(double)fres); + + Rl = (fww - aw)/(bw - aw) * (br - ar) + ar; /* Line at this point */ + getval_lxspec(&p->imedia, &Rm, fww); /* Media at this point */ + + if (Rm < Rl) + Rl = Rm; + + Rmb += Rl * weight; + tweight += weight; + } + Rmb /= tweight; /* Base media estimate */ + + /* Compute value of line and media at this wavelength */ + Rl = (ww - aw)/(bw - aw) * (br - ar) + ar; /* Line at this point */ + + getval_lxspec(&p->imedia, &Rm, ww); /* Media at this point */ + DBGF((DBGA,"ww %f, Rl %f, Rm %f, Rmb %f\n",ww,Rl,Rm,Rmb)); + + /* Stop following the filter once the actual media has crossed over it */ + if (ww < 450.0 && Rm < Rmb) + flag = 0; + + /* Don't follow smoothed at long wl or if media has caught up to filtered */ + if (flag == 0 || ww > 570.0) + Rmb = Rm; + + if (Rm > Rmb && ww <= 570.0) { /* Media is over the line */ + double Ii; + + p->media.spec[i] = p->media.norm * Rmb; /* Convert media to base media */ + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised illuminant at this wavelength */ + if (Ii < 1e-9) + Ii = 1e-9; + + p->emits.spec[i] = p->emits.norm * (Rm - Rmb) * Ii; + DBGF((DBGA,"ww %fnm, Rm %f, Rmb %f, Eu %f\n",ww, Rm, Rmb, p->emits.spec[i]/p->emits.norm)); + + } else { + p->emits.spec[i] = 0.0; + } +#ifdef DOPLOT + xx[i] = ww; + y1[i] = Rl; + y2[i] = Rm; + y3[i] = Rmb; + y4[i] = p->emits.spec[i]/p->emits.norm; +#endif + } +#ifdef DOPLOT + printf("Estimated vs. real media spectrum calculation\n"); + do_plot6(xx,y1,y2,y3,y4,NULL,NULL,p->media.spec_n); +#endif + /* Prevent silliness */ + p->emits.spec[0] = 0.0; + p->emits.spec[p->emits.spec_n-1] = 0.0; +#endif /* !STOCKFWA */ + + /* Compute level of UV stimulating FWA */ + p->Sm = 0.0; + for (ww = FWA1_stim.spec_wl_short; ww <= FWA1_stim.spec_wl_long; ww += p->bw) { + double Ii; + double Su; + + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised illuminant at this wavelength */ + if (Ii < 1e-9) + Ii = 1e-9; + getval_lxspec(&FWA1_stim, &Su, ww); /* FWA stimulation profile at this wavelength */ + p->Sm += Su * Ii; + } + DBGF((DBGA,"Sm = %f\n",p->Sm)); + + /* Compute FWA content of this media, for information purposes */ + p->FWAc = 0.0; + for (ww = p->emits.spec_wl_short; ww <= p->emits.spec_wl_long; ww += p->bw) { + double Eu; + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + p->FWAc += Eu; + } + p->FWAc /= p->Sm; /* Divided by stimulation */ + DBGF((DBGA,"FWA content = %f\n",p->FWAc)); + + /* Turn on FWA compensation */ + p->convert = xsp2cie_fwa_convert; + p->sconvert = xsp2cie_fwa_sconvert; + p->extract = xsp2cie_fwa_extract; + p->apply = xsp2cie_fwa_apply; + +#if defined(DOPLOT) || defined(DEBUG) + /* Print the estimated vs. real media spectrum */ + for (i = 0, ww = p->media.spec_wl_short; ww <= p->media.spec_wl_long; ww += 1.0, i++) { + double Rm; /* Real media reflectance */ + double Rmb; /* Media reflectance without FWA */ + double Rmd; /* Estimated media reflectance with FWA */ + double Ii; + double Eu; + + getval_lxspec(&p->imedia, &Rm, ww); /* Media at this point */ + getval_lxspec(&p->media, &Rmb, ww); /* Base Media */ + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised illuminant at this wavelength */ + if (Ii < 1e-9) + Ii = 1e-9; + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + + Rmd = ((Ii * Rmb) + Eu)/Ii; /* Base Media plus FWA */ + DBGF((DBGA,"%fnm, is %f should be %f, Rmb %f, Eu %f\n",ww, Rm, Rmd, Rmb, Eu)); + +#ifdef DOPLOT + xx[i] = ww; +// y1[i] = Rm; + y1[i] = Rmb; + y2[i] = Eu; + y3[i] = Rmd; +#endif + } +#ifdef DOPLOT + printf("Estimated vs. real media spectrum\n"); + do_plot(xx,y1,y2,y3,i); +#endif +#endif /* DEBUG */ + + DBGF((DBGA,"We're done\n")); + return 0; +} + +/* Set FWA given instrument illuminant and white media measurement */ +static int xsp2cie_set_fwa(xsp2cie *p, /* this */ +xspect *iillum, /* Spectrum of instrument illuminent */ +xspect *tillum, /* Spectrum of target/simulated instrument illuminant */ + /* NULL to use observer illuminant. */ +xspect *media /* Spectrum of plain media measured under that instrument */ +) { + p->iillum = *iillum; /* Take copy of instrument illuminant */ + xspect_denorm(&p->iillum); /* Remove normalisation factor */ + if (tillum != NULL) { + p->tillum = *tillum; /* Take copy of target/simulated instrument illuminant */ + xspect_denorm(&p->tillum); /* Remove normalisation factor */ + } else { + p->tillum.spec_n = 0; + } + p->imedia = *media; /* Take copy of measured media */ + + return xsp2cie_set_fwa_imp(p); +} + +/* Set FWA given updated conversion illuminant. */ +/* We assume that xsp2cie_set_fwa has been called first. */ +static int xsp2cie_update_fwa_custillum( +xsp2cie *p, /* this */ +xspect *tillum, /* Spectrum of target/simulated instrument illuminant, */ + /* NULL to use previous set_fwa() value. */ +xspect *custIllum /* Spectrum of observer illuminant */ +) { + if (tillum != NULL) { + p->tillum = *tillum; /* Take copy of target/simulated instrument illuminant */ + xspect_denorm(&p->tillum); /* Remove normalisation factor */ + } + p->illuminant = *custIllum; + + return xsp2cie_set_fwa_imp(p); +} + +/* Get Fluorescent Whitening Agent compensation information */ +/* return NZ if error */ +static void xsp2cie_get_fwa_info( +xsp2cie *p, +double *FWAc) { + + if (FWAc != NULL) + *FWAc = p->FWAc; +} + +/* Do the FWA corrected spectral to CIE conversion. */ +/* If the instrument and target illuminant are the same, */ +/* then FWA correction is bypassed. */ +/* Note that the input spectrum normalisation value is used. */ +/* Emissive spectral values are assumed to be in mW/nm, and sampled */ +/* rather than integrated if they are not at 1nm spacing. */ +static void xsp2cie_fwa_sconvert( +xsp2cie *p, /* this */ +xspect *sout, /* Return corrected input spectrum (may be NULL, or same as imput) */ +double *out, /* Return XYZ or D50 Lab value (may be NULL) */ +xspect *in /* Spectrum to be converted */ +) { + double ww; + int i, j, k; + double Emc, Smc; /* Emission and Stimulation multipiers for instrument meas. */ + double Emct, Smct; /* Emission and Stimulation multipiers for target illum. */ + double scale = 0.0; + xspect tsout; /* Temporary sout */ + double wout[3]; /* Working CIE out */ +#ifdef DEBUG + double chout[3]; /* Out check values */ + double oout[3]; +#endif /* DEBUG */ +#ifdef DOPLOT_ALL_FWA + double xx[XSPECT_MAX_BANDS]; + double y1[XSPECT_MAX_BANDS]; + double y2[XSPECT_MAX_BANDS]; + double y3[XSPECT_MAX_BANDS]; + int plix = 0; +#endif /* DOPLOT_ALL_FWA */ + + tsout.spec_n = 0; + tsout.spec_wl_short = 0.0; + tsout.spec_wl_long = 0.0; + tsout.norm = 0.0; + +#define MIN_ILLUM 1e-8 /* Minimum assumed illumination level at wavelength */ +#define MIN_REFL 1e-6 /* Minimum assumed reflectance at wavelength */ + + /* With colorant, estimate stimulation level of FWA for instrument illuminant */ + /* and for target illuminant. Because the colorant estimate depends on the FWA */ + /* estimate, and the FWA emissions can contribute to FWA stimulation, */ + /* we itterate a few times to allow this to converge. */ + Emc = Emct = 0.0; + for (k = 0; k < 4; k++) { + Smct = Smc = 0.0; + for (ww = FWA1_stim.spec_wl_short; ww <= FWA1_stim.spec_wl_long; ww += p->bw) { + double Kc; /* FWA contribution for instrument illum */ + double Kct; /* FWA contribution for target illum */ + double Ii; /* Instrument illuminant level */ + double It; /* Target illuminant level */ + double Eu; /* FWA emmission profile */ + double Rc; /* Measured reflectance under inst. illum. */ + double Rmb; /* Media reflectance measured on instrument */ + double Rcch; /* Half colorant reflectance value */ + double Su; /* FWA sensitivity */ + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + Kc = Emc * Eu; /* FWA contribution under inst. illum. */ + Kct = Emct * Eu; /* FWA contribution under target illum. */ + + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised instr. illuminant at wavelength */ + if (Ii < MIN_ILLUM) + Ii = MIN_ILLUM; + + getval_lxspec(&p->tillum, &It, ww);/* Normalised target. illuminant at wavelength */ + if (It < MIN_ILLUM) + It = MIN_ILLUM; + + getval_lxspec(&p->media, &Rmb, ww); /* Base media reflectance at this wavelength */ + if (Rmb < MIN_REFL) + Rmb = MIN_REFL; + + getval_lxspec(in, &Rc, ww) ; /* Media + colorant reflectance at wavelength */ + if (Rc < 0.0) + Rc = 0.0; + +#ifdef NEVER + Rcch = sqrt(Rc/Rmb); /* Half reflectance estimate (valid if no FWA) */ + +#else + /* Solve for underlying colorant half reflectance, discounting FWA */ + if (Rmb <= MIN_REFL) /* Hmm. */ + Rcch = sqrt(fabs(Rmb)); + else + Rcch = (-Kc + sqrt(Kc * Kc + 4.0 * Ii * Ii * Rmb * Rc))/(2.0 * Ii * Rmb); +#endif + + getval_lxspec(&FWA1_stim, &Su, ww); /* FWA stimulation sensitivity this wavelength */ + + + Smc += Su * (Ii * Rcch + Kc); + Smct += Su * (It * Rcch + Kct); + DBGF((DBGA,"at %.1fnm, Rmb %f, Rc %f, Rch %f, Rcch %f, Ii %f, It %f, Kct %f, Smc %f, Smct %f,\n",ww,Rmb,Rc,sqrt(Rc),Rcch,Ii,It,Kct,Su * (Ii * Rcch + Kc),Su * (It * Rcch + Kct))); + } + Emc = Smc/p->Sm; /* FWA Emmsion muliplier with colorant for instr. illum. */ + Emct = Smct/p->Sm; /* FWA Emmsion muliplier with colorant for target illum. */ + + DBGF((DBGA,"Itteration %d, Smc %f, Smct %f, Emc %f, Emct %f\n\n",k, Smc,Smct,Emc,Emct)); + } + + for (j = 0; j < 3; j++) { + wout[j] = 0.0; +#ifdef DEBUG + chout[j] = 0.0; +#endif /* DEBUG */ + } + + /* Compute CIE output over observer range in 1nm increments */ + scale = 0.0; + for (ww = p->observer[1].spec_wl_short; ww <= p->observer[1].spec_wl_long; ww += p->bw) { + double Kc; /* FWA contribution for instrument illum */ + double Kct; /* FWA contribution for target illum */ + double Ii; /* Instrument illuminant level */ + double It; /* Target illuminant level */ + double Io; /* Observer illuminant level */ + double Rmb; /* Base media reflectance estimate */ + double Eu; /* FWA emmission profile */ + double Rc; /* Measured reflectance under inst. illum. */ + /* Rch Measured half reflectance under inst. illum */ + double Rcch; /* Corrected Rc colorant half reflectance */ + double Rct; /* Corrected Rc for target illuminant */ + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + Kc = Emc * Eu; /* FWA contribution under inst. illum. */ + Kct = Emct * Eu; /* FWA contribution under target illum. */ + + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised instr. illuminant at wavelength */ + if (Ii < MIN_ILLUM) + Ii = MIN_ILLUM; + + getval_lxspec(&p->tillum, &It, ww);/* Normalised target. illuminant at wavelength */ + if (It < MIN_ILLUM) + It = MIN_ILLUM; + + getval_lxspec(&p->media, &Rmb, ww); /* Base media reflectance at this wavelength */ + if (Rmb < MIN_REFL) + Rmb = MIN_REFL; + + getval_lxspec(in, &Rc, ww) ; /* Media + colorant reflectance at wavelength */ + if (Rc < 0.0) + Rc = 0.0; + + /* Solve for underlying colorant half transmittance, discounting FWA */ + if (Rmb <= MIN_REFL) /* Hmm. */ + Rcch = sqrt(fabs(Rmb)); + else + Rcch = (-Kc + sqrt(Kc * Kc + 4.0 * Ii * Ii * Rmb * Rc))/(2.0 * Ii * Rmb); + + /* Estimated corrected reflectance */ + Rct = ((It * Rcch * Rmb + Kct) * Rcch)/It; + + DBGF((DBGA,"at %.1fnm, Rmb %f, Rc %f, Rch %f, Rcch %f, Ii %f, It %f, Kct %f, Rct %f\n",ww,Rmb,Rc,sqrt(Rc),Rcch,Ii,It,Kct,Rct)); + + if (p->insteqtarget) /* Ignore FWA corrected value if same illuminant */ + Rct = Rc; + +#ifdef DOPLOT_ALL_FWA + xx[plix] = ww; + y1[plix] = Rc; /* Uncorrected reflectance */ +// y2[plix] = Rct - Rc; /* Difference between corrected and uncorrected */ +// y2[plix] = Rcch * Rcch; /* Estimated underlying colorant reflectance without FWA */ +// y2[plix] = Rmb; /* Base media relectance estimate */ + y2[plix] = Kct; /* FWA contribution under target illuminant */ + y3[plix++] = Rct; /* Corrected reflectance */ +#endif /* DOPLOT_ALL_FWA */ + + /* Observer illuminant */ + getval_lxspec(&p->oillum, &Io, ww); /* Normalised observer illuminant */ + + /* Compute CIE result */ + for (j = 0; j < 3; j++) { + double O; + getval_lxspec(&p->observer[j], &O, ww); + if (j == 1) + scale += Io * O; /* Integrate Y illuminant/observer values */ + wout[j] += Rct * Io * O; /* Corrected refl. * Observer illuminant */ +#ifdef DEBUG + chout[j] += Rc * It * O; +#endif /* DEBUG */ + } + } + if (p->isemis) { + scale = 0.683002; /* Convert from mW/m^2 to Lumens/m^2 */ + /* (== 683 Luments/Watt/m^2) */ + } else { + scale = 1.0/scale; + } + for (j = 0; j < 3; j++) { /* Scale for illuminant/observer normalisation of Y */ + wout[j] *= scale; +#ifdef CLAMP_XYZ + if (p->clamp && wout[j] < 0.0) + wout[j] = 0.0; /* Just to be sure we don't get silly values */ +#endif /* CLAMP_XYZ */ + } + +#ifdef DEBUG + for (j = 0; j < 3; j++) { /* Scale for illuminant/observer normalisation of Y */ + chout[j] *= scale; +#ifdef CLAMP_XYZ + if (p->clamp && chout[j] < 0.0) + chout[j] = 0.0; /* Just to be sure we don't get silly values */ +#endif /* CLAMP_XYZ */ + } + icmXYZ2Lab(&icmD50, oout, wout); + icmXYZ2Lab(&icmD50, chout, chout); + DBGF((DBGA,"Compensated %f %f %f, uncompensated %f %f %f\n", + oout[0], oout[1], oout[2], chout[0], chout[1], chout[2])); +#endif /* DEBUG */ + +#ifdef DOPLOT_ALL_FWA + printf("FWA compensated spectrum for sample\n"); + do_plot(xx,y1,y2,y3,plix); +#endif /* DOPLOT_ALL_FWA */ + + /* Do it again for output over optional returned spectrum range */ + if (sout != NULL) { + tsout.spec_n = in->spec_n; + tsout.spec_wl_short = in->spec_wl_short; + tsout.spec_wl_long = in->spec_wl_long; + tsout.norm = in->norm; + + for (i = 0; i < in->spec_n; i++) { + double Kc; /* FWA contribution for instrument illum */ + double Kct; /* FWA contribution for target illum */ + double Ii; /* Instrument illuminant level */ + double It; /* Target/simulated instrument illuminant level */ + double Rmb; /* Base media reflectance estimate */ + double Eu; /* FWA emmission profile */ + double Rc; /* Reflectance under inst. illum. */ + double Rcch; /* Corrected Rc half reflectance */ + double Rct; /* Corrected Rc for target illuminant */ + +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(i); +#endif + ww = (in->spec_wl_long - in->spec_wl_short) + * ((double)i/(in->spec_n-1.0)) + in->spec_wl_short; + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + Kc = Emc * Eu; /* FWA contribution under inst. illum. */ + Kct = Emct * Eu; /* FWA contribution under target illum. */ + + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised instr. illuminant at wavelength */ + if (Ii < MIN_ILLUM) + Ii = MIN_ILLUM; + + getval_lxspec(&p->tillum, &It, ww);/* Normalised target. illuminant at wavelength */ + if (It < MIN_ILLUM) + It = MIN_ILLUM; + + getval_lxspec(&p->media, &Rmb, ww); /* Base media reflectance at this wavelength */ + if (Rmb < MIN_REFL) + Rmb = MIN_REFL; + + getval_lxspec(in, &Rc, ww) ; /* Media + colorant reflectance at wavelength */ + if (Rc < 0.0) + Rc = 0.0; + + if (Rmb < MIN_REFL) /* Hmm. */ + Rcch = sqrt(fabs(Rmb)); + else + Rcch = (-Kc + sqrt(Kc * Kc + 4.0 * Ii * Ii * Rmb * Rc))/(2.0 * Ii * Rmb); + + Rct = ((It * Rcch * Rmb + Kct) * Rcch)/It; + + if (p->insteqtarget) /* Ignore FWA corrected value if same illuminant */ + Rct = Rc; + + tsout.spec[i] = tsout.norm * Rct; + } + } + + /* If Lab is target, convert to D50 Lab */ + if (p->doLab) { + icmXYZ2Lab(&icmD50, wout, wout); + } + + if (out != NULL) { + out[0] = wout[0]; + out[1] = wout[1]; + out[2] = wout[2]; + } + + if (sout != NULL) { + *sout = tsout; /* Structure copy */ + } + +#undef MIN_ILLUM +#undef MIN_REFL + +} + +/* Normal conversion without returning spectrum */ +static void xsp2cie_fwa_convert(xsp2cie *p, double *out, xspect *in) { + xsp2cie_fwa_sconvert(p, NULL, out, in); +} + +/* Extract the colorant reflectance value from the media. Takes FWA */ +/* into account if set. FWA must be set. */ +static int xsp2cie_fwa_extract(xsp2cie *p, /* this */ +xspect *out, /* Extracted colorant refl. spectrum */ +xspect *in /* Spectrum to be converted, normalised by norm */ +) { + double ww; + int i, j, k; + double Emc, Smc; /* Emission and Stimulation multipiers for instrument meas. */ + +#ifdef DOPLOT_ALL_FWA + double xx[XSPECT_MAX_BANDS]; + double y1[XSPECT_MAX_BANDS]; + double y2[XSPECT_MAX_BANDS]; + double y3[XSPECT_MAX_BANDS]; + int plix = 0; +#endif /* DOPLOT_ALL_FWA */ + + /* With colorant, estimate stimulation level of FWA for instrument illuminant */ + /* and for target illuminant. Because the colorant estimate depends on the FWA */ + /* estimate, and the FWA emissions can contribute to FWA stimulation, */ + /* we itterate a few times to allow this to converge. */ + Emc = 0.0; + for (k = 0; k < 4; k++) { + Smc = 0.0; + for (ww = FWA1_stim.spec_wl_short; ww <= FWA1_stim.spec_wl_long; ww += p->bw) { + double Kc; /* FWA contribution for instrument illum */ + double Ii; /* Instrument illuminant level */ + double Su; /* FWA sensitivity */ + double Rmb; /* Media reflectance measured on instrument */ + double Eu; /* FWA emmission profile */ + double Rc; /* Reflectance under inst. illum. */ + double Rcch; /* Half colorant reflectance value */ + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + Kc = Emc * Eu; /* FWA contribution under inst. illum. */ + + getval_lxspec(&p->media, &Rmb, ww); /* Base Media */ + getval_lxspec(in, &Rc, ww); /* Media + colorant reflectance at wavelength + FWA */ + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised instrument illuminant */ + if (Ii < 1e-9) + Ii = 1e-9; + + if (Rmb < 1e-9) /* Hmm. */ + Rcch = sqrt(fabs(Rmb)); + else + Rcch = (-Kc + sqrt(Kc * Kc + 4.0 * Ii * Ii * Rmb * Rc))/(2.0 * Ii * Rmb); + + getval_lxspec(&FWA1_stim, &Su, ww); /* FWA stimulation sensitivity this wavelength */ + Smc += Su * (Ii * Rcch + Kc); + +//DBGF((DBGA,"ww = %f, Rmb %f, Rcch %f, Ii %f, Su %f, Smc %f\n", ww,Rmb,Rcch,Ii,Su,Smc)); + } + Emc = Smc/p->Sm; /* FWA Emmsion muliplier with colorant for instr. illum. */ + } + + DBGF((DBGA,"extract:\n")); + DBGF((DBGA,"Smc = %f\n",Smc)); + DBGF((DBGA,"Emc = %f\n",Emc)); + + out->spec_n = in->spec_n; + out->spec_wl_short = in->spec_wl_short; + out->spec_wl_long = in->spec_wl_long; + out->norm = in->norm; + + for (i = 0; i < in->spec_n; i++) { + double Kc; /* FWA contribution for instrument illum */ + double Ii; /* Instrument illuminant level */ + double Rmb; /* Base media reflectance estimate */ + double Eu; /* FWA emmission profile */ + double Rc; /* Reflectance under inst. illum. */ + double Rcch; /* Corrected Rc half reflectance */ + +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(i); +#endif + ww = (in->spec_wl_long - in->spec_wl_short) + * ((double)i/(in->spec_n-1.0)) + in->spec_wl_short; + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + Kc = Emc * Eu; /* FWA contribution under inst. illum. */ + + getval_lxspec(&p->media, &Rmb, ww); /* Base Media */ + getval_lxspec(in, &Rc, ww); /* Media + colorant reflectance at wavelength + FWA */ + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised instrument illuminant */ + if (Ii < 1e-9) + Ii = 1e-9; + + if (Rmb < 1e-9) /* Hmm. */ + Rcch = sqrt(fabs(Rmb)); + else + Rcch = (-Kc + sqrt(Kc * Kc + 4.0 * Ii * Ii * Rmb * Rc))/(2.0 * Ii * Rmb); + + Rcch *= Rcch; /* Full reflectance value */ + + out->spec[i] = out->norm * Rcch; + +#ifdef DOPLOT_ALL_FWA + xx[plix] = ww; + y1[plix] = Rmb; /* Base media */ + y2[plix] = Rc; /* Uncorrected reflectance */ + y3[plix] = Rcch; /* Underlying colorant reflectance without FWA */ + plix++; +#endif /* DOPLOT_ALL_FWA */ + } +#ifdef DOPLOT_ALL_FWA + printf("FWA compensated extraction for sample\n"); + do_plot(xx,y1,y2,y3,plix); +#endif /* DOPLOT_ALL_FWA */ + return 0; +} + + +/* 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. */ +static int xsp2cie_fwa_apply(xsp2cie *p, /* this */ +xspect *out, /* Applied refl. spectrum */ +xspect *in /* Colorant reflectance to be applied */ +) { + double ww; + int i, j, k; + double Emc, Smc; /* Emission and Stimulation multipiers for instrument meas. */ + +#ifdef DOPLOT_ALL_FWA + double xx[XSPECT_MAX_BANDS]; + double y1[XSPECT_MAX_BANDS]; + double y2[XSPECT_MAX_BANDS]; + double y3[XSPECT_MAX_BANDS]; + int plix = 0; +#endif /* DOPLOT_ALL_FWA */ + + /* With colorant, estimate stimulation level of FWA for instrument illuminant. */ + /* We itterate a few times to allow for FWA self stimulation. */ + Emc = 0.0; + for (k = 0; k < 4; k++) { + Smc = 0.0; + for (ww = FWA1_stim.spec_wl_short; ww <= FWA1_stim.spec_wl_long; ww += p->bw) { + double Kc; /* FWA contribution for instrument illum */ + double Ii; /* Instrument illuminant level */ + double Eu; /* FWA emmission profile */ + double Su; /* FWA sensitivity */ + double Rcch; /* Half colorant reflectance value */ + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + Kc = Emc * Eu; /* FWA contribution under inst. illum. */ + + getval_lxspec(in, &Rcch, ww); /* Colorant reflectance at wavelength */ + Rcch = sqrt(Rcch); /* Half reflectance estimate (valid if no FWA) */ + + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised instr. illuminant at wavelength */ + if (Ii < 1e-9) + Ii = 1e-9; + + getval_lxspec(&FWA1_stim, &Su, ww); /* FWA stimulation sensitivity this wavelength */ + Smc += Su * (Ii * Rcch + Kc); +//DBGF((DBGA,"ww = %f, Rcch %f, Ii %f, Su %f, Smc %f\n", ww,Rcch,Ii,Su,Smc)); + } + Emc = Smc/p->Sm; /* FWA Emmsion muliplier with colorant for instr. illum. */ + } + + DBGF((DBGA,"apply:\n")); + DBGF((DBGA,"Smc = %f\n",Smc)); + DBGF((DBGA,"Emc = %f\n",Emc)); + + out->spec_n = in->spec_n; + out->spec_wl_short = in->spec_wl_short; + out->spec_wl_long = in->spec_wl_long; + out->norm = in->norm; + + for (i = 0; i < in->spec_n; i++) { + double Kc; /* FWA contribution for instrument illum */ + double Ii; /* Instrument illuminant level */ + double Rmb; /* Base media reflectance estimate */ + double Eu; /* FWA emmission profile */ + double Rc; /* Reflectance under inst. illum. */ + double Rcch; /* Rc half reflectance */ + double RcI; /* Reconstituted Rc for inst. illuminant times illuminant */ + +#if defined(__APPLE__) && defined(__POWERPC__) + gcc_bug_fix(i); +#endif + ww = (in->spec_wl_long - in->spec_wl_short) + * ((double)i/(in->spec_n-1.0)) + in->spec_wl_short; + + getval_lxspec(&p->emits, &Eu, ww); /* FWA emission at this wavelength */ + Kc = Emc * Eu; /* FWA contribution under inst. illum. */ + + getval_lxspec(&p->media, &Rmb, ww); /* Base Media */ + getval_lxspec(in, &Rcch, ww); /* Colorant reflectance at wavelength */ + Rcch = sqrt(Rcch); /* Half reflectance at wavelength */ + if (Rmb < 1e-9) /* Hmm. */ + Rcch = sqrt(fabs(Rmb)); + + getval_lxspec(&p->iillum, &Ii, ww); /* Normalised instrument illuminant */ + if (Ii < 1e-9) + Ii = 1e-9; + + RcI = (Ii * Rcch * Rmb + Kc) * Rcch; + + out->spec[i] = out->norm * RcI/Ii; /* Reconstituted reflectance */ +#ifdef DOPLOT_ALL_FWA + xx[plix] = ww; + y1[plix] = Rmb; /* Base media */ + y2[plix] = Rcch; /* Underlying colorant reflectance without FWA */ + y3[plix] = RcI/Ii; /* Reconstituted reflectance */ + plix++; +#endif /* DOPLOT_ALL_FWA */ + } +#ifdef DOPLOT_ALL_FWA + printf("FWA compensated application for sample\n"); + do_plot(xx,y1,y2,y3,plix); +#endif /* DOPLOT_ALL_FWA */ + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#endif /* !SALONEINSTLIB */ + +/* Do the normal spectral to CIE conversion. */ +/* Note that the input spectrum normalisation value is used. */ +/* Emissive spectral values are assumed to be in mW/nm, and sampled */ +/* rather than integrated if they are not at 1nm spacing. */ +void xsp2cie_sconvert( +xsp2cie *p, /* this */ +xspect *sout, /* Return input spectrum (may be NULL) */ +double *out, /* Return XYZ or D50 Lab value */ +xspect *in /* Spectrum to be converted */ +) { + int j; + double scale = 0.0; + + /* Compute the XYZ values (normalised to 1.0) */ + for (j = 0; j < 3; j++) { + double ww; + + /* Integrate at 1nm intervals over the observer range (as */ + /* per CIE recommendations). Lower resolution spectra are */ + /* upsampled using linear/3rd order polinomial interpolated */ + /* (also as per CIE recommendations), and consistent (?) with the */ + /* assumption of a triangular spectral response made in the */ + /* ANSI CGATS.5-1993 spec. If illumninant or material spectra */ + /* values are truncated at the extremes, then the last valid values */ + /* are used, also consistent with CIE and ANSI CGATS recommendations. */ + out[j] = 0.0; + for (ww = p->observer[j].spec_wl_short; ww <= p->observer[j].spec_wl_long; ww += 1.0) { + double I, O, S; + getval_xspec(&p->illuminant, &I, ww); + getval_xspec(&p->observer[j], &O, ww); + getval_xspec(in, &S, ww); + if (j == 1) + scale += I * O; /* Integrate Y illuminant/observer values */ + out[j] += I * O * S; + } + } + if (p->isemis) { + scale = 0.683002; /* Convert from mW/m^2 to Lumens/m^2 */ + /* (== 683 Luments/Watt/m^2) */ + } else { + scale = 1.0/scale; + } + for (j = 0; j < 3; j++) { /* Scale for illuminant/observer normalisation of Y */ + out[j] *= scale; +#ifdef CLAMP_XYZ + if (p->clamp && out[j] < 0.0) + out[j] = 0.0; /* Just to be sure we don't get silly values */ +#endif /* CLAMP_XYZ */ + } + +#ifndef SALONEINSTLIB + /* If Lab is target, convert to D50 Lab */ + if (p->doLab) { + icmXYZ2Lab(&icmD50, out, out); + } +#endif /* !SALONEINSTLIB */ + + if (sout != NULL) { + *sout = *in; /* Structure copy */ + } +} + +/* Normal Tristumulus conversion */ +void xsp2cie_convert(xsp2cie *p, double *out, xspect *in) { + xsp2cie_sconvert(p, NULL, out, in); +} + +void xsp2cie_del( +xsp2cie *p +) { + free(p); + return; +} + +/* Create and return a new spectral conversion object */ +xsp2cie *new_xsp2cie( +icxIllumeType ilType, /* Illuminant */ +xspect *custIllum, /* Optional custom illuminant */ +icxObserverType obType, /* Observer */ +xspect custObserver[3], /* Optional custom observer */ +icColorSpaceSignature rcs, /* Return color space, icSigXYZData or icSigLabData */ + /* ** Must be icSigXYZData if SALONEINSTLIB ** */ +icxClamping clamp /* NZ to clamp XYZ/Lab to be +ve */ +) { + xsp2cie *p; + + if ((p = (xsp2cie *) calloc(1,sizeof(xsp2cie))) == NULL) + return NULL; + + p->isemis = 0; + switch (ilType) { + case icxIT_none: + p->illuminant = il_none; /* Emissive */ + p->isemis = 1; + break; + case icxIT_custom: + p->illuminant = *custIllum; + break; + case icxIT_A: + p->illuminant = il_A; + break; + case icxIT_C: + p->illuminant = il_C; + break; + case icxIT_default: + case icxIT_D50: + p->illuminant = il_D50; + break; + case icxIT_D50M2: + if (il_D50M2.spec_n == 0) + uv_filter(&il_D50M2, &il_D50); + p->illuminant = il_D50M2; + break; + case icxIT_D65: + p->illuminant = il_D65; + break; + case icxIT_E: + p->illuminant = il_none; + break; +#ifndef SALONEINSTLIB + case icxIT_F5: + p->illuminant = il_F5; + break; + case icxIT_F8: + p->illuminant = il_F8; + break; + case icxIT_F10: + p->illuminant = il_F10; + break; + case icxIT_Spectrocam: + p->illuminant = il_Spectrocam; + break; +#endif /* !SALONEINSTLIB */ + default: + DBGF((DBGA,"new_xsp2cie() unrecognised illuminant 0x%x\n",ilType)); + free(p); + return NULL; + } + + /* Do 3 structure copies to record observer sensitivity curves */ + switch (obType) { + case icxOT_custom: + p->observer[0] = custObserver[0]; + p->observer[1] = custObserver[1]; + p->observer[2] = custObserver[2]; + break; + case icxOT_default: + case icxOT_CIE_1931_2: + p->observer[0] = ob_CIE_1931_2[0]; + p->observer[1] = ob_CIE_1931_2[1]; + p->observer[2] = ob_CIE_1931_2[2]; + break; + case icxOT_CIE_1964_10: + p->observer[0] = ob_CIE_1964_10[0]; + p->observer[1] = ob_CIE_1964_10[1]; + p->observer[2] = ob_CIE_1964_10[2]; + break; +#ifndef SALONEINSTLIB + case icxOT_Stiles_Burch_2: + p->observer[0] = ob_Stiles_Burch_2[0]; + p->observer[1] = ob_Stiles_Burch_2[1]; + p->observer[2] = ob_Stiles_Burch_2[2]; + break; + case icxOT_Judd_Voss_2: + p->observer[0] = ob_Judd_Voss_2[0]; + p->observer[1] = ob_Judd_Voss_2[1]; + p->observer[2] = ob_Judd_Voss_2[2]; + break; + case icxOT_CIE_1964_10c: + p->observer[0] = ob_CIE_1964_10c[0]; + p->observer[1] = ob_CIE_1964_10c[1]; + p->observer[2] = ob_CIE_1964_10c[2]; + break; + case icxOT_Shaw_Fairchild_2: + p->observer[0] = ob_Shaw_Fairchild_2[0]; + p->observer[1] = ob_Shaw_Fairchild_2[1]; + p->observer[2] = ob_Shaw_Fairchild_2[2]; + break; +#endif /* !SALONEINSTLIB */ + default: + DBGF((DBGA,"new_xsp2cie() unrecognised observer type 0x%x\n",obType)); + free(p); + return NULL; + } + + if (rcs == icSigXYZData) + p->doLab = 0; +#ifndef SALONEINSTLIB + else if (rcs == icSigLabData) + p->doLab = 1; +#endif /* !SALONEINSTLIB */ + else { + DBGF((DBGA,"new_xsp2cie() unrecognised CIE type 0x%x",rcs)); + free(p); + return NULL; + } + + p->clamp = clamp; + + p->convert = xsp2cie_convert; + p->sconvert = xsp2cie_sconvert; +#ifndef SALONEINSTLIB + p->set_mw = xsp2cie_set_mw; /* Default no media white */ + p->set_fwa = xsp2cie_set_fwa; /* Default no FWA compensation */ + p->update_fwa_custillum = xsp2cie_update_fwa_custillum; + p->get_fwa_info = xsp2cie_get_fwa_info; + p->extract = xsp2cie_extract; + p->apply = xsp2cie_apply; +#endif /* !SALONEINSTLIB */ + p->del = xsp2cie_del; + + return p; +} + + +#ifndef SALONEINSTLIB +/* -------------------------------------------------------- */ + +/* Return the spectrum locus rangefor 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) { + xspect *sp[3]; + if (standardObserver(sp, obType)) + return 1; + if (min_wl != NULL) + *min_wl = sp[0]->spec_wl_short; + if (max_wl != NULL) + *max_wl = sp[0]->spec_wl_long; + + return 0; +} + +/* 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 wl, icxObserverType obType) { + xspect *sp[3]; + + DBGF((DBGA,"icx_spectrum_locus got obs %d wl %f\n",obType, wl)); + + if (standardObserver(sp, obType)) + return 1; + + if (wl < sp[0]->spec_wl_short) + wl = sp[0]->spec_wl_short; + if (wl > sp[0]->spec_wl_long) + wl = sp[0]->spec_wl_long; + + xyz[0] = value_xspect(sp[0], wl); + xyz[1] = value_xspect(sp[1], wl); + xyz[2] = value_xspect(sp[2], wl); + + DBGF((DBGA,"returning %f %f %f\n", xyz[0], xyz[1], xyz[2])); + + return 0; +} + +/* Init a xslpoly */ +/* Return nz on error */ +static int icx_init_locus_poly(icxObserverType obType) { + xslpoly *poly; + + if ((poly = spectral_locus_poligon(obType)) == NULL) + return 1; + + /* Initialise (should have a mutex!) */ + if (poly->n == 0) { + int i, j, c; + double Yxy[3]; + double xyz[3]; + xspect *sp[3]; +double tt[3][3]; + + if (standardObserver(sp, obType)) + return 3; + + poly->n = sp[0]->spec_n; + poly->xmin = poly->ymin = 1e6; + poly->xmax = poly->ymax = -1e6; + + for (i = 0; i < poly->n; i++) { + xyz[0] = sp[0]->spec[i]; + xyz[1] = sp[1]->spec[i]; + xyz[2] = sp[2]->spec[i]; + + icmXYZ2Yxy(Yxy, xyz); + + poly->x[i] = Yxy[1]; + poly->y[i] = Yxy[2]; + if (poly->x[i] < poly->xmin) + poly->xmin = poly->x[i]; + if (poly->x[i] > poly->xmax) + poly->xmax = poly->x[i]; + if (poly->y[i] < poly->ymin) + poly->ymin = poly->y[i]; + if (poly->y[i] > poly->ymax) + poly->ymax = poly->y[i]; + } + + /* Select 3 points for inner triangle in RGB order */ + poly->tx[0] = poly->x[poly->n - 1]; + poly->ty[0] = poly->y[poly->n - 1]; + + xyz[0] = value_xspect(sp[0], 517.0); + xyz[1] = value_xspect(sp[1], 517.0); + xyz[2] = value_xspect(sp[2], 517.0); + icmXYZ2Yxy(Yxy, xyz); + poly->tx[1] = Yxy[1]; + poly->ty[1] = Yxy[2]; + + poly->tx[2] = poly->x[0]; + poly->ty[2] = poly->y[0]; + + /* Compute distance from triangles to 0.3, 0.3 */ +// for (i = 0; i < 3; i++) { +// poly->eed[i] = sqrt((poly->tx[i] - 0.3) * (poly->tx[i] - 0.3) +// + (poly->ty[i] - 0.3) * (poly->ty[i] - 0.3)); +// } + + /* Compute baricentric equations */ + for (i = 0; i < 3; i++) { + tt[0][i] = poly->tx[i]; + tt[1][i] = poly->ty[i]; + tt[2][i] = 1.0; + } + if (icmInverse3x3(poly->be, tt)) + error("icx_init_locus_poly: Matrix inversion failed"); + + /* Compute baricentric of 0.3 0.3 */ + /* (Not currently used. How to move center to 0.3 0.3 ?? */ +// for (i = 0; i < 3; i++) +// poly->eed[i] = poly->be[i][0] * 0.3 + poly->be[i][1] * 0.3 + poly->be[i][2]; + } + return 0; +} + +/* 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) { + int i, j, c; + xslpoly *poly; + double Yxy[3]; + + if ((poly = spectral_locus_poligon(obType)) == NULL) + return 2; + + /* Init poly if needed */ + if (poly->n == 0 && icx_init_locus_poly(obType)) + return 2; + + icmXYZ2Yxy(Yxy, xyz); + + /* Quick test - bounding box */ + if (Yxy[1] < poly->xmin || Yxy[1] > poly->xmax + || Yxy[2] < poly->ymin || Yxy[2] > poly->ymax) + return 1; + + /* Quick test - inner triangle */ + for (c = 1, i = 0, j = 3-1; i < 3; j = i++) { + if ( ((poly->ty[i] > Yxy[2]) != (poly->ty[j] > Yxy[2])) + && (Yxy[1] < (poly->tx[j] - poly->tx[i]) * (Yxy[2] - poly->ty[i]) + / (poly->ty[j] - poly->ty[i]) + poly->tx[i]) ) + c = !c; + } + if (c == 0) + return 0; + + /* Do point in poligon test */ + /* (This could be speeded up in many ways) */ + for (c = 1, i = 0, j = poly->n-1; i < poly->n; j = i++) { + if ( ((poly->y[i] > Yxy[2]) != (poly->y[j] > Yxy[2])) + && (Yxy[1] < (poly->x[j] - poly->x[i]) * (Yxy[2] - poly->y[i]) + / (poly->y[j] - poly->y[i]) + poly->x[i]) ) + c = !c; + } + + return c; +} + +/* Return an aproximate RGB value for coloring within the spectrum locus */ +void icx_spec_locus_color(double rgb[3], double xyz[3], icxObserverType obType) { + int i, j; + xslpoly *poly; + double Yxy[3]; + double dtt[3]; /* Distances to triangle points */ + double v[3]; + double max; + + if ((poly = spectral_locus_poligon(obType)) == NULL) + return; + + /* Init poly if needed */ + if (poly->n == 0 && icx_init_locus_poly(obType)) + return; + + icmXYZ2Yxy(Yxy, xyz); + + /* Compute the baricentric coord for the input point, */ + for (max = -1e6, i = 0; i < 3; i++) { + v[i] = poly->be[i][0] * Yxy[1] + poly->be[i][1] * Yxy[2] + poly->be[i][2]; + if (v[i] < 0.0) + v[i] = 0.0; + else if (v[i] > 1.0) + v[i] = 1.0; + + /* Normalise to put wp at 0.3 0.3 */ + // ~~99 + + v[i] = pow(v[i], 1.0/2.2); + + if (v[i] > max) + max = v[i]; + } + + for (i = 0; i < 3; i++) { + rgb[i] = v[i]/max; + } +} + +/* -------------------------------------------------------- */ + +/* Status T log10 weightings */ +/* CMYV */ +static xspect denT[4] = { + { + 44, 340.0, 770.0, /* 44 bands from 340 to 770 nm in 10nm steps */ + 1.0, /* Log10 Scale factor */ + { + 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.500, 1.778, 2.653, 4.477, + 5.000, 4.929, 4.740, 4.398, 4.000, + 3.699, 3.176, 2.699, 2.477, 2.176, + 1.699, 1.000, 0.500, 0.000, 0.000, + 0.000, 0.000, 0.000 + } + }, + { + 44, 340.0, 770.0, /* 44 bands from 340 to 770 nm in 10nm steps */ + 1.0, /* Log10 Scale factor */ + { + 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.500, 3.000, 3.699, + 4.447, 4.833, 4.964, 5.000, 4.944, + 4.820, 4.623, 4.342, 3.954, 3.398, + 2.845, 1.954, 1.000, 0.500, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000 + } + }, + { + 44, 340.0, 770.0, /* 44 bands from 340 to 770 nm in 10nm steps */ + 1.0, /* Log10 Scale factor */ + { + 0.500, + 1.000, 1.301, 2.000, 2.477, 3.176, + 3.778, 4.230, 4.602, 4.778, 4.914, + 4.973, 5.000, 4.987, 4.929, 4.813, + 4.602, 4.255, 3.699, 2.301, 1.602, + 0.500, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.000, 0.000, 0.000 + } + }, + { + 44, 340.0, 770.0, /* 44 bands from 340 to 770 nm in 10nm steps */ + 1.0, /* Log10 Scale factor */ + { + 0.000, + 0.000, 0.000, 0.000, 0.000, 0.000, + 0.500, 1.332, 1.914, 2.447, 2.881, + 3.090, 3.346, 3.582, 3.818, 4.041, + 4.276, 4.513, 4.702, 4.825, 4.905, + 4.957, 4.989, 5.000, 4.989, 4.956, + 4.902, 4.827, 4.731, 4.593, 4.433, + 4.238, 4.013, 3.749, 3.490, 3.188, + 2.901, 2.622, 2.334, 2.041, 1.732, + 1.431, 1.146, 0.500 + } + } +}; + +/* Given a reflectance or transmition spectral product, (Relative */ +/* to the scale factor), return status T CMYV log10 density values */ +void xsp_Tdensity( +double *out, /* Return CMYV density */ +xspect *in /* Spectral product to be converted */ +) { + int j; + + /* Compute the CMYV values (normalised to 1.0) */ + for (j = 0; j < 4; j++) { + double ww; + double sum; + + /* Integrate at 1nm intervals */ + sum = out[j] = 0.0; + for (ww = denT[j].spec_wl_short; ww <= denT[j].spec_wl_long; ww += 1.0) { + double W, S; + + getval_xspec(&denT[j], &W, ww); + getval_xspec(in, &S, ww); + W = pow(10.0, W); /* Convert from log to linear weighting */ + sum += W; /* Sum of weightings */ + out[j] += S * W; + } + out[j] /= sum; /* Normalise */ + if (out[j] < 0.00001) + out[j] = 0.00001; /* Just to be sure we don't get silly values */ + else if (out[j] > 1.0) + out[j] = 1.0; + + out[j] = -log10(out[j]); /* Convert to density */ + } +} + +/* XYZ to status T density aproximate conversion matrix */ +/* (Note we're multiplying by a 0.83 factor below to */ +/* avoid some limiting for some XYZ values) */ +static double xyz2tden[4][3] = { + { 1.750557, -0.361811, -0.265150 }, /* Red density */ + { -0.919004, 1.861722, 0.105787 }, /* Green density */ + { -0.047821, 0.093820, 1.163331 }, /* Blue density */ + { 0.369966, 0.708047, -0.076312 } /* Visual density */ +}; + +/* 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 */ +) { + int i, j; + double den[4]; + +//DBGF((DBGA,"icx_XYZ2den got %f %f %f\n",in[0],in[1],in[2])); + for (i = 0; i < 4; i++) { + + den[i] = 0.0; + for (j = 0; j < 3; j++) + den[i] += 0.83 * xyz2tden[i][j] * in[j]; + +//DBGF((DBGA,"icx_XYZ2den raw den %d = %f\n",i,den[i])); + if (den[i] < 0.00001) + den[i] = 0.00001; /* Just to be sure we don't get silly values */ + else if (den[i] > 1.0) + den[i] = 1.0; + + out[i] = -log10(den[i]); /* Convert to density */ + } +//DBGF((DBGA,"icx_XYZ2den returning densities %f %f %f\n",out[0],out[1],out[2])); +} + +/* 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 */ +) { + int i; + double den[3]; + + for (i = 0; i < 3; i++) { + + den[i] = in[i]; + + if (den[i] < 0.00001) + den[i] = 0.00001; /* Just to be sure we don't get silly values */ + else if (den[i] > 1.0) + den[i] = 1.0; + + out[i] = -log10(den[i]); /* Convert to density */ + } +} + +/* Given an XYZ value, */ +/* return approximate sRGB values */ +void icx_XYZ2sRGB( +double *out, /* Return aproximate CMYV log10 density */ +double *wp, /* Input XYZ white point (may be NULL) */ +double *in /* Input XYZ values */ +) { + int i, j; + double XYZ[3]; + double d65[3] = { 0.950543, 1.0, 1.089303 }; + double mat[3][3] = { + { 3.2406, -1.5372, -0.4986 }, + { -0.9689, 1.8758, 0.0415 }, + { 0.0557, -0.2040, 1.0570 } + }; + + /* Do a simple Von Kries between input white point and D65 */ + if (wp != NULL) { + for (j = 0; j < 3; j++) + XYZ[j] = d65[j] * in[j]/wp[j]; + } else { + for (j = 0; j < 3; j++) + XYZ[j] = in[j]; + } + + /* Convert to sRGB cromaticities */ + for (i = 0; i < 3; i++) { + out[i] = 0.0; + for (j = 0; j < 3; j++) { + out[i] += XYZ[j] * mat[i][j]; + } + } + + /* Apply gamma */ + for (j = 0; j < 3; j++) { + if (out[j] <= (0.03928/12.92)) { + out[j] *= 12.92; + if (out[j] < 0.0) + out[j] = 0.0; + } else { + out[j] = pow(out[j], 1.0/2.4) * 1.055 - 0.055; + if (out[j] > 1.0) + out[j] = 1.0; + } + } +} + +/* ------------------- */ + +#ifdef NEVER /* Deprecated */ + +/* Given a daylight color temperature in degrees K, */ +/* return the corresponding XYZ value (standard 2 degree observer) */ +void icx_DTEMP2XYZ( +double *out, /* Return XYZ value with Y == 1 */ +double ct /* Input temperature in degrees K */ +) { + double Yxy[3]; + +//DBGF((DBGA,"computing temperature %f\n",ct)); + /* Compute chromaticity coordinates */ + if (ct < 7000.0) { + Yxy[1] = -4.6070e9/(ct * ct * ct) + 2.9678e6/(ct * ct) + 0.09911e3/ct + 0.244063; + } else { + Yxy[1] = -2.0064e9/(ct * ct * ct) + 1.9018e6/(ct * ct) + 0.24748e3/ct + 0.237040; + } + Yxy[2] = -3.000 * Yxy[1] * Yxy[1] + 2.870 * Yxy[1] - 0.275; + + Yxy[0] = 1.0; +//DBGF((DBGA,"Yxy = %f %f %f\n",Yxy[0],Yxy[1],Yxy[2])); + + /* Convert to XYZ */ + icmYxy2XYZ(out, Yxy); + +//DBGF((DBGA,"XYZ = %f %f %f\n",out[0],out[1],out[2])); +} + +#endif + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* 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, icxIT_Dtemp or icxIT_Ptemp */ +double ct, /* Input temperature in degrees K */ +xspect *custIllum /* Optional custom illuminant */ +) { + xspect sp; /* Xspect to fill in */ + xsp2cie *conv; /* Means of converting spectrum to XYZ */ + + if (ilType == icxIT_custom) + sp = *custIllum; + else if (standardIlluminant(&sp, ilType, ct) != 0) + return 1; + + if ((conv = new_xsp2cie(icxIT_none, NULL, obType, custObserver, icSigXYZData, 1)) == NULL) + return 1; + + conv->convert(conv, xyz, &sp); + + conv->del(conv); + + /* Normalise */ + xyz[0] /= xyz[1]; + xyz[2] /= xyz[1]; + xyz[1] /= xyz[1]; + + return 0; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* Context for optimiser callback */ +typedef struct { + icxIllumeType ilType; /* Type of illuminant, icxIT_Dtemp or icxIT_Ptemp */ + double xyz[3]; /* Target XYZ */ + icmXYZNumber XYZ; /* Target as XYZ number */ + xsp2cie *conv; /* Means of converting spectrum to XYZ */ + int viscct; /* nz to use visual best match color temperature */ +} cctctx; + +static double cct_func(void *fdata, double tp[]) { + cctctx *x = (cctctx *)fdata; + double xyz[3]; /* Current value */ + double lab1[3], lab2[3]; + xspect sp; + double rv = 0.0; + icmXYZNumber *wp = &x->XYZ; + + /* Compute the XYZ for the given temperature */ + if (x->ilType == icxIT_Dtemp) { + if (daylight_il(&sp, tp[0]) != 0) + rv = 1e6; + } else { + if (planckian_il(&sp, tp[0]) != 0) + rv = 1e6; + } + + if (rv == 0.0) { + x->conv->convert(x->conv, xyz, &sp); + xyz[0] /= xyz[1]; + xyz[2] /= xyz[1]; + xyz[1] /= xyz[1]; + /* Compute the color difference to the target */ + if (x->viscct) { + /* Use modern CIEDE2000 color difference - gives a better visual match */ + icmXYZ2Lab(wp, lab1, x->xyz); + icmXYZ2Lab(wp, lab2, xyz); + rv = icmCIE2Ksq(lab1, lab2); + } else { + /* Use original CIE 1960 UCS space color difference */ + icmXYZ21960UCS(lab1, x->xyz); + icmXYZ21960UCS(lab2, xyz); + rv = icmLabDEsq(lab1, lab2); + } + } + +//DBGF((DBGA,"returning %f for temp = %f\n",rv,tp[0])); + return rv; + +} + +/* 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 can use CIEDE2000, 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 locus 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 *insp, /* Input spectrum value, NULL if xyz[] instead */ +int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */ +) { + cctctx x; /* Context for callback */ + double cp[1], s[1]; + double rv; + int i; + double tc, ber, bct = 0.0; + + x.viscct = viscct; + + if (ilType != icxIT_Dtemp && ilType != icxIT_Ptemp) + return -1.0; + x.ilType = ilType; + + if ((x.conv = new_xsp2cie(icxIT_none, NULL, obType, custObserver, icSigXYZData, 1)) == NULL) + return -1; + + if (xyz == NULL) { + if (insp == NULL) + return -1.0; + x.conv->convert(x.conv, x.xyz, insp); + } else { + icmAry2Ary(x.xyz, xyz); + } + + /* Normalise target */ + x.xyz[0] /= x.xyz[1]; + x.xyz[2] /= x.xyz[1]; + x.xyz[1] /= x.xyz[1]; + icmAry2XYZ(x.XYZ, x.xyz); + + /* Do some start samples, to avoid getting trapped in local minima */ + for (ber = 1e9, i = 0; i < 6; i++) { + double er; + tc = 1000.0 + i * 2000.0; + if ((er = cct_func((void *)&x, &tc)) < ber) { + ber = er; + bct = tc; + } +//DBGF((DBGA,"tc = %f, er = %f\n",tc,er)); + } + cp[0] = bct; + s[0] = 500.0; + + /* Locate the CCT */ + if (powell(&rv, 1, cp, s, 0.01, 1000, cct_func, (void *)&x, NULL, NULL) != 0) { + x.conv->del(x.conv); + return -1.0; + } + + if (txyz != NULL) { + xspect sp; + if (x.ilType == icxIT_Dtemp) { + if (daylight_il(&sp, cp[0]) != 0) { + x.conv->del(x.conv); + txyz[0] = txyz[2] = txyz[1] = cp[0] = 0.0; + return cp[0]; + } + } else { + if (planckian_il(&sp, cp[0]) != 0) { + x.conv->del(x.conv); + txyz[0] = txyz[2] = txyz[1] = cp[0] = 0.0; + return cp[0]; + } + } + x.conv->convert(x.conv, txyz, &sp); + /* Make sure locus XYZ is Normalised */ + txyz[0] /= txyz[1]; + txyz[2] /= txyz[1]; + txyz[1] /= txyz[1]; + } + x.conv->del(x.conv); + +//DBGF((DBGA,"returning %f with error %f delta E94 %f\n",cp[0],sqrt(rv))); + return cp[0]; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Convert UCS Yuv to chromatic adaptation Ycd */ +static void UCSYuv2Ycd(double *out, double *in) { + double u, v; + u = in[1]; + v = in[2]; + + out[0] = in[0]; + out[1] = (4.0 - u - 10.0 * v)/v; + out[2] = (1.708 * v - 1.481 * u + 0.404)/v; +} + + + +/* 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 */ +) { + int i; + double cct; + xspect wts; /* Reference white spectrum */ + xsp2cie *tocie; + double wt[3]; /* Reference white in CIE 1960 UCS */ + icmXYZNumber wtn; + double wt_Ycd[3]; /* Ycd reference white */ + double sa[3]; /* Sample white in CIE 1960 UCS */ + double sa_Ycd[3]; /* Ycd sample white */ + double dc; /* delta of sample to reference white in 1960 UCS */ + double ref[8][3]; /* reference XYZ/1964 color space */ + double sam[8][3]; /* sample XYZ/1964 color space */ + double c_ad, d_ad; /* Chromatic adaptation scaling factors */ + double cri = 0.0; + +//DBGF((DBGA,"icx_CIE1995_CRI called\n")); + + /* First find the standard 2 degree observer plankian CCT */ + if ((cct = icx_XYZ2ill_ct(NULL, icxIT_Ptemp, icxOT_CIE_1931_2, NULL, NULL, sample, 0)) < 0.0) + return -1.0; + +//DBGF((DBGA,"CCT = %f\n", cct)); + + /* Create a reference white spectrum with the same CCT */ + if (cct < 5000.0) { + if (planckian_il(&wts, cct)) + return -1.0; + } else { + if (daylight_il(&wts, cct)) + return -1.0; + } + + if ((tocie = new_xsp2cie(icxIT_none, NULL, icxOT_CIE_1931_2, NULL, icSigXYZData, 1)) == NULL) + return -1.0; + + /* Compute the XYZ of the reference white and sample */ + tocie->convert(tocie, wt, &wts); + tocie->convert(tocie, sa, sample); + +//DBGF((DBGA,"XYZ white = %f %f %f\n",wt[0],wt[1],wt[2])); +//DBGF((DBGA,"XYZ sampl = %f %f %f\n",sa[0],sa[1],sa[2])); + + /* Normalize the spectra so as to create a normalized white */ + wts.norm *= wt[1]; + sample->norm *= sa[1]; /* ~~~ shouldn't change sample!!!! ~~~~ */ + tocie->convert(tocie, wt, &wts); + tocie->convert(tocie, sa, sample); + tocie->del(tocie); + +//DBGF((DBGA,"norm XYZ white = %f %f %f\n",wt[0],wt[1],wt[2])); +//DBGF((DBGA,"norm XYZ sampl = %f %f %f\n",sa[0],sa[1],sa[2])); + + /* Convert to perceptual CIE 1960 UCS */ + icmAry2XYZ(wtn, wt); /* Use reference white as UCS white */ + icmXYZ21960UCS(wt, wt); /* 1960 UCS Yuv reference white */ + UCSYuv2Ycd(wt_Ycd, wt); /* Ycd version for chromatic adapation */ + icmXYZ21960UCS(sa, sa); /* 1960 UCS Yuv sample white */ + UCSYuv2Ycd(sa_Ycd, sa); /* Ycd version for chromatic adapation */ + + c_ad = wt_Ycd[1]/sa_Ycd[1]; /* Chromatic adaptation scaling factors */ + d_ad = wt_Ycd[2]/sa_Ycd[2]; + +//DBGF((DBGA,"UCS white = %f %f %f\n",wt[0],wt[1],wt[2])); +//DBGF((DBGA,"UCS sampl = %f %f %f\n",sa[0],sa[1],sa[2])); + + dc = sqrt((wt[1] - sa[1]) * (wt[1] - sa[1]) + (wt[2] - sa[2]) * (wt[2] - sa[2])); + +//DBGF((DBGA,"dc = %f\n",dc)); +//if (dc > 0.0054) DBGF((DBGA,"CRI is invalid\n")); + + /* If dc > 0.0054 we should abort computing the CRI, */ + /* but this means we fail on lots of real world lighting. */ + if (invalid != NULL) { + if (dc > 0.0054) + *invalid = 1; + else + *invalid = 0; + } + + /* Check out the delta E for each reflective sample */ + if ((tocie = new_xsp2cie(icxIT_custom, &wts, icxOT_CIE_1931_2, NULL, icSigXYZData, 1)) == NULL) + return -1.0; + for (i = 0; i < 8; i++) { + tocie->convert(tocie, ref[i], &CIE1995_TCS[i]); + icmXYZ21964WUV(&wtn, ref[i], ref[i]); +//DBGF((DBGA,"ref samp %d = WUV %f %f %f\n", i,ref[i][0],ref[i][1],ref[i][2])); + } + tocie->del(tocie); + + if ((tocie = new_xsp2cie(icxIT_custom, sample, icxOT_CIE_1931_2, NULL, icSigXYZData, 1)) == NULL) + return -1.0; + for (i = 0; i < 8; i++) { + double c, d; + tocie->convert(tocie, sam[i], &CIE1995_TCS[i]); + + icmXYZ21960UCS(sam[i], sam[i]); + + /* Do chromatic adaptation */ + UCSYuv2Ycd(sam[i], sam[i]); + c = sam[i][1]; + d = sam[i][2]; + sam[i][1] = (10.872 + 0.404 * c * c_ad - 4.0 * d * d_ad)/ + (16.518 + 1.481 * c * c_ad - 1.0 * d * d_ad); + + sam[i][2] = (5.520)/ + (16.518 + 1.481 * c * c_ad - 1.0 * d * d_ad); + + icm1960UCS21964WUV(&wtn, sam[i], sam[i]); + +//DBGF((DBGA,"sam samp %d = WUV %f %f %f\n", i,sam[i][0],sam[i][1],sam[i][2])); + } + tocie->del(tocie); + + /* Compute the CRI */ + for (i = 0; i < 8; i++) { + double de, tcri; + + de = icmLabDE(ref[i], sam[i]); + tcri = 100.0 - 4.6 * de; +//DBGF((DBGA,"sample %d: de = %f, CRI = %f\n",i,de,tcri)); + cri += tcri; + } + cri /= 8.0; + +//DBGF((DBGA,"average CRI = %f\n",cri)); + if (cri < 0.0) + cri = -1.0; + +//DBGF((DBGA,"returning CRI = %f\n",cri)); + return cri; +} +/* - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#endif /* !SALONEINSTLIB */ + + + + + -- cgit v1.2.3