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 (8 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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: spec_rgb.c,v 2.34 2024/08/14 20:05:23 greg Exp $";
3 #endif
4 /*
5 * Convert colors and spectral ranges.
6 * Added von Kries white-balance calculations 10/01 (GW).
7 *
8 * Externals declared in color.h
9 */
10
11 #include "copyright.h"
12
13 #include <stdio.h>
14 #include <string.h>
15 #include <math.h>
16 #include "color.h"
17
18 #define CEPS 1e-4 /* color epsilon */
19
20 #define CEQ(v1,v2) (((v1) <= (v2)+CEPS) & ((v2) <= (v1)+CEPS))
21
22 #define XYEQ(c1,c2) (CEQ((c1)[CIEX],(c2)[CIEX]) & CEQ((c1)[CIEY],(c2)[CIEY]))
23
24
25 RGBPRIMS stdprims = STDPRIMS; /* standard primary chromaticities */
26 RGBPRIMS xyzprims = {1,0, 0,1, 0,0, 1.f/3.f,1.f/3.f};
27
28 const COLOR cblack = BLKCOLOR; /* global black color */
29 const COLOR cwhite = WHTCOLOR; /* global white color */
30 const SCOLOR scblack = {0}; /* global black spectrum */
31
32 float xyneu[2] = {1./3., 1./3.}; /* neutral xy chromaticities */
33
34 /*
35 * 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 */
39
40 #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 };
202
203 COLORMAT xyz2rgbmat = { /* XYZ to RGB (no white balance) */
204 {(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 };
214
215 COLORMAT rgb2xyzmat = { /* RGB to XYZ (no white balance) */
216 {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
223 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
235
236 void
237 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 spec_rgb( /* compute RGB color from spectral range */
267 COLOR col,
268 int s,
269 int e
270 )
271 {
272 COLOR ciecolor;
273
274 spec_cie(ciecolor, s, e);
275 colortrans(col, xyz2rgbmat, ciecolor);
276 }
277
278
279 static double
280 spec_dot( /* spectrum dot-product with cumulative observer */
281 const SCOLOR scol,
282 int ncs,
283 const float wlpt[4],
284 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 wl1 = (int)(ncs==3 ? wlpt[2-n] : wlpt[3] + (n+1)*wlstp);
303 if (wl1 >= wlmax) {
304 sum += (65535 - cumul[wl0-wlmin]) * scol[ncs-1-n];
305 break;
306 }
307 sum += (cumul[wl1-wlmin] - cumul[wl0-wlmin]) * scol[ncs-1-n++];
308 }
309 return(sum * (1./65535.));
310 }
311
312
313 void
314 scolor2cie( /* accurate conversion from spectrum to XYZ */
315 COLOR col,
316 const SCOLOR scol,
317 int ncs,
318 const float wlpt[4]
319 )
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 const SCOLOR scol,
335 int ncs,
336 const float wlpt[4]
337 )
338 {
339 COLOR ciecolor;
340
341 if (ncs == 3) { /* not really a spectrum, so we punt... */
342 copycolor(col, scol);
343 return;
344 }
345 scolor2cie(ciecolor, scol, ncs, wlpt);
346 cie_rgb(col, ciecolor);
347 }
348
349
350 void
351 scolor_out( /* prepare (spectral) color for output */
352 COLORV *cout,
353 RGBPRIMS pr,
354 const SCOLOR cres
355 )
356 {
357 static COLORMAT xyz2myp;
358 static RGBPRIMP lastp = NULL;
359
360 if (!pr) { /* output is spectral */
361 copyscolor(cout, cres);
362 } else if (pr == stdprims) { /* output is standard RGB */
363 scolor_rgb(cout, cres);
364 } else if (pr == xyzprims) { /* output is XYZ */
365 scolor_cie(cout, cres);
366 scalecolor(cout, WHTEFFICACY);
367 } else if (NCSAMP > 3) { /* spectral -> custom RGB */
368 COLOR xyz;
369 if (lastp != pr)
370 compxyz2rgbWBmat(xyz2myp, lastp=pr);
371 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 double
381 scolor2photopic( /* compute scotopic integral for spectral color */
382 const SCOLOR scol,
383 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 const SCOLOR scol,
397 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 const SCOLOR scol,
408 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 scolor_photopic( /* compute scotopic integral for spectral color */
418 const SCOLOR scol
419 )
420 {
421 return(scolor2photopic(scol, NCSAMP, WLPART));
422 }
423
424
425 double
426 scolor_scotopic( /* compute Y channel for spectral color */
427 const SCOLOR scol
428 )
429 {
430 return(scolor2scotopic(scol, NCSAMP, WLPART));
431 }
432
433
434 double
435 scolor_melanopic( /* compute melanopic integral for spectral color */
436 const SCOLOR scol
437 )
438 {
439 return(scolor2melanopic(scol, NCSAMP, WLPART));
440 }
441
442
443 void
444 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 cie_rgb( /* convert CIE color to standard RGB */
472 COLOR rgb,
473 const COLOR xyz
474 )
475 {
476 colortrans(rgb, xyz2rgbmat, xyz);
477 clipgamut(rgb, xyz[CIEY], CGAMUT_LOWER, cblack, cwhite);
478 }
479
480
481 int
482 clipgamut( /* clip to gamut cube */
483 COLOR col,
484 double brt,
485 int gamut,
486 const COLOR lower,
487 const COLOR upper
488 )
489 {
490 int rflags = 0;
491 double brtmin, brtmax, v, vv;
492 COLOR cgry;
493 int i;
494 /* 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 v = (lower[i] - cgry[i])/(col[i] - cgry[i]);
515 if (v < vv) vv = v;
516 rflags |= CGAMUT_LOWER;
517 } else if (gamut & CGAMUT_UPPER && col[i] > upper[i]) {
518 v = (upper[i] - cgry[i])/(col[i] - cgry[i]);
519 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 void
530 colortrans( /* convert c1 by mat and put into c2 */
531 COLOR c2,
532 const COLORMAT mat,
533 const COLOR c1
534 )
535 {
536 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 }
544
545
546 void
547 multcolormat( /* multiply m1 by m2 and put into m3 */
548 COLORMAT m3, /* m3 can be either m1 or m2 w/o harm */
549 const COLORMAT m2,
550 const COLORMAT m1
551 )
552 {
553 COLORMAT mt;
554 int i, j;
555
556 for (i = 0; i < 3; i++)
557 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 int
566 colorprimsOK( /* are color primaries reasonable? */
567 RGBPRIMS pr
568 )
569 {
570 int i, j;
571 /* 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 for (i = 0; i < 3; i++) {
582 if ((pr[i][CIEX] <= -2.) | (pr[i][CIEY] <= -2.))
583 return(0);
584 if ((pr[i][CIEX] >= 3.) | (pr[i][CIEY] >= 3.))
585 return(0);
586 if (pr[i][CIEX] + pr[i][CIEY] <= -2.)
587 return(0);
588 if (pr[i][CIEX] + pr[i][CIEY] >= 3.)
589 return(0);
590 }
591 for (i = 0; i < 4; i++) /* make sure space is 3-dimensional */
592 for (j = i+1; j < 4; j++)
593 if (CEQ(pr[i][CIEX],pr[j][CIEX]) &
594 CEQ(pr[i][CIEY],pr[j][CIEY]))
595 return(0);
596 return(1);
597 }
598
599
600
601 int
602 compxyz2rgbmat( /* compute conversion from CIE to RGB space */
603 COLORMAT mat,
604 RGBPRIMS pr
605 )
606 {
607 double C_rD, C_gD, C_bD;
608
609 if (pr == stdprims) { /* can use xyz2rgbmat */
610 cpcolormat(mat, xyz2rgbmat);
611 return(1);
612 }
613 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 if (CEQ(pr[WHT][CIEX],0.) | CEQ(pr[WHT][CIEY],0.))
619 return(0);
620 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 if (CEQ(C_rD,0.))
625 return(0);
626 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 if (CEQ(C_gD,0.))
631 return(0);
632 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 if (CEQ(C_bD,0.))
637 return(0);
638 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 return(1);
663 }
664
665
666 int
667 comprgb2xyzmat( /* compute conversion from RGB to CIE space */
668 COLORMAT mat,
669 RGBPRIMS pr
670 )
671 {
672 double C_rD, C_gD, C_bD, D;
673
674 if (pr == stdprims) { /* can use rgb2xyzmat */
675 cpcolormat(mat, rgb2xyzmat);
676 return(1);
677 }
678 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 if (CEQ(pr[WHT][CIEX],0.) | CEQ(pr[WHT][CIEY],0.))
684 return(0);
685 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 if (CEQ(D,0.))
701 return(0);
702 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 return(1);
712 }
713
714
715 int
716 comprgb2rgbmat( /* compute conversion from RGB1 to RGB2 */
717 COLORMAT mat,
718 RGBPRIMS pr1,
719 RGBPRIMS pr2
720 )
721 {
722 COLORMAT pr1toxyz, xyztopr2;
723
724 if (pr1 == pr2) {
725 memset(mat, 0, sizeof(COLORMAT));
726 mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
727 return(1);
728 }
729 if (!comprgb2xyzmat(pr1toxyz, pr1))
730 return(0);
731 if (!compxyz2rgbmat(xyztopr2, pr2))
732 return(0);
733 /* combine transforms */
734 multcolormat(mat, pr1toxyz, xyztopr2);
735 return(1);
736 }
737
738
739 int
740 compxyzWBmat( /* CIE von Kries transform from wht1 to wht2 */
741 COLORMAT mat,
742 const float wht1[2],
743 const float wht2[2]
744 )
745 {
746 COLOR cw1, cw2;
747 if (XYEQ(wht1,wht2)) {
748 memset(mat, 0, sizeof(COLORMAT));
749 mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
750 return(1);
751 }
752 if (CEQ(wht1[CIEX],0.) | CEQ(wht1[CIEY],0.))
753 return(0);
754 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 if (CEQ(wht2[CIEX],0.) | CEQ(wht2[CIEY],0.))
759 return(0);
760 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 if (CEQ(cw1[RED],0.) | CEQ(cw1[GRN],0.) | CEQ(cw1[BLU],0.))
765 return(0);
766 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 return(1);
774 }
775
776
777 int
778 compxyz2rgbWBmat( /* von Kries conversion from CIE to RGB space */
779 COLORMAT mat,
780 RGBPRIMS pr
781 )
782 {
783 COLORMAT wbmat;
784
785 if (!compxyz2rgbmat(mat, pr))
786 return(0);
787 if (XYEQ(pr[WHT],xyneu))
788 return(1);
789 if (!compxyzWBmat(wbmat, xyneu, pr[WHT]))
790 return(0);
791 multcolormat(mat, wbmat, mat);
792 return(1);
793 }
794
795 int
796 comprgb2xyzWBmat( /* von Kries conversion from RGB to CIE space */
797 COLORMAT mat,
798 RGBPRIMS pr
799 )
800 {
801 COLORMAT wbmat;
802
803 if (!comprgb2xyzmat(mat, pr))
804 return(0);
805 if (XYEQ(pr[WHT],xyneu))
806 return(1);
807 if (!compxyzWBmat(wbmat, pr[WHT], xyneu))
808 return(0);
809 multcolormat(mat, mat, wbmat);
810 return(1);
811 }
812
813 int
814 comprgb2rgbWBmat( /* von Kries conversion from RGB1 to RGB2 */
815 COLORMAT mat,
816 RGBPRIMS pr1,
817 RGBPRIMS pr2
818 )
819 {
820 COLORMAT pr1toxyz, xyztopr2, wbmat;
821
822 if (pr1 == pr2) {
823 memset(mat, 0, sizeof(COLORMAT));
824 mat[0][0] = mat[1][1] = mat[2][2] = 1.f;
825 return(1);
826 }
827 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 /* combine transforms */
834 multcolormat(mat, pr1toxyz, wbmat);
835 multcolormat(mat, mat, xyztopr2);
836 return(1);
837 }