summaryrefslogtreecommitdiff
path: root/xicc/xspect.c
diff options
context:
space:
mode:
Diffstat (limited to 'xicc/xspect.c')
-rw-r--r--xicc/xspect.c4714
1 files changed, 4714 insertions, 0 deletions
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 <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+#include <string.h>
+#include <math.h>
+#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 */
+
+
+
+
+