ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/spec_rgb.c
Revision: 2.35
Committed: Thu Aug 22 04:27:53 2024 UTC (9 months, 1 week ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.34: +4 -3 lines
Log Message:
refactor: Added scblack and changed cblack and cwhite to global const arrays

File Contents

# User Rev Content
1 greg 1.1 #ifndef lint
2 greg 2.35 static const char RCSid[] = "$Id: spec_rgb.c,v 2.34 2024/08/14 20:05:23 greg Exp $";
3 greg 1.1 #endif
4     /*
5     * Convert colors and spectral ranges.
6 greg 2.11 * Added von Kries white-balance calculations 10/01 (GW).
7     *
8     * Externals declared in color.h
9     */
10    
11 greg 2.12 #include "copyright.h"
12 greg 1.1
13 greg 2.13 #include <stdio.h>
14     #include <string.h>
15 greg 2.32 #include <math.h>
16 greg 1.1 #include "color.h"
17 greg 2.11
18     #define CEPS 1e-4 /* color epsilon */
19    
20 greg 2.26 #define CEQ(v1,v2) (((v1) <= (v2)+CEPS) & ((v2) <= (v1)+CEPS))
21 greg 1.1
22 greg 2.26 #define XYEQ(c1,c2) (CEQ((c1)[CIEX],(c2)[CIEX]) & CEQ((c1)[CIEY],(c2)[CIEY]))
23 greg 2.5
24 gwlarson 2.10
25 greg 2.11 RGBPRIMS stdprims = STDPRIMS; /* standard primary chromaticities */
26 greg 2.27 RGBPRIMS xyzprims = {1,0, 0,1, 0,0, 1.f/3.f,1.f/3.f};
27 greg 2.5
28 greg 2.35 const COLOR cblack = BLKCOLOR; /* global black color */
29     const COLOR cwhite = WHTCOLOR; /* global white color */
30     const SCOLOR scblack = {0}; /* global black spectrum */
31 greg 2.8
32 greg 2.11 float xyneu[2] = {1./3., 1./3.}; /* neutral xy chromaticities */
33    
34 greg 1.1 /*
35 greg 2.27 * The tables below encode the CIE 1931 2° standard observer
36     * functions for X, Y, and Z. These tables are cumulative, as are
37     * ones that follow.
38 greg 1.1 */
39    
40 greg 2.27 #define CIE_X_WLMIN 362
41     #define CIE_X_WLMAX 774
42    
43     static const unsigned short cie_x_cumul[CIE_X_WLMAX-CIE_X_WLMIN+1] = {
44     0,1,1,1,1,1,1,2,2,2,3,3,3,4,5,5,6,7,8,9,10,11,12,14,16,18,20,23,26,29,
45     33,37,41,47,53,60,68,77,86,97,108,121,135,151,170,190,214,241,271,304,
46     342,385,432,486,545,612,686,768,860,961,1073,1195,1326,1467,1618,
47     1776,1943,2117,2298,2485,2677,2875,3076,3281,3489,3700,3912,4125,4340,4555,
48     4769,4983,5197,5409,5620,5830,6038,6244,6448,6651,6851,7049,7245,7437,
49     7627,7813,7995,8173,8347,8517,8682,8842,8996,9143,9284,9418,9545,9665,
50     9778,9884,9984,10077,10164,10245,10321,10390,10454,10513,10566,10615,
51     10659,10698,10734,10766,10794,10819,10842,10861,10879,10893,10906,10917,
52     10926,10933,10939,10944,10948,10951,10953,10955,10957,10958,10960,10961,
53     10964,10967,10971,10977,10984,10994,11006,11020,11038,11060,11085,11114,
54     11148,11187,11231,11280,11335,11396,11464,11537,11618,11705,11799,11901,
55     12010,12126,12249,12380,12518,12664,12818,12980,13150,13328,13515,13709,
56     13913,14124,14345,14574,14813,15060,15317,15582,15858,16142,16437,16741,
57     17055,17379,17713,18057,18412,18776,19151,19536,19931,20337,20753,21180,
58     21616,22063,22520,22988,23465,23953,24450,24957,25474,26000,26535,27080,
59     27633,28195,28765,29343,29929,30522,31122,31729,32342,32961,33585,34215,
60     34849,35487,36129,36775,37423,38073,38724,39375,40027,40679,41329,41978,
61     42625,43270,43911,44548,45181,45808,46429,47044,47652,48253,48845,49430,
62     50005,50571,51128,51674,52209,52733,53245,53745,54232,54706,55167,55614,
63     56048,56468,56876,57269,57651,58019,58376,58720,59052,59373,59681,59979,
64     60265,60539,60803,61056,61298,61529,61750,61962,62163,62355,62538,62712,
65     62877,63034,63183,63325,63459,63586,63706,63820,63927,64028,64123,64213,
66     64297,64376,64451,64520,64586,64647,64704,64758,64808,64855,64899,64941,
67     64980,65016,65051,65083,65114,65143,65169,65194,65218,65240,65260,65278,
68     65296,65312,65327,65341,65354,65366,65377,65387,65397,65406,65415,65423,
69     65430,65437,65444,65450,65455,65461,65466,65470,65475,65479,65483,65486,
70     65489,65493,65495,65498,65501,65503,65505,65507,65509,65511,65513,65514,
71     65516,65517,65518,65519,65520,65521,65522,65523,65524,65525,65526,65526,
72     65527,65527,65528,65528,65529,65529,65530,65530,65530,65531,65531,65531,
73     65532,65532,65532,65532,65532,65533,65533,65533,65533,65533,65533,65533,
74     65533,65534,65534,65534,65534,65534,65534,65534,65534,65534,65534,65534,
75     65534,65534,65534,65534,65535
76     };
77    
78     #define CIE_Y_WLMIN 386
79     #define CIE_Y_WLMAX 760
80    
81     static const unsigned short cie_y_cumul[CIE_Y_WLMAX-CIE_Y_WLMIN+1] = {
82     0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,4,4,5,5,6,7,8,8,10,11,12,14,
83     15,17,19,22,25,28,31,35,40,45,50,56,63,70,78,86,95,105,115,126,
84     138,150,164,178,193,208,225,242,260,280,300,321,343,367,391,
85     417,443,472,501,532,564,598,633,670,708,748,790,833,879,926,
86     975,1027,1080,1136,1194,1255,1318,1384,1453,1525,1601,1679,1761,
87     1846,1935,2027,2123,2223,2327,2435,2548,2665,2787,2915,3048,3187,
88     3332,3484,3643,3808,3981,4162,4352,4550,4757,4975,5203,5442,5691,
89     5952,6225,6509,6805,7114,7435,7769,8116,8476,8849,9235,9634,10045,
90     10469,10904,11351,11808,12275,12752,13239,13734,14239,14752,15273,
91     15801,16337,16880,17429,17984,18546,19112,19684,20260,20841,21426,
92     22015,22608,23203,23802,24403,25007,25612,26220,26828,27439,28050,
93     28662,29275,29888,30501,31114,31727,32340,32951,33561,34170,34777,
94     35382,35985,36585,37182,37777,38368,38955,39539,40119,40695,41266,
95     41832,42393,42950,43501,44046,44586,45119,45646,46167,46682,47189,
96     47690,48183,48670,49149,49621,50085,50542,50991,51432,51866,52293,
97     52711,53122,53524,53919,54306,54685,55057,55420,55775,56123,56463,
98     56795,57119,57435,57743,58044,58337,58623,58901,59172,59435,59691,
99     59939,60180,60414,60640,60859,61070,61274,61471,61661,61844,62019,
100     62188,62351,62507,62657,62802,62940,63073,63201,63323,63441,63553,
101     63660,63763,63861,63954,64043,64128,64208,64285,64358,64427,64493,
102     64555,64614,64670,64723,64773,64820,64865,64907,64947,64984,65019,
103     65052,65083,65113,65140,65166,65190,65212,65233,65253,65271,65288,
104     65304,65319,65334,65347,65360,65371,65383,65393,65403,65412,65420,
105     65428,65435,65442,65449,65454,65460,65465,65470,65474,65478,65482,
106     65485,65488,65492,65494,65497,65500,65502,65504,65506,65508,65510,
107     65512,65513,65515,65516,65517,65519,65520,65521,65522,65523,65523,
108     65524,65525,65526,65526,65527,65527,65528,65528,65529,65529,65530,
109     65530,65530,65531,65531,65531,65532,65532,65532,65532,65532,65533,
110     65533,65533,65533,65533,65533,65533,65534,65534,65534,65534,65534,
111     65534,65534,65534,65534,65534,65534,65534,65534,65534,65534,65534,65535
112     };
113    
114     #define CIE_Z_WLMIN 359
115     #define CIE_Z_WLMAX 624
116    
117     static const unsigned short cie_z_cumul[CIE_Z_WLMAX-CIE_Z_WLMIN+1] = {
118     0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,19,22,25,28,32,37,41,47,52,59,66,75,84,
119     95,107,121,136,154,173,196,221,250,283,321,362,408,458,512,573,640,717,804,
120     903,1015,1142,1285,1446,1627,1830,2058,2313,2598,2917,3272,3668,4108,4597,
121     5135,5723,6360,7044,7773,8544,9356,10205,11090,12006,12952,13923,14918,
122     15934,16967,18016,19076,20148,21227,22312,23401,24492,25585,26678,27771,
123     28861,29950,31037,32121,33202,34281,35355,36424,37487,38542,39588,40624,
124     41647,42657,43652,44631,45590,46527,47438,48321,49173,49993,50783,51542,
125     52270,52968,53636,54275,54885,55465,56018,56543,57042,57514,57961,58384,
126     58784,59162,59519,59856,60175,60477,60762,61032,61287,61529,61757,61974,
127     62179,62374,62559,62734,62901,63059,63211,63355,63492,63622,63745,63862,
128     63971,64075,64172,64263,64347,64427,64500,64569,64632,64692,64747,64798,
129     64846,64891,64933,64973,65010,65045,65078,65109,65139,65166,65192,65216,
130     65239,65260,65280,65298,65315,65331,65345,65359,65371,65383,65393,65403,
131     65412,65420,65428,65435,65441,65447,65452,65457,65462,65466,65470,65473,
132     65476,65479,65482,65485,65487,65489,65491,65493,65495,65497,65498,65500,
133     65501,65503,65504,65505,65506,65508,65509,65510,65511,65512,65513,65514,
134     65515,65516,65517,65518,65519,65520,65520,65521,65522,65523,65523,65524,
135     65525,65525,65526,65527,65527,65528,65528,65529,65529,65530,65530,65531,
136     65531,65531,65532,65532,65532,65532,65533,65533,65533,65533,65533,65533,
137     65534,65534,65534,65534,65534,65534,65534,65534,65534,65535
138     };
139    
140     #define SCOTOPIC_WLMIN 382
141     #define SCOTOPIC_WLMAX 684
142    
143     static const unsigned short scotopic_cumul[SCOTOPIC_WLMAX-SCOTOPIC_WLMIN+1] = {
144     0, 1, 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 15, 17, 20, 24, 28, 33, 38, 44,
145     52, 60, 69, 80, 93, 107, 123, 142, 163, 186, 213, 242, 275, 312, 353,
146     398, 448, 502, 562, 627, 698, 775, 859, 949, 1046, 1150, 1261, 1380,
147     1507, 1642, 1785, 1936, 2096, 2265, 2442, 2628, 2823, 3027, 3239, 3461,
148     3691, 3930, 4178, 4435, 4700, 4974, 5257, 5548, 5847, 6154, 6469, 6793,
149     7123, 7462, 7809, 8162, 8524, 8892, 9268, 9651, 10041, 10438, 10843, 11254,
150     11673, 12099, 12532, 12973, 13422, 13878, 14342, 14814, 15293, 15780, 16276,
151     16779, 17290, 17809, 18336, 18872, 19415, 19967, 20526, 21093, 21667, 22249,
152     22839, 23436, 24039, 24649, 25266, 25890, 26519, 27154, 27795, 28441, 29092,
153     29747, 30405, 31068, 31734, 32402, 33074, 33747, 34420, 35095, 35771, 36446,
154     37119, 37793, 38464, 39132, 39798, 40460, 41118, 41772, 42421, 43064, 43701,
155     44332, 44957, 45575, 46185, 46787, 47381, 47967, 48543, 49110, 49668, 50215,
156     50753, 51280, 51797, 52302, 52797, 53281, 53754, 54215, 54665, 55104, 55531,
157     55947, 56352, 56744, 57125, 57495, 57853, 58200, 58536, 58860, 59174, 59477,
158     59769, 60051, 60322, 60583, 60834, 61075, 61306, 61528, 61741, 61944, 62139,
159     62326, 62504, 62674, 62836, 62991, 63138, 63278, 63412, 63538, 63659, 63773,
160     63881, 63983, 64080, 64172, 64259, 64340, 64418, 64490, 64559, 64623, 64684,
161     64742, 64795, 64846, 64893, 64937, 64978, 65017, 65053, 65087, 65119, 65149,
162     65176, 65202, 65226, 65248, 65269, 65289, 65307, 65323, 65339, 65353, 65367,
163     65379, 65391, 65402, 65412, 65421, 65430, 65438, 65445, 65452, 65458, 65464,
164     65469, 65474, 65479, 65483, 65487, 65491, 65494, 65497, 65500, 65503, 65505,
165     65507, 65509, 65511, 65513, 65514, 65516, 65517, 65519, 65520, 65521, 65522,
166     65523, 65524, 65525, 65525, 65526, 65527, 65527, 65528, 65528, 65529, 65529,
167     65529, 65530, 65530, 65530, 65531, 65531, 65531, 65531, 65532, 65532, 65532,
168     65532, 65532, 65533, 65533, 65533, 65533, 65533, 65533, 65533, 65533, 65533,
169     65533, 65534, 65534, 65534, 65534, 65534, 65534, 65534, 65534, 65535
170     };
171    
172     #define MELANOPIC_WLMIN 380
173     #define MELANOPIC_WLMAX 651
174    
175     static const unsigned short melanopic_cumul[MELANOPIC_WLMAX-MELANOPIC_WLMIN+1] = {
176     0, 1, 2, 3, 4, 5, 7, 8, 10, 12, 14, 17, 20, 23, 27, 32, 37, 42, 49, 56,
177     65, 75, 86, 99, 114, 131, 150, 173, 200, 230, 265, 303, 347, 395, 448,
178     508, 574, 650, 734, 828, 931, 1041, 1158, 1283, 1415, 1554, 1703, 1862,
179     2031, 2211, 2400, 2600, 2809, 3028, 3257, 3497, 3748, 4012, 4288, 4576,
180     4876, 5187, 5509, 5842, 6185, 6540, 6905, 7283, 7673, 8075, 8489, 8915,
181     9351, 9799, 10259, 10729, 11211, 11705, 12211, 12729, 13258, 13799,
182     14351, 14915, 15491, 16078, 16676, 17286, 17908, 18541, 19184, 19837,
183     20498, 21168, 21846, 22532, 23226, 23928, 24637, 25353, 26075, 26802,
184     27532, 28267, 29005, 29746, 30488, 31233, 31979, 32726, 33473, 34220,
185     34966, 35711, 36455, 37196, 37935, 38671, 39403, 40129, 40851, 41568,
186     42279, 42983, 43680, 44369, 45050, 45724, 46388, 47043, 47688, 48322,
187     48945, 49557, 50156, 50743, 51317, 51879, 52428, 52964, 53487, 53997,
188     54493, 54976, 55445, 55901, 56343, 56771, 57186, 57587, 57976, 58351,
189     58712, 59061, 59397, 59720, 60031, 60330, 60616, 60891, 61154, 61405,
190     61646, 61875, 62094, 62303, 62501, 62690, 62870, 63040, 63201, 63354,
191     63498, 63634, 63763, 63884, 63998, 64105, 64206, 64300, 64389, 64472,
192     64549, 64622, 64689, 64753, 64811, 64866, 64917, 64964, 65008, 65049,
193     65086, 65121, 65154, 65184, 65211, 65237, 65260, 65282, 65302, 65321,
194     65338, 65354, 65368, 65381, 65394, 65405, 65415, 65425, 65434, 65442,
195     65449, 65456, 65463, 65468, 65474, 65479, 65483, 65487, 65491, 65494,
196     65498, 65501, 65503, 65506, 65508, 65510, 65512, 65514, 65515, 65517,
197     65518, 65520, 65521, 65522, 65523, 65524, 65525, 65525, 65526, 65527,
198     65527, 65528, 65528, 65529, 65529, 65530, 65530, 65530, 65531, 65531,
199     65531, 65531, 65532, 65532, 65532, 65532, 65532, 65533, 65533, 65533,
200     65533, 65533, 65533, 65533, 65533, 65533, 65534, 65534, 65534, 65535
201 greg 1.1 };
202    
203 greg 2.11 COLORMAT xyz2rgbmat = { /* XYZ to RGB (no white balance) */
204 greg 2.3 {(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g)/CIE_C_rD,
205     (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b)/CIE_C_rD,
206     (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g)/CIE_C_rD},
207     {(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b)/CIE_C_gD,
208     (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r)/CIE_C_gD,
209     (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b)/CIE_C_gD},
210     {(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r)/CIE_C_bD,
211     (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g)/CIE_C_bD,
212     (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r)/CIE_C_bD}
213 greg 1.2 };
214 greg 1.1
215 greg 2.11 COLORMAT rgb2xyzmat = { /* RGB to XYZ (no white balance) */
216 greg 2.4 {CIE_x_r*CIE_C_rD/CIE_D,CIE_x_g*CIE_C_gD/CIE_D,CIE_x_b*CIE_C_bD/CIE_D},
217     {CIE_y_r*CIE_C_rD/CIE_D,CIE_y_g*CIE_C_gD/CIE_D,CIE_y_b*CIE_C_bD/CIE_D},
218     {(1.-CIE_x_r-CIE_y_r)*CIE_C_rD/CIE_D,
219     (1.-CIE_x_g-CIE_y_g)*CIE_C_gD/CIE_D,
220     (1.-CIE_x_b-CIE_y_b)*CIE_C_bD/CIE_D}
221     };
222 greg 1.2
223 greg 2.11 COLORMAT vkmat = { /* Sharp primary matrix */
224     { 1.2694, -0.0988, -0.1706},
225     {-0.8364, 1.8006, 0.0357},
226     { 0.0297, -0.0315, 1.0018}
227     };
228    
229     COLORMAT ivkmat = { /* inverse Sharp primary matrix */
230     { 0.8156, 0.0472, 0.1372},
231     { 0.3791, 0.5769, 0.0440},
232     {-0.0123, 0.0167, 0.9955}
233     };
234 greg 1.2
235 greg 2.4
236 greg 2.11 void
237 greg 2.27 spec_cie( /* compute XYZ color from a spectral range */
238     COLOR col, /* returned color */
239     int s, /* starting and ending wavelengths */
240     int e
241     )
242     {
243     if (s > e) { int i = s; s = e; e = i; }
244    
245     if ((s >= CIE_X_WLMAX) | (e <= CIE_X_WLMIN))
246     col[CIEX] = 0;
247     else
248     col[CIEX] = (cie_x_cumul[e>=CIE_X_WLMAX ? CIE_X_WLMAX-CIE_X_WLMIN : e-CIE_X_WLMIN] -
249     cie_x_cumul[(s>CIE_X_WLMIN)*(s-CIE_X_WLMIN)])*(1./65535.);
250    
251     if ((s >= CIE_Y_WLMAX) | (e <= CIE_Y_WLMIN))
252     col[CIEY] = 0;
253     else
254     col[CIEY] = (cie_y_cumul[e>=CIE_Y_WLMAX ? CIE_Y_WLMAX-CIE_Y_WLMIN : e-CIE_Y_WLMIN] -
255     cie_y_cumul[(s>CIE_Y_WLMIN)*(s-CIE_Y_WLMIN)])*(1./65535.);
256    
257     if ((s >= CIE_Z_WLMAX) | (e <= CIE_Z_WLMIN))
258     col[CIEZ] = 0;
259     else
260     col[CIEZ] = (cie_z_cumul[e>=CIE_Z_WLMAX ? CIE_Z_WLMAX-CIE_Z_WLMIN : e-CIE_Z_WLMIN] -
261     cie_z_cumul[(s>CIE_Z_WLMIN)*(s-CIE_Z_WLMIN)])*(1./65535.);
262     }
263    
264    
265     void
266 greg 2.15 spec_rgb( /* compute RGB color from spectral range */
267     COLOR col,
268     int s,
269     int e
270     )
271 greg 1.1 {
272     COLOR ciecolor;
273    
274     spec_cie(ciecolor, s, e);
275 greg 2.31 colortrans(col, xyz2rgbmat, ciecolor);
276 greg 1.1 }
277    
278    
279 greg 2.27 static double
280     spec_dot( /* spectrum dot-product with cumulative observer */
281 greg 2.34 const SCOLOR scol,
282 greg 2.27 int ncs,
283 greg 2.29 const float wlpt[4],
284 greg 2.27 const unsigned short cumul[],
285     int wlmin,
286     int wlmax
287     )
288     {
289     const double wlstp = (wlpt[0] - wlpt[3])/(double)ncs;
290     int wl1 = (int)wlpt[3];
291     int n = 0;
292     double sum = 0;
293    
294     if (wl1 < wlmin) {
295     n += (int)((wlmin - wl1)/wlstp);
296     wl1 = wlmin;
297     } else if (wl1 >= wlmax)
298     return(.0);
299    
300     while (n < ncs) {
301     int wl0 = wl1;
302 greg 2.28 wl1 = (int)(ncs==3 ? wlpt[2-n] : wlpt[3] + (n+1)*wlstp);
303 greg 2.27 if (wl1 >= wlmax) {
304 greg 2.28 sum += (65535 - cumul[wl0-wlmin]) * scol[ncs-1-n];
305 greg 2.27 break;
306     }
307 greg 2.28 sum += (cumul[wl1-wlmin] - cumul[wl0-wlmin]) * scol[ncs-1-n++];
308 greg 2.27 }
309     return(sum * (1./65535.));
310     }
311    
312    
313 greg 2.11 void
314 greg 2.27 scolor2cie( /* accurate conversion from spectrum to XYZ */
315     COLOR col,
316 greg 2.34 const SCOLOR scol,
317 greg 2.27 int ncs,
318 greg 2.29 const float wlpt[4]
319 greg 2.27 )
320     {
321     if (ncs == 3) { /* not a spectrum! */
322     rgb_cie(col, scol);
323     return;
324     }
325     col[CIEX] = spec_dot(scol, ncs, wlpt, cie_x_cumul, CIE_X_WLMIN, CIE_X_WLMAX);
326     col[CIEY] = spec_dot(scol, ncs, wlpt, cie_y_cumul, CIE_Y_WLMIN, CIE_Y_WLMAX);
327     col[CIEZ] = spec_dot(scol, ncs, wlpt, cie_z_cumul, CIE_Z_WLMIN, CIE_Z_WLMAX);
328     }
329    
330    
331     void
332     scolor2rgb( /* accurate conversion from spectrum to RGB */
333     COLOR col,
334 greg 2.34 const SCOLOR scol,
335 greg 2.27 int ncs,
336 greg 2.29 const float wlpt[4]
337 greg 2.15 )
338 greg 1.1 {
339 greg 2.27 COLOR ciecolor;
340    
341     if (ncs == 3) { /* not really a spectrum, so we punt... */
342     copycolor(col, scol);
343 greg 1.2 return;
344     }
345 greg 2.27 scolor2cie(ciecolor, scol, ncs, wlpt);
346     cie_rgb(col, ciecolor);
347     }
348    
349 greg 1.1
350 greg 2.33 void
351     scolor_out( /* prepare (spectral) color for output */
352     COLORV *cout,
353 greg 2.34 RGBPRIMS pr,
354     const SCOLOR cres
355 greg 2.33 )
356     {
357     static COLORMAT xyz2myp;
358     static RGBPRIMP lastp = NULL;
359    
360 greg 2.34 if (!pr) { /* output is spectral */
361 greg 2.33 copyscolor(cout, cres);
362 greg 2.34 } else if (pr == stdprims) { /* output is standard RGB */
363 greg 2.33 scolor_rgb(cout, cres);
364 greg 2.34 } else if (pr == xyzprims) { /* output is XYZ */
365 greg 2.33 scolor_cie(cout, cres);
366     scalecolor(cout, WHTEFFICACY);
367     } else if (NCSAMP > 3) { /* spectral -> custom RGB */
368     COLOR xyz;
369 greg 2.34 if (lastp != pr)
370     compxyz2rgbWBmat(xyz2myp, lastp=pr);
371 greg 2.33 scolor_cie(xyz, cres);
372     colortrans(cout, xyz2myp, xyz);
373     clipgamut(cout, xyz[CIEY], CGAMUT_LOWER, cblack, cwhite);
374     } else { /* else copy unknown RGB */
375     copycolor(cout, cres);
376     }
377     }
378    
379    
380 greg 2.27 double
381 greg 2.30 scolor2photopic( /* compute scotopic integral for spectral color */
382 greg 2.34 const SCOLOR scol,
383 greg 2.30 int ncs,
384     const float wlpt[4]
385     )
386     {
387     if (ncs == 3)
388     return bright(scol);
389    
390     return(spec_dot(scol, ncs, wlpt, cie_y_cumul, CIE_Y_WLMIN, CIE_Y_WLMAX));
391     }
392    
393    
394     double
395     scolor2scotopic( /* compute Y channel for spectral color */
396 greg 2.34 const SCOLOR scol,
397 greg 2.30 int ncs,
398     const float wlpt[4]
399     )
400     {
401     return(spec_dot(scol, ncs, wlpt, scotopic_cumul, SCOTOPIC_WLMIN, SCOTOPIC_WLMAX));
402     }
403    
404    
405     double
406     scolor2melanopic( /* compute melanopic integral for spectral color */
407 greg 2.34 const SCOLOR scol,
408 greg 2.30 int ncs,
409     const float wlpt[4]
410     )
411     {
412     return(spec_dot(scol, ncs, wlpt, melanopic_cumul, MELANOPIC_WLMIN, MELANOPIC_WLMAX));
413     }
414    
415    
416     double
417 greg 2.27 scolor_photopic( /* compute scotopic integral for spectral color */
418 greg 2.34 const SCOLOR scol
419 greg 2.27 )
420     {
421 greg 2.30 return(scolor2photopic(scol, NCSAMP, WLPART));
422 greg 2.27 }
423    
424    
425     double
426     scolor_scotopic( /* compute Y channel for spectral color */
427 greg 2.34 const SCOLOR scol
428 greg 2.27 )
429     {
430 greg 2.30 return(scolor2scotopic(scol, NCSAMP, WLPART));
431 greg 2.27 }
432 greg 1.1
433    
434 greg 2.27 double
435     scolor_melanopic( /* compute melanopic integral for spectral color */
436 greg 2.34 const SCOLOR scol
437 greg 2.27 )
438     {
439 greg 2.30 return(scolor2melanopic(scol, NCSAMP, WLPART));
440 greg 1.1 }
441    
442    
443 greg 2.11 void
444 greg 2.32 convertscolorcol( /* any uniform spectrum to working */
445     SCOLOR rcol,
446     const COLORV src[],
447     int snc,
448     double swl0,
449     double swl1
450     )
451     {
452     if (NCSAMP > 3) { /* spectrum -> spectrum */
453     convertscolor(rcol, NCSAMP, WLPART[0], WLPART[3],
454     src, snc, swl0, swl1);
455     } else if ((snc <= MAXCSAMP) & (swl0 > swl1)) {
456     float wlpt[4]; /* no intermediate conversion needed */
457     wlpt[0] = swl0; wlpt[3] = swl1;
458     wlpt[1] = WLPART[1]; wlpt[2] = WLPART[2];
459     scolor2rgb(rcol, (COLORV *)src, snc, wlpt);
460     } else {
461     SCOLOR dcol; /* else convert spectrum first */
462     int dnc = snc*(WLPART[0] - WLPART[3])/fabs(swl0 - swl1) + .99;
463     if (dnc > MAXCSAMP) dnc = MAXCSAMP;
464     convertscolor(dcol, dnc, WLPART[0], WLPART[3], src, snc, swl0, swl1);
465     scolor2rgb(rcol, dcol, dnc, WLPART);
466     }
467     }
468    
469    
470     void
471 greg 2.15 cie_rgb( /* convert CIE color to standard RGB */
472     COLOR rgb,
473 greg 2.34 const COLOR xyz
474 greg 2.15 )
475 greg 2.8 {
476     colortrans(rgb, xyz2rgbmat, xyz);
477     clipgamut(rgb, xyz[CIEY], CGAMUT_LOWER, cblack, cwhite);
478     }
479    
480    
481     int
482 greg 2.15 clipgamut( /* clip to gamut cube */
483     COLOR col,
484     double brt,
485     int gamut,
486 greg 2.34 const COLOR lower,
487     const COLOR upper
488 greg 2.15 )
489 greg 2.8 {
490     int rflags = 0;
491     double brtmin, brtmax, v, vv;
492     COLOR cgry;
493 greg 2.25 int i;
494 greg 2.8 /* check for no check */
495     if (gamut == 0) return(0);
496     /* check brightness limits */
497     brtmin = 1./3.*(lower[0]+lower[1]+lower[2]);
498     if (gamut & CGAMUT_LOWER && brt < brtmin) {
499     copycolor(col, lower);
500     return(CGAMUT_LOWER);
501     }
502     brtmax = 1./3.*(upper[0]+upper[1]+upper[2]);
503     if (gamut & CGAMUT_UPPER && brt > brtmax) {
504     copycolor(col, upper);
505     return(CGAMUT_UPPER);
506     }
507     /* compute equivalent grey */
508     v = (brt - brtmin)/(brtmax - brtmin);
509     for (i = 0; i < 3; i++)
510     cgry[i] = v*upper[i] + (1.-v)*lower[i];
511     vv = 1.; /* check each limit */
512     for (i = 0; i < 3; i++)
513     if (gamut & CGAMUT_LOWER && col[i] < lower[i]) {
514 greg 2.14 v = (lower[i] - cgry[i])/(col[i] - cgry[i]);
515 greg 2.8 if (v < vv) vv = v;
516     rflags |= CGAMUT_LOWER;
517     } else if (gamut & CGAMUT_UPPER && col[i] > upper[i]) {
518 greg 2.14 v = (upper[i] - cgry[i])/(col[i] - cgry[i]);
519 greg 2.8 if (v < vv) vv = v;
520     rflags |= CGAMUT_UPPER;
521     }
522     if (rflags) /* desaturate to cube face */
523     for (i = 0; i < 3; i++)
524     col[i] = vv*col[i] + (1.-vv)*cgry[i];
525     return(rflags);
526     }
527    
528    
529 greg 2.11 void
530 greg 2.15 colortrans( /* convert c1 by mat and put into c2 */
531 greg 2.25 COLOR c2,
532 greg 2.34 const COLORMAT mat,
533     const COLOR c1
534 greg 2.15 )
535 greg 1.1 {
536 greg 2.9 COLOR cout;
537    
538     cout[0] = mat[0][0]*c1[0] + mat[0][1]*c1[1] + mat[0][2]*c1[2];
539     cout[1] = mat[1][0]*c1[0] + mat[1][1]*c1[1] + mat[1][2]*c1[2];
540     cout[2] = mat[2][0]*c1[0] + mat[2][1]*c1[1] + mat[2][2]*c1[2];
541    
542     copycolor(c2, cout);
543 greg 2.4 }
544    
545    
546 greg 2.11 void
547 greg 2.15 multcolormat( /* multiply m1 by m2 and put into m3 */
548     COLORMAT m3, /* m3 can be either m1 or m2 w/o harm */
549 greg 2.34 const COLORMAT m2,
550     const COLORMAT m1
551 greg 2.15 )
552 greg 2.4 {
553 greg 2.5 COLORMAT mt;
554 greg 2.25 int i, j;
555 greg 2.4
556     for (i = 0; i < 3; i++)
557 greg 2.5 for (j = 0; j < 3; j++)
558     mt[i][j] = m1[i][0]*m2[0][j] +
559     m1[i][1]*m2[1][j] +
560     m1[i][2]*m2[2][j] ;
561     cpcolormat(m3, mt);
562     }
563    
564    
565 greg 2.15 int
566     colorprimsOK( /* are color primaries reasonable? */
567     RGBPRIMS pr
568     )
569     {
570 greg 2.20 int i, j;
571 greg 2.26 /* check white point */
572     if ((pr[3][CIEX] <= CEPS) | (pr[3][CIEX] >= 1.-CEPS) |
573     (pr[3][CIEY] <= CEPS) | (pr[3][CIEY] >= 1.-CEPS))
574     return(0);
575     for (i = 3; i--; ) /* check for XYZ color primaries */
576     if (!CEQ(pr[i][CIEX],(i==0)) | !CEQ(pr[i][CIEY],(i==1)))
577     break;
578     if (i < 0)
579     return(-1); /* flag as XYZ color space */
580     /* check color primaries */
581 greg 2.22 for (i = 0; i < 3; i++) {
582 greg 2.24 if ((pr[i][CIEX] <= -2.) | (pr[i][CIEY] <= -2.))
583 greg 2.15 return(0);
584 greg 2.24 if ((pr[i][CIEX] >= 3.) | (pr[i][CIEY] >= 3.))
585 greg 2.17 return(0);
586 greg 2.24 if (pr[i][CIEX] + pr[i][CIEY] <= -2.)
587 greg 2.21 return(0);
588 greg 2.24 if (pr[i][CIEX] + pr[i][CIEY] >= 3.)
589 greg 2.15 return(0);
590     }
591 greg 2.26 for (i = 0; i < 4; i++) /* make sure space is 3-dimensional */
592 greg 2.20 for (j = i+1; j < 4; j++)
593 greg 2.26 if (CEQ(pr[i][CIEX],pr[j][CIEX]) &
594 greg 2.20 CEQ(pr[i][CIEY],pr[j][CIEY]))
595     return(0);
596 greg 2.15 return(1);
597     }
598    
599    
600    
601     int
602     compxyz2rgbmat( /* compute conversion from CIE to RGB space */
603     COLORMAT mat,
604 greg 2.25 RGBPRIMS pr
605 greg 2.15 )
606 greg 2.5 {
607     double C_rD, C_gD, C_bD;
608    
609 greg 2.6 if (pr == stdprims) { /* can use xyz2rgbmat */
610     cpcolormat(mat, xyz2rgbmat);
611 greg 2.16 return(1);
612 greg 2.6 }
613 greg 2.27 if (pr == xyzprims) { /* identity */
614     memset(mat, 0, sizeof(COLORMAT));
615     mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
616     return(1);
617     }
618 greg 2.15 if (CEQ(pr[WHT][CIEX],0.) | CEQ(pr[WHT][CIEY],0.))
619     return(0);
620 greg 2.5 C_rD = (1./pr[WHT][CIEY]) *
621     ( pr[WHT][CIEX]*(pr[GRN][CIEY] - pr[BLU][CIEY]) -
622     pr[WHT][CIEY]*(pr[GRN][CIEX] - pr[BLU][CIEX]) +
623     pr[GRN][CIEX]*pr[BLU][CIEY] - pr[BLU][CIEX]*pr[GRN][CIEY] ) ;
624 greg 2.15 if (CEQ(C_rD,0.))
625     return(0);
626 greg 2.5 C_gD = (1./pr[WHT][CIEY]) *
627     ( pr[WHT][CIEX]*(pr[BLU][CIEY] - pr[RED][CIEY]) -
628     pr[WHT][CIEY]*(pr[BLU][CIEX] - pr[RED][CIEX]) -
629     pr[RED][CIEX]*pr[BLU][CIEY] + pr[BLU][CIEX]*pr[RED][CIEY] ) ;
630 greg 2.15 if (CEQ(C_gD,0.))
631     return(0);
632 greg 2.5 C_bD = (1./pr[WHT][CIEY]) *
633     ( pr[WHT][CIEX]*(pr[RED][CIEY] - pr[GRN][CIEY]) -
634     pr[WHT][CIEY]*(pr[RED][CIEX] - pr[GRN][CIEX]) +
635     pr[RED][CIEX]*pr[GRN][CIEY] - pr[GRN][CIEX]*pr[RED][CIEY] ) ;
636 greg 2.15 if (CEQ(C_bD,0.))
637     return(0);
638 greg 2.5 mat[0][0] = (pr[GRN][CIEY] - pr[BLU][CIEY] -
639     pr[BLU][CIEX]*pr[GRN][CIEY] +
640     pr[BLU][CIEY]*pr[GRN][CIEX])/C_rD ;
641     mat[0][1] = (pr[BLU][CIEX] - pr[GRN][CIEX] -
642     pr[BLU][CIEX]*pr[GRN][CIEY] +
643     pr[GRN][CIEX]*pr[BLU][CIEY])/C_rD ;
644     mat[0][2] = (pr[GRN][CIEX]*pr[BLU][CIEY] -
645     pr[BLU][CIEX]*pr[GRN][CIEY])/C_rD ;
646     mat[1][0] = (pr[BLU][CIEY] - pr[RED][CIEY] -
647     pr[BLU][CIEY]*pr[RED][CIEX] +
648     pr[RED][CIEY]*pr[BLU][CIEX])/C_gD ;
649     mat[1][1] = (pr[RED][CIEX] - pr[BLU][CIEX] -
650     pr[RED][CIEX]*pr[BLU][CIEY] +
651     pr[BLU][CIEX]*pr[RED][CIEY])/C_gD ;
652     mat[1][2] = (pr[BLU][CIEX]*pr[RED][CIEY] -
653     pr[RED][CIEX]*pr[BLU][CIEY])/C_gD ;
654     mat[2][0] = (pr[RED][CIEY] - pr[GRN][CIEY] -
655     pr[RED][CIEY]*pr[GRN][CIEX] +
656     pr[GRN][CIEY]*pr[RED][CIEX])/C_bD ;
657     mat[2][1] = (pr[GRN][CIEX] - pr[RED][CIEX] -
658     pr[GRN][CIEX]*pr[RED][CIEY] +
659     pr[RED][CIEX]*pr[GRN][CIEY])/C_bD ;
660     mat[2][2] = (pr[RED][CIEX]*pr[GRN][CIEY] -
661     pr[GRN][CIEX]*pr[RED][CIEY])/C_bD ;
662 greg 2.15 return(1);
663 greg 2.5 }
664    
665    
666 greg 2.15 int
667     comprgb2xyzmat( /* compute conversion from RGB to CIE space */
668     COLORMAT mat,
669 greg 2.25 RGBPRIMS pr
670 greg 2.15 )
671 greg 2.5 {
672     double C_rD, C_gD, C_bD, D;
673    
674 greg 2.6 if (pr == stdprims) { /* can use rgb2xyzmat */
675     cpcolormat(mat, rgb2xyzmat);
676 greg 2.16 return(1);
677 greg 2.6 }
678 greg 2.27 if (pr == xyzprims) { /* identity */
679     memset(mat, 0, sizeof(COLORMAT));
680     mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
681     return(1);
682     }
683 greg 2.15 if (CEQ(pr[WHT][CIEX],0.) | CEQ(pr[WHT][CIEY],0.))
684     return(0);
685 greg 2.5 C_rD = (1./pr[WHT][CIEY]) *
686     ( pr[WHT][CIEX]*(pr[GRN][CIEY] - pr[BLU][CIEY]) -
687     pr[WHT][CIEY]*(pr[GRN][CIEX] - pr[BLU][CIEX]) +
688     pr[GRN][CIEX]*pr[BLU][CIEY] - pr[BLU][CIEX]*pr[GRN][CIEY] ) ;
689     C_gD = (1./pr[WHT][CIEY]) *
690     ( pr[WHT][CIEX]*(pr[BLU][CIEY] - pr[RED][CIEY]) -
691     pr[WHT][CIEY]*(pr[BLU][CIEX] - pr[RED][CIEX]) -
692     pr[RED][CIEX]*pr[BLU][CIEY] + pr[BLU][CIEX]*pr[RED][CIEY] ) ;
693     C_bD = (1./pr[WHT][CIEY]) *
694     ( pr[WHT][CIEX]*(pr[RED][CIEY] - pr[GRN][CIEY]) -
695     pr[WHT][CIEY]*(pr[RED][CIEX] - pr[GRN][CIEX]) +
696     pr[RED][CIEX]*pr[GRN][CIEY] - pr[GRN][CIEX]*pr[RED][CIEY] ) ;
697     D = pr[RED][CIEX]*(pr[GRN][CIEY] - pr[BLU][CIEY]) +
698     pr[GRN][CIEX]*(pr[BLU][CIEY] - pr[RED][CIEY]) +
699     pr[BLU][CIEX]*(pr[RED][CIEY] - pr[GRN][CIEY]) ;
700 greg 2.15 if (CEQ(D,0.))
701     return(0);
702 greg 2.5 mat[0][0] = pr[RED][CIEX]*C_rD/D;
703     mat[0][1] = pr[GRN][CIEX]*C_gD/D;
704     mat[0][2] = pr[BLU][CIEX]*C_bD/D;
705     mat[1][0] = pr[RED][CIEY]*C_rD/D;
706     mat[1][1] = pr[GRN][CIEY]*C_gD/D;
707     mat[1][2] = pr[BLU][CIEY]*C_bD/D;
708     mat[2][0] = (1.-pr[RED][CIEX]-pr[RED][CIEY])*C_rD/D;
709     mat[2][1] = (1.-pr[GRN][CIEX]-pr[GRN][CIEY])*C_gD/D;
710     mat[2][2] = (1.-pr[BLU][CIEX]-pr[BLU][CIEY])*C_bD/D;
711 greg 2.15 return(1);
712 greg 2.5 }
713    
714    
715 greg 2.15 int
716     comprgb2rgbmat( /* compute conversion from RGB1 to RGB2 */
717     COLORMAT mat,
718     RGBPRIMS pr1,
719     RGBPRIMS pr2
720     )
721 greg 2.5 {
722     COLORMAT pr1toxyz, xyztopr2;
723    
724 greg 2.6 if (pr1 == pr2) {
725 greg 2.27 memset(mat, 0, sizeof(COLORMAT));
726     mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
727 greg 2.16 return(1);
728 greg 2.6 }
729 greg 2.15 if (!comprgb2xyzmat(pr1toxyz, pr1))
730     return(0);
731     if (!compxyz2rgbmat(xyztopr2, pr2))
732     return(0);
733 greg 2.5 /* combine transforms */
734     multcolormat(mat, pr1toxyz, xyztopr2);
735 greg 2.15 return(1);
736 greg 2.11 }
737    
738    
739 greg 2.15 int
740     compxyzWBmat( /* CIE von Kries transform from wht1 to wht2 */
741     COLORMAT mat,
742 greg 2.34 const float wht1[2],
743     const float wht2[2]
744 greg 2.15 )
745 greg 2.11 {
746     COLOR cw1, cw2;
747     if (XYEQ(wht1,wht2)) {
748 greg 2.27 memset(mat, 0, sizeof(COLORMAT));
749     mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
750 greg 2.15 return(1);
751 greg 2.11 }
752 greg 2.15 if (CEQ(wht1[CIEX],0.) | CEQ(wht1[CIEY],0.))
753     return(0);
754 greg 2.11 cw1[RED] = wht1[CIEX]/wht1[CIEY];
755     cw1[GRN] = 1.;
756     cw1[BLU] = (1. - wht1[CIEX] - wht1[CIEY])/wht1[CIEY];
757     colortrans(cw1, vkmat, cw1);
758 greg 2.15 if (CEQ(wht2[CIEX],0.) | CEQ(wht2[CIEY],0.))
759     return(0);
760 greg 2.11 cw2[RED] = wht2[CIEX]/wht2[CIEY];
761     cw2[GRN] = 1.;
762     cw2[BLU] = (1. - wht2[CIEX] - wht2[CIEY])/wht2[CIEY];
763     colortrans(cw2, vkmat, cw2);
764 greg 2.15 if (CEQ(cw1[RED],0.) | CEQ(cw1[GRN],0.) | CEQ(cw1[BLU],0.))
765     return(0);
766 greg 2.11 mat[0][0] = cw2[RED]/cw1[RED];
767     mat[1][1] = cw2[GRN]/cw1[GRN];
768     mat[2][2] = cw2[BLU]/cw1[BLU];
769     mat[0][1] = mat[0][2] = mat[1][0] =
770     mat[1][2] = mat[2][0] = mat[2][1] = 0.0;
771     multcolormat(mat, vkmat, mat);
772     multcolormat(mat, mat, ivkmat);
773 greg 2.15 return(1);
774 greg 2.11 }
775    
776    
777 greg 2.15 int
778     compxyz2rgbWBmat( /* von Kries conversion from CIE to RGB space */
779     COLORMAT mat,
780     RGBPRIMS pr
781     )
782 greg 2.11 {
783     COLORMAT wbmat;
784    
785 greg 2.15 if (!compxyz2rgbmat(mat, pr))
786     return(0);
787 greg 2.11 if (XYEQ(pr[WHT],xyneu))
788 greg 2.15 return(1);
789     if (!compxyzWBmat(wbmat, xyneu, pr[WHT]))
790     return(0);
791 greg 2.11 multcolormat(mat, wbmat, mat);
792 greg 2.15 return(1);
793 greg 2.11 }
794    
795 greg 2.15 int
796     comprgb2xyzWBmat( /* von Kries conversion from RGB to CIE space */
797     COLORMAT mat,
798     RGBPRIMS pr
799     )
800 greg 2.11 {
801     COLORMAT wbmat;
802    
803 greg 2.15 if (!comprgb2xyzmat(mat, pr))
804     return(0);
805 greg 2.11 if (XYEQ(pr[WHT],xyneu))
806 greg 2.15 return(1);
807     if (!compxyzWBmat(wbmat, pr[WHT], xyneu))
808     return(0);
809 greg 2.11 multcolormat(mat, mat, wbmat);
810 greg 2.15 return(1);
811 greg 2.11 }
812    
813 greg 2.15 int
814     comprgb2rgbWBmat( /* von Kries conversion from RGB1 to RGB2 */
815     COLORMAT mat,
816     RGBPRIMS pr1,
817     RGBPRIMS pr2
818     )
819 greg 2.11 {
820     COLORMAT pr1toxyz, xyztopr2, wbmat;
821    
822     if (pr1 == pr2) {
823 greg 2.27 memset(mat, 0, sizeof(COLORMAT));
824     mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
825 greg 2.15 return(1);
826 greg 2.11 }
827 greg 2.15 if (!comprgb2xyzmat(pr1toxyz, pr1))
828     return(0);
829     if (!compxyzWBmat(wbmat, pr1[WHT], pr2[WHT]))
830     return(0);
831     if (!compxyz2rgbmat(xyztopr2, pr2))
832     return(0);
833 greg 2.11 /* combine transforms */
834     multcolormat(mat, pr1toxyz, wbmat);
835     multcolormat(mat, mat, xyztopr2);
836 greg 2.15 return(1);
837 greg 1.1 }