1 |
greg |
1.1 |
#ifndef lint |
2 |
greg |
2.40 |
static const char RCSid[] = "$Id: color.c,v 2.39 2025/02/06 02:15:45 greg Exp $"; |
3 |
greg |
1.1 |
#endif |
4 |
|
|
/* |
5 |
|
|
* color.c - routines for color calculations. |
6 |
|
|
* |
7 |
greg |
2.9 |
* Externals declared in color.h |
8 |
|
|
*/ |
9 |
|
|
|
10 |
greg |
2.10 |
#include "copyright.h" |
11 |
greg |
1.1 |
|
12 |
|
|
#include <stdio.h> |
13 |
greg |
2.9 |
#include <stdlib.h> |
14 |
greg |
2.7 |
#include <math.h> |
15 |
greg |
1.1 |
#include "color.h" |
16 |
greg |
2.14 |
|
17 |
|
|
#ifdef getc_unlocked /* avoid horrendous overhead of flockfile */ |
18 |
greg |
2.15 |
#undef getc |
19 |
|
|
#undef putc |
20 |
greg |
2.19 |
#undef ferror |
21 |
greg |
2.14 |
#define getc getc_unlocked |
22 |
|
|
#define putc putc_unlocked |
23 |
greg |
2.18 |
#define ferror ferror_unlocked |
24 |
greg |
2.14 |
#endif |
25 |
greg |
1.1 |
|
26 |
greg |
2.34 |
#define MINELEN 17 /* minimum scanline length for encoding */ |
27 |
greg |
2.6 |
#define MAXELEN 0x7fff /* maximum scanline length for encoding */ |
28 |
greg |
1.14 |
#define MINRUN 4 /* minimum run length */ |
29 |
|
|
|
30 |
greg |
2.40 |
const float cxponent[256] = { /* exponent look-up */ |
31 |
|
|
.0f, 2.2958874e-41f, 4.59177481e-41f, 9.18354962e-41f, |
32 |
|
|
1.83670992e-40f, 3.67341985e-40f, 7.34683969e-40f, 1.46936794e-39f, |
33 |
|
|
2.93873588e-39f, 5.87747175e-39f, 1.17549435e-38f, 2.3509887e-38f, |
34 |
|
|
4.7019774e-38f, 9.40395481e-38f, 1.88079096e-37f, 3.76158192e-37f, |
35 |
|
|
7.52316385e-37f, 1.50463277e-36f, 3.00926554e-36f, 6.01853108e-36f, |
36 |
|
|
1.20370622e-35f, 2.40741243e-35f, 4.81482486e-35f, 9.62964972e-35f, |
37 |
|
|
1.92592994e-34f, 3.85185989e-34f, 7.70371978e-34f, 1.54074396e-33f, |
38 |
|
|
3.08148791e-33f, 6.16297582e-33f, 1.23259516e-32f, 2.46519033e-32f, |
39 |
|
|
4.93038066e-32f, 9.86076132e-32f, 1.97215226e-31f, 3.94430453e-31f, |
40 |
|
|
7.88860905e-31f, 1.57772181e-30f, 3.15544362e-30f, 6.31088724e-30f, |
41 |
|
|
1.26217745e-29f, 2.5243549e-29f, 5.04870979e-29f, 1.00974196e-28f, |
42 |
|
|
2.01948392e-28f, 4.03896783e-28f, 8.07793567e-28f, 1.61558713e-27f, |
43 |
|
|
3.23117427e-27f, 6.46234854e-27f, 1.29246971e-26f, 2.58493941e-26f, |
44 |
|
|
5.16987883e-26f, 1.03397577e-25f, 2.06795153e-25f, 4.13590306e-25f, |
45 |
|
|
8.27180613e-25f, 1.65436123e-24f, 3.30872245e-24f, 6.6174449e-24f, |
46 |
|
|
1.32348898e-23f, 2.64697796e-23f, 5.29395592e-23f, 1.05879118e-22f, |
47 |
|
|
2.11758237e-22f, 4.23516474e-22f, 8.47032947e-22f, 1.69406589e-21f, |
48 |
|
|
3.38813179e-21f, 6.77626358e-21f, 1.35525272e-20f, 2.71050543e-20f, |
49 |
|
|
5.42101086e-20f, 1.08420217e-19f, 2.16840434e-19f, 4.33680869e-19f, |
50 |
|
|
8.67361738e-19f, 1.73472348e-18f, 3.46944695e-18f, 6.9388939e-18f, |
51 |
|
|
1.38777878e-17f, 2.77555756e-17f, 5.55111512e-17f, 1.11022302e-16f, |
52 |
|
|
2.22044605e-16f, 4.4408921e-16f, 8.8817842e-16f, 1.77635684e-15f, |
53 |
|
|
3.55271368e-15f, 7.10542736e-15f, 1.42108547e-14f, 2.84217094e-14f, |
54 |
|
|
5.68434189e-14f, 1.13686838e-13f, 2.27373675e-13f, 4.54747351e-13f, |
55 |
|
|
9.09494702e-13f, 1.8189894e-12f, 3.63797881e-12f, 7.27595761e-12f, |
56 |
|
|
1.45519152e-11f, 2.91038305e-11f, 5.82076609e-11f, 1.16415322e-10f, |
57 |
|
|
2.32830644e-10f, 4.65661287e-10f, 9.31322575e-10f, 1.86264515e-09f, |
58 |
|
|
3.7252903e-09f, 7.4505806e-09f, 1.49011612e-08f, 2.98023224e-08f, |
59 |
|
|
5.96046448e-08f, 1.1920929e-07f, 2.38418579e-07f, 4.76837158e-07f, |
60 |
|
|
9.53674316e-07f, 1.90734863e-06f, 3.81469727e-06f, 7.62939453e-06f, |
61 |
|
|
1.52587891e-05f, 3.05175781e-05f, 6.10351562e-05f, 0.000122070312f, |
62 |
|
|
0.000244140625f, 0.00048828125f, 0.0009765625f, 0.001953125f, |
63 |
|
|
0.00390625f, 0.0078125f, 0.015625f, 0.03125f, 0.0625f, 0.125f, |
64 |
|
|
0.25f, 0.5f, 1.f, 2.f, 4.f, 8.f, 16.f, 32.f, 64.f, 128.f, 256.f, |
65 |
|
|
512.f, 1024.f, 2048.f, 4096.f, 8192.f, 16384.f, 32768.f, 65536.f, |
66 |
|
|
131072.f, 262144.f, 524288.f, 1048576.f, 2097152.f, 4194304.f, |
67 |
|
|
8388608.f, 16777216.f, 33554432.f, 67108864.f, 134217728.f, |
68 |
|
|
268435456.f, 536870912.f, 1.07374182e+09f, 2.14748365e+09f, |
69 |
|
|
4.2949673e+09f, 8.58993459e+09f, 1.71798692e+10f, 3.43597384e+10f, |
70 |
|
|
6.87194767e+10f, 1.37438953e+11f, 2.74877907e+11f, 5.49755814e+11f, |
71 |
|
|
1.09951163e+12f, 2.19902326e+12f, 4.39804651e+12f, 8.79609302e+12f, |
72 |
|
|
1.7592186e+13f, 3.51843721e+13f, 7.03687442e+13f, 1.40737488e+14f, |
73 |
|
|
2.81474977e+14f, 5.62949953e+14f, 1.12589991e+15f, 2.25179981e+15f, |
74 |
|
|
4.50359963e+15f, 9.00719925e+15f, 1.80143985e+16f, 3.6028797e+16f, |
75 |
|
|
7.2057594e+16f, 1.44115188e+17f, 2.88230376e+17f, 5.76460752e+17f, |
76 |
|
|
1.1529215e+18f, 2.30584301e+18f, 4.61168602e+18f, 9.22337204e+18f, |
77 |
|
|
1.84467441e+19f, 3.68934881e+19f, 7.37869763e+19f, 1.47573953e+20f, |
78 |
|
|
2.95147905e+20f, 5.9029581e+20f, 1.18059162e+21f, 2.36118324e+21f, |
79 |
|
|
4.72236648e+21f, 9.44473297e+21f, 1.88894659e+22f, 3.77789319e+22f, |
80 |
|
|
7.55578637e+22f, 1.51115727e+23f, 3.02231455e+23f, 6.0446291e+23f, |
81 |
|
|
1.20892582e+24f, 2.41785164e+24f, 4.83570328e+24f, 9.67140656e+24f, |
82 |
|
|
1.93428131e+25f, 3.86856262e+25f, 7.73712525e+25f, 1.54742505e+26f, |
83 |
|
|
3.0948501e+26f, 6.1897002e+26f, 1.23794004e+27f, 2.47588008e+27f, |
84 |
|
|
4.95176016e+27f, 9.90352031e+27f, 1.98070406e+28f, 3.96140813e+28f, |
85 |
|
|
7.92281625e+28f, 1.58456325e+29f, 3.1691265e+29f, 6.338253e+29f, |
86 |
|
|
1.2676506e+30f, 2.5353012e+30f, 5.0706024e+30f, 1.01412048e+31f, |
87 |
|
|
2.02824096e+31f, 4.05648192e+31f, 8.11296384e+31f, 1.62259277e+32f, |
88 |
|
|
3.24518554e+32f, 6.49037107e+32f, 1.29807421e+33f, 2.59614843e+33f, |
89 |
|
|
5.19229686e+33f, 1.03845937e+34f, 2.07691874e+34f, 4.15383749e+34f, |
90 |
|
|
8.30767497e+34f, 1.66153499e+35f, 3.32306999e+35f, 6.64613998e+35f |
91 |
|
|
}; |
92 |
greg |
2.4 |
|
93 |
greg |
2.27 |
int CNDX[4] = {0,1,2,3}; /* RGBE indices for SCOLOR, SCOLR */ |
94 |
|
|
float WLPART[4] = {780,588,480,380}; /* RGB wavelength limits+partitions (nm) */ |
95 |
|
|
|
96 |
|
|
|
97 |
|
|
int |
98 |
|
|
setspectrsamp( /* assign spectral sampling, 1 if good, -1 if bad */ |
99 |
|
|
int cn[4], /* input cn[3]=nsamps */ |
100 |
|
|
float wlpt[4] /* input wlpt[0],wlpt[3]=extrema */ |
101 |
|
|
) |
102 |
|
|
{ |
103 |
|
|
static const float PKWL[3] = {607, 553, 469}; |
104 |
|
|
int i, j; |
105 |
|
|
|
106 |
|
|
if (cn[3] < 3) |
107 |
|
|
return(-1); /* reject this */ |
108 |
|
|
|
109 |
|
|
if (wlpt[0] < wlpt[3]) { |
110 |
|
|
float tf = wlpt[0]; |
111 |
|
|
wlpt[0] = wlpt[3]; wlpt[3] = tf; |
112 |
|
|
} |
113 |
|
|
if (wlpt[0] - wlpt[3] < 50.f) |
114 |
|
|
return(-1); /* also reject */ |
115 |
|
|
|
116 |
|
|
if (cn[3] > MAXCSAMP) |
117 |
|
|
cn[3] = MAXCSAMP; |
118 |
|
|
|
119 |
|
|
if ((wlpt[3] >= PKWL[2]) | (wlpt[0] <= PKWL[0])) { |
120 |
|
|
wlpt[1] = wlpt[0] + 0.333333f*(wlpt[3]-wlpt[0]); |
121 |
|
|
wlpt[2] = wlpt[0] + 0.666667f*(wlpt[3]-wlpt[0]); |
122 |
|
|
cn[0] = 0; cn[1] = cn[3]/3; cn[2] = cn[3]*2/3; |
123 |
|
|
return(0); /* unhappy but non-fatal return value */ |
124 |
|
|
} |
125 |
|
|
wlpt[1] = 588.f; /* tuned for standard green channel */ |
126 |
|
|
wlpt[2] = 480.f; |
127 |
|
|
if (cn[3] == 3) { /* nothing to tune? */ |
128 |
|
|
cn[0] = 0; cn[1] = 1; cn[2] = 2; |
129 |
|
|
} else { /* else find nearest color indices */ |
130 |
|
|
double curwl[3]; |
131 |
|
|
memset(curwl, 0, sizeof(curwl)); |
132 |
|
|
for (i = cn[3]; i--; ) { |
133 |
|
|
const float cwl = (i+.5f)/cn[3]*(wlpt[3]-wlpt[0]) + wlpt[0]; |
134 |
|
|
for (j = 3; j--; ) |
135 |
|
|
if (fabs(cwl - PKWL[j]) < fabs(curwl[j] - PKWL[j])) { |
136 |
|
|
curwl[j] = cwl; |
137 |
|
|
cn[j] = i; |
138 |
|
|
} |
139 |
|
|
} |
140 |
|
|
} |
141 |
|
|
return(1); /* happy return value */ |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
|
145 |
|
|
void |
146 |
|
|
setscolor( /* assign spectral color from RGB */ |
147 |
|
|
SCOLOR scol, |
148 |
|
|
double r, |
149 |
|
|
double g, |
150 |
|
|
double b |
151 |
|
|
) |
152 |
|
|
{ |
153 |
greg |
2.39 |
double step, cwl; |
154 |
greg |
2.27 |
int i; |
155 |
|
|
|
156 |
greg |
2.39 |
if (NCSAMP == 3) { |
157 |
|
|
setcolor(scol, r, g, b); |
158 |
|
|
return; |
159 |
|
|
} |
160 |
|
|
step = (WLPART[3] - WLPART[0])/(double)NCSAMP; |
161 |
|
|
cwl = WLPART[0] + .5*step; |
162 |
greg |
2.27 |
for (i = 0; i < NCSAMP; i++) { |
163 |
|
|
if (cwl >= WLPART[1]) |
164 |
|
|
scol[i] = r; |
165 |
|
|
else if (cwl >= WLPART[2]) |
166 |
|
|
scol[i] = g; |
167 |
|
|
else |
168 |
|
|
scol[i] = b; |
169 |
|
|
cwl += step; |
170 |
|
|
} |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
|
174 |
|
|
void |
175 |
greg |
2.38 |
setscolr( /* assign common-exponent spectral from RGB */ |
176 |
|
|
SCOLR sclr, |
177 |
|
|
double r, |
178 |
|
|
double g, |
179 |
|
|
double b |
180 |
|
|
) |
181 |
|
|
{ |
182 |
|
|
SCOLOR scol; |
183 |
|
|
|
184 |
|
|
setscolor(scol, r, g, b); |
185 |
|
|
scolor2scolr(sclr, scol, NCSAMP); |
186 |
|
|
} |
187 |
|
|
|
188 |
|
|
|
189 |
|
|
void |
190 |
greg |
2.27 |
scolor2color( /* assign RGB color from spectrum */ |
191 |
|
|
COLOR col, |
192 |
greg |
2.35 |
const SCOLOR scol, /* uses average over bands */ |
193 |
greg |
2.27 |
int ncs, |
194 |
greg |
2.30 |
const float wlpt[4] |
195 |
greg |
2.27 |
) |
196 |
|
|
{ |
197 |
|
|
const double step = (wlpt[3] - wlpt[0])/(double)ncs; |
198 |
|
|
double cwl = wlpt[0] + .5*step; |
199 |
|
|
int i, j=0, n=0; |
200 |
|
|
|
201 |
|
|
setcolor(col, 0, 0, 0); |
202 |
|
|
for (i = 0; i < ncs; i++) { |
203 |
|
|
if (cwl < wlpt[j+1]) { |
204 |
|
|
if (n > 1) col[j] /= (COLORV)n; |
205 |
|
|
j++; |
206 |
|
|
n = 0; |
207 |
|
|
} |
208 |
|
|
col[j] += scol[i]; |
209 |
|
|
n++; |
210 |
|
|
cwl += step; |
211 |
|
|
} |
212 |
|
|
if (n > 1) col[j] /= (COLORV)n; |
213 |
|
|
} |
214 |
|
|
|
215 |
|
|
|
216 |
|
|
void |
217 |
greg |
2.36 |
scolr2colr( /* assign RGBE from common-exponent spectrum */ |
218 |
|
|
COLR clr, |
219 |
|
|
const SCOLR sclr, |
220 |
|
|
int ncs, |
221 |
|
|
const float wlpt[4] |
222 |
|
|
) |
223 |
greg |
2.37 |
#if 0 /* fancier method seems to be slower(!) */ |
224 |
greg |
2.36 |
{ |
225 |
|
|
const double step = (wlpt[3] - wlpt[0])/(double)ncs; |
226 |
|
|
double cwl; |
227 |
|
|
int csum[3], cnt[3], eshft; |
228 |
|
|
int i, j; |
229 |
|
|
|
230 |
|
|
csum[0] = csum[1] = csum[2] = 0; |
231 |
|
|
cnt[0] = cnt[1] = cnt[2] = 0; |
232 |
|
|
cwl = wlpt[j=0] + .5*step; |
233 |
|
|
for (i = 0; i < ncs; i++) { |
234 |
|
|
csum[j] += sclr[i]; |
235 |
|
|
++cnt[j]; |
236 |
|
|
j += ((cwl += step) < wlpt[j+1]); |
237 |
|
|
} |
238 |
|
|
eshft = 7; /* compute exponent shift */ |
239 |
|
|
for (j = 3; (eshft > 0) & (j-- > 0); ) { |
240 |
|
|
i = 0; |
241 |
|
|
while (csum[j] < 128*cnt[j] >> i) |
242 |
|
|
if (++i >= eshft) |
243 |
|
|
break; |
244 |
|
|
if (eshft > i) |
245 |
|
|
eshft = i; |
246 |
|
|
} |
247 |
|
|
if (sclr[ncs] <= eshft) { |
248 |
|
|
clr[RED] = clr[GRN] = clr[BLU] = 0; |
249 |
|
|
clr[EXP] = 0; |
250 |
|
|
return; |
251 |
|
|
} |
252 |
|
|
for (j = 3; j--; ) |
253 |
|
|
clr[j] = (csum[j]<<eshft)/cnt[j]; |
254 |
|
|
|
255 |
|
|
clr[EXP] = sclr[ncs] - eshft; |
256 |
|
|
} |
257 |
greg |
2.37 |
#else |
258 |
|
|
{ |
259 |
|
|
SCOLOR scol; |
260 |
|
|
COLOR col; |
261 |
|
|
|
262 |
|
|
scolr2scolor(scol, sclr, ncs); |
263 |
|
|
scolor2color(col, scol, ncs, wlpt); |
264 |
|
|
setcolr(clr, col[RED], col[GRN], col[BLU]); |
265 |
|
|
} |
266 |
|
|
#endif |
267 |
greg |
2.36 |
|
268 |
|
|
|
269 |
|
|
void |
270 |
greg |
2.27 |
scolor2colr( /* assign RGBE from spectral color */ |
271 |
|
|
COLR clr, |
272 |
greg |
2.35 |
const SCOLOR scol, /* uses average over bands */ |
273 |
greg |
2.27 |
int ncs, |
274 |
greg |
2.30 |
const float wlpt[4] |
275 |
greg |
2.27 |
) |
276 |
|
|
{ |
277 |
|
|
COLOR col; |
278 |
|
|
|
279 |
|
|
scolor2color(col, scol, ncs, wlpt); |
280 |
|
|
setcolr(clr, col[RED], col[GRN], col[BLU]); |
281 |
|
|
} |
282 |
|
|
|
283 |
|
|
|
284 |
|
|
void |
285 |
greg |
2.33 |
scolr2color( /* assign RGB from common exponent */ |
286 |
|
|
COLOR col, |
287 |
greg |
2.35 |
const SCOLR sclr, |
288 |
greg |
2.33 |
int ncs, |
289 |
|
|
const float wlpt[4] |
290 |
|
|
) |
291 |
|
|
{ |
292 |
|
|
SCOLOR scol; |
293 |
|
|
|
294 |
|
|
scolr2scolor(scol, sclr, ncs); |
295 |
|
|
scolor2color(col, scol, ncs, wlpt); |
296 |
|
|
} |
297 |
|
|
|
298 |
|
|
|
299 |
|
|
void |
300 |
greg |
2.27 |
scolor2scolr( /* float spectrum to common exponent */ |
301 |
|
|
SCOLR sclr, |
302 |
greg |
2.35 |
const SCOLOR scol, |
303 |
greg |
2.27 |
int ncs |
304 |
|
|
) |
305 |
|
|
{ |
306 |
|
|
int i = ncs; |
307 |
|
|
COLORV p = scol[--i]; |
308 |
|
|
|
309 |
|
|
while (i) |
310 |
|
|
if (scol[--i] > p) |
311 |
|
|
p = scol[i]; |
312 |
|
|
if (p <= 1e-32) { |
313 |
|
|
memset(sclr, 0, ncs+1); |
314 |
|
|
return; |
315 |
|
|
} |
316 |
|
|
p = frexp(p, &i) * 256.0 / p; |
317 |
|
|
sclr[ncs] = i + COLXS; |
318 |
|
|
for (i = ncs; i--; ) |
319 |
|
|
sclr[i] = (scol[i] > 0) * (int)(scol[i]*p); |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
|
323 |
|
|
void |
324 |
|
|
scolr2scolor( /* common exponent to float spectrum */ |
325 |
|
|
SCOLOR scol, |
326 |
greg |
2.35 |
const SCOLR sclr, |
327 |
greg |
2.27 |
int ncs |
328 |
|
|
) |
329 |
|
|
{ |
330 |
|
|
double f; |
331 |
|
|
int i; |
332 |
|
|
|
333 |
|
|
if (sclr[ncs] == 0) { |
334 |
|
|
memset(scol, 0, sizeof(COLORV)*ncs); |
335 |
|
|
return; |
336 |
|
|
} |
337 |
greg |
2.40 |
f = cxponent[sclr[ncs]]; |
338 |
greg |
2.27 |
|
339 |
|
|
for (i = ncs; i--; ) |
340 |
|
|
scol[i] = (sclr[i] + 0.5)*f; |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
|
344 |
|
|
double |
345 |
|
|
scolor_mean( /* compute average for spectral color */ |
346 |
greg |
2.35 |
const SCOLOR scol |
347 |
greg |
2.27 |
) |
348 |
|
|
{ |
349 |
|
|
int i = NCSAMP; |
350 |
|
|
double sum = 0; |
351 |
|
|
|
352 |
|
|
while (i--) |
353 |
|
|
sum += scol[i]; |
354 |
|
|
|
355 |
|
|
return sum/(double)NCSAMP; |
356 |
|
|
} |
357 |
|
|
|
358 |
|
|
|
359 |
|
|
double |
360 |
|
|
sintens( /* find maximum value from spectrum */ |
361 |
greg |
2.35 |
const SCOLOR scol |
362 |
greg |
2.27 |
) |
363 |
|
|
{ |
364 |
|
|
int i = NCSAMP; |
365 |
|
|
COLORV peak = scol[--i]; |
366 |
|
|
|
367 |
|
|
while (i) |
368 |
|
|
if (scol[--i] > peak) |
369 |
|
|
peak = scol[i]; |
370 |
|
|
|
371 |
|
|
return peak; |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
|
375 |
|
|
void |
376 |
|
|
convertscolor( /* spectrum conversion, zero-fill ends */ |
377 |
|
|
SCOLOR dst, /* destination spectrum */ |
378 |
|
|
int dnc, /* destination # of spectral samples/intervals */ |
379 |
|
|
double dwl0, /* starting destination wavelength (longer) */ |
380 |
|
|
double dwl1, /* ending destination wavelength (shorter) */ |
381 |
|
|
const COLORV src[], /* source spectrum array */ |
382 |
|
|
int snc, |
383 |
|
|
double swl0, /* long/short wavelengths may be reversed */ |
384 |
|
|
double swl1 |
385 |
|
|
) |
386 |
|
|
{ |
387 |
|
|
const int sdir = 1 - 2*(swl0 < swl1); |
388 |
|
|
const double sstp = (swl1 - swl0)/(double)snc; |
389 |
|
|
const double dstp = (dwl1 - dwl0)/(double)dnc; |
390 |
|
|
const double rdstp = 1./dstp; |
391 |
|
|
int si, ssi, di; |
392 |
|
|
double wl; |
393 |
|
|
|
394 |
|
|
if ((dnc < 3) | (dwl0 <= dwl1) | (dst == src)) |
395 |
|
|
return; /* invalid destination */ |
396 |
|
|
|
397 |
|
|
if (dnc == snc && (dwl0-swl0)*(dwl0-swl0) + (dwl1-swl1)*(dwl1-swl1) <= .5) { |
398 |
|
|
memcpy(dst, src, sizeof(COLORV)*dnc); |
399 |
|
|
return; /* same spectral sampling */ |
400 |
|
|
} |
401 |
|
|
memset(dst, 0, sizeof(COLORV)*dnc); |
402 |
|
|
/* set starting positions */ |
403 |
|
|
if ((sdir>0 ? swl0 : swl1) <= dwl0) { |
404 |
|
|
if (sdir > 0) { |
405 |
|
|
wl = swl0; |
406 |
|
|
ssi = 0; |
407 |
|
|
} else { |
408 |
|
|
wl = swl1; |
409 |
|
|
ssi = snc-1; |
410 |
|
|
} |
411 |
|
|
si = 0; |
412 |
|
|
di = (wl - dwl0)*rdstp; |
413 |
|
|
} else { |
414 |
|
|
wl = dwl0; |
415 |
greg |
2.29 |
if (sdir > 0) { |
416 |
|
|
ssi = si = (wl - swl0)/sstp; |
417 |
|
|
} else { |
418 |
|
|
si = (wl - swl1)/sstp; |
419 |
|
|
ssi = snc-1 - si; |
420 |
|
|
} |
421 |
greg |
2.27 |
di = 0; |
422 |
|
|
} |
423 |
|
|
swl0 += (sdir < 0)*sstp; |
424 |
|
|
/* step through intervals */ |
425 |
|
|
while ((si < snc) & (di < dnc)) { |
426 |
|
|
double intvl; |
427 |
|
|
if (swl0 + (ssi+sdir)*sstp < dwl0 + (di+1)*dstp) { |
428 |
|
|
intvl = dwl0 + (di+1)*dstp - wl; |
429 |
|
|
dst[di++] += src[ssi]*intvl*rdstp; |
430 |
|
|
} else { |
431 |
|
|
intvl = swl0 + (ssi+sdir)*sstp - wl; |
432 |
|
|
dst[di] += src[ssi]*intvl*rdstp; |
433 |
|
|
ssi += sdir; |
434 |
|
|
si++; |
435 |
|
|
} |
436 |
|
|
wl += intvl; |
437 |
|
|
} |
438 |
|
|
} |
439 |
|
|
|
440 |
|
|
|
441 |
greg |
2.22 |
void * |
442 |
greg |
2.17 |
tempbuffer( /* get a temporary buffer */ |
443 |
greg |
2.27 |
size_t len |
444 |
greg |
2.17 |
) |
445 |
greg |
1.14 |
{ |
446 |
greg |
2.27 |
static void *tempbuf = NULL; |
447 |
|
|
static size_t tempbuflen = 0; |
448 |
greg |
1.14 |
|
449 |
greg |
2.22 |
if (!len) { /* call to free */ |
450 |
|
|
if (tempbuflen) { |
451 |
greg |
2.20 |
free(tempbuf); |
452 |
greg |
2.22 |
tempbuf = NULL; |
453 |
|
|
tempbuflen = 0; |
454 |
|
|
} |
455 |
|
|
return(NULL); |
456 |
greg |
1.14 |
} |
457 |
greg |
2.22 |
if (len <= tempbuflen) /* big enough already? */ |
458 |
|
|
return(tempbuf); |
459 |
|
|
/* else free & reallocate */ |
460 |
|
|
if (tempbuflen) |
461 |
|
|
free(tempbuf); |
462 |
|
|
tempbuf = malloc(len); |
463 |
|
|
tempbuflen = len*(tempbuf != NULL); |
464 |
greg |
1.14 |
return(tempbuf); |
465 |
|
|
} |
466 |
|
|
|
467 |
|
|
|
468 |
greg |
2.9 |
int |
469 |
greg |
2.17 |
fwritecolrs( /* write out a colr scanline */ |
470 |
|
|
COLR *scanline, |
471 |
|
|
int len, |
472 |
|
|
FILE *fp |
473 |
|
|
) |
474 |
greg |
1.1 |
{ |
475 |
greg |
2.17 |
int i, j, beg, cnt = 1; |
476 |
greg |
1.14 |
int c2; |
477 |
greg |
1.1 |
|
478 |
schorsch |
2.13 |
if ((len < MINELEN) | (len > MAXELEN)) /* OOBs, write out flat */ |
479 |
greg |
1.14 |
return(fwrite((char *)scanline,sizeof(COLR),len,fp) - len); |
480 |
greg |
2.2 |
/* put magic header */ |
481 |
|
|
putc(2, fp); |
482 |
greg |
1.14 |
putc(2, fp); |
483 |
|
|
putc(len>>8, fp); |
484 |
greg |
2.21 |
putc(len&0xff, fp); |
485 |
greg |
1.14 |
/* put components seperately */ |
486 |
|
|
for (i = 0; i < 4; i++) { |
487 |
|
|
for (j = 0; j < len; j += cnt) { /* find next run */ |
488 |
|
|
for (beg = j; beg < len; beg += cnt) { |
489 |
greg |
2.20 |
for (cnt = 1; (cnt < 127) & (beg+cnt < len) && |
490 |
greg |
1.14 |
scanline[beg+cnt][i] == scanline[beg][i]; cnt++) |
491 |
|
|
; |
492 |
|
|
if (cnt >= MINRUN) |
493 |
|
|
break; /* long enough */ |
494 |
greg |
1.1 |
} |
495 |
greg |
2.20 |
if ((beg-j > 1) & (beg-j < MINRUN)) { |
496 |
greg |
1.15 |
c2 = j+1; |
497 |
|
|
while (scanline[c2++][i] == scanline[j][i]) |
498 |
|
|
if (c2 == beg) { /* short run */ |
499 |
|
|
putc(128+beg-j, fp); |
500 |
|
|
putc(scanline[j][i], fp); |
501 |
|
|
j = beg; |
502 |
|
|
break; |
503 |
|
|
} |
504 |
|
|
} |
505 |
|
|
while (j < beg) { /* write out non-run */ |
506 |
greg |
1.14 |
if ((c2 = beg-j) > 128) c2 = 128; |
507 |
|
|
putc(c2, fp); |
508 |
|
|
while (c2--) |
509 |
|
|
putc(scanline[j++][i], fp); |
510 |
|
|
} |
511 |
|
|
if (cnt >= MINRUN) { /* write out run */ |
512 |
|
|
putc(128+cnt, fp); |
513 |
|
|
putc(scanline[beg][i], fp); |
514 |
|
|
} else |
515 |
|
|
cnt = 0; |
516 |
|
|
} |
517 |
greg |
1.1 |
} |
518 |
|
|
return(ferror(fp) ? -1 : 0); |
519 |
|
|
} |
520 |
|
|
|
521 |
greg |
2.23 |
/* |
522 |
|
|
* An old-format scanline is either a stream of valid RGBE or XYZE real |
523 |
|
|
* pixels or at least one real pixel followed by some number of |
524 |
|
|
* invalid real pixels of the form (1,1,1,n), where n is a count. |
525 |
|
|
* These can themselves be repeated to create a multibyte repeat |
526 |
|
|
* count, with the least significant byte first (little-endian order.) |
527 |
|
|
* Repeat counts are limited by the size of an int; if a repetition |
528 |
|
|
* leads to an overrun, the rest of the the repetition will be |
529 |
|
|
* silently ignored. |
530 |
|
|
*/ |
531 |
greg |
2.9 |
static int |
532 |
greg |
2.19 |
oldreadcolrs( /* read in an old-style colr scanline */ |
533 |
greg |
2.17 |
COLR *scanline, |
534 |
|
|
int len, |
535 |
|
|
FILE *fp |
536 |
|
|
) |
537 |
greg |
2.9 |
{ |
538 |
greg |
2.25 |
int rshift = 0; |
539 |
greg |
2.17 |
int i; |
540 |
greg |
2.9 |
|
541 |
|
|
while (len > 0) { |
542 |
|
|
scanline[0][RED] = getc(fp); |
543 |
|
|
scanline[0][GRN] = getc(fp); |
544 |
|
|
scanline[0][BLU] = getc(fp); |
545 |
greg |
2.18 |
scanline[0][EXP] = i = getc(fp); |
546 |
|
|
if (i == EOF) |
547 |
greg |
2.9 |
return(-1); |
548 |
greg |
2.20 |
if (scanline[0][GRN] == 1 && |
549 |
|
|
(scanline[0][RED] == 1) & |
550 |
greg |
2.25 |
(scanline[0][BLU] == 1)) { |
551 |
greg |
2.20 |
i = scanline[0][EXP] << rshift; |
552 |
|
|
while (i--) { |
553 |
greg |
2.9 |
copycolr(scanline[0], scanline[-1]); |
554 |
greg |
2.20 |
if (--len <= 0) |
555 |
|
|
return(0); |
556 |
greg |
2.9 |
scanline++; |
557 |
|
|
} |
558 |
|
|
rshift += 8; |
559 |
|
|
} else { |
560 |
|
|
scanline++; |
561 |
|
|
len--; |
562 |
|
|
rshift = 0; |
563 |
|
|
} |
564 |
|
|
} |
565 |
|
|
return(0); |
566 |
|
|
} |
567 |
|
|
|
568 |
greg |
2.23 |
/* |
569 |
|
|
* There are two scanline formats: old and new. The old format |
570 |
|
|
* compresses runs of RGBE or XYZE four-byte real pixels; the new |
571 |
|
|
* format breaks the pixels into R, G, B, and E lines (or XYZE lines) |
572 |
|
|
* which are individually run-length encoded. |
573 |
|
|
* |
574 |
|
|
* An old-format scanline always begins with a valid real pixel; at |
575 |
|
|
* least one of the RGB (or XYZ) values will have its high-order bit |
576 |
|
|
* set. A new-format scanline begins with four bytes which are not a |
577 |
|
|
* valid real pixel: (2, 2, lenhigh, lenlow) where lenhigh is always |
578 |
|
|
* less than 128 and hence never has a high-order bit set. |
579 |
|
|
* |
580 |
|
|
* A new-format scanline is broken into its RGBE or XYZE components. |
581 |
|
|
* Each is output and run-length encoded separately so that a scanline |
582 |
|
|
* is broken into four records. In turn, each record is organized |
583 |
|
|
* into chunks of up to 128 characters, which begin with a count byte. |
584 |
|
|
* If the count byte is greater than 128, the following data byte is |
585 |
|
|
* repeated (count-128) times. If not, the count byte is followed by |
586 |
|
|
* that many data bytes. |
587 |
|
|
*/ |
588 |
greg |
2.9 |
int |
589 |
greg |
2.17 |
freadcolrs( /* read in an encoded colr scanline */ |
590 |
|
|
COLR *scanline, |
591 |
|
|
int len, |
592 |
|
|
FILE *fp |
593 |
|
|
) |
594 |
greg |
1.1 |
{ |
595 |
greg |
2.17 |
int i, j; |
596 |
greg |
2.6 |
int code, val; |
597 |
greg |
1.14 |
/* determine scanline type */ |
598 |
greg |
2.26 |
if (len <= 0) |
599 |
|
|
return(0); |
600 |
greg |
1.14 |
if ((i = getc(fp)) == EOF) |
601 |
|
|
return(-1); |
602 |
greg |
2.26 |
scanline[0][RED] = i; |
603 |
greg |
1.14 |
scanline[0][GRN] = getc(fp); |
604 |
|
|
scanline[0][BLU] = getc(fp); |
605 |
|
|
if ((i = getc(fp)) == EOF) |
606 |
|
|
return(-1); |
607 |
greg |
2.26 |
if ((scanline[0][RED] != 2) | (scanline[0][GRN] != 2) | |
608 |
|
|
(scanline[0][BLU] & 0x80)) { |
609 |
greg |
1.14 |
scanline[0][EXP] = i; |
610 |
|
|
return(oldreadcolrs(scanline+1, len-1, fp)); |
611 |
|
|
} |
612 |
|
|
if ((scanline[0][BLU]<<8 | i) != len) |
613 |
|
|
return(-1); /* length mismatch! */ |
614 |
|
|
/* read each component */ |
615 |
|
|
for (i = 0; i < 4; i++) |
616 |
|
|
for (j = 0; j < len; ) { |
617 |
|
|
if ((code = getc(fp)) == EOF) |
618 |
|
|
return(-1); |
619 |
|
|
if (code > 128) { /* run */ |
620 |
greg |
2.6 |
code &= 127; |
621 |
greg |
2.9 |
if ((val = getc(fp)) == EOF) |
622 |
|
|
return -1; |
623 |
greg |
2.16 |
if (j + code > len) |
624 |
|
|
return -1; /* overrun */ |
625 |
greg |
2.6 |
while (code--) |
626 |
|
|
scanline[j++][i] = val; |
627 |
greg |
2.16 |
} else { /* non-run */ |
628 |
|
|
if (j + code > len) |
629 |
|
|
return -1; /* overrun */ |
630 |
greg |
2.9 |
while (code--) { |
631 |
|
|
if ((val = getc(fp)) == EOF) |
632 |
|
|
return -1; |
633 |
|
|
scanline[j++][i] = val; |
634 |
|
|
} |
635 |
greg |
2.16 |
} |
636 |
greg |
1.14 |
} |
637 |
greg |
1.1 |
return(0); |
638 |
|
|
} |
639 |
|
|
|
640 |
|
|
|
641 |
greg |
2.30 |
/* read an nc-component common-exponent color scanline */ |
642 |
|
|
int |
643 |
greg |
2.35 |
freadscolrs(COLRV *scanline, int nc, int len, FILE *fp) |
644 |
greg |
2.30 |
{ |
645 |
greg |
2.32 |
if (nc < 3) |
646 |
|
|
return(-1); |
647 |
|
|
if (nc == 3) |
648 |
|
|
return(freadcolrs((COLR *)scanline, len, fp)); |
649 |
|
|
|
650 |
greg |
2.30 |
if (fread(scanline, nc+1, len, fp) != len) |
651 |
|
|
return(-1); |
652 |
|
|
return(0); |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
|
656 |
greg |
2.36 |
/* read nc-component common-exponent color scan and convert to COLR's */ |
657 |
|
|
int |
658 |
|
|
fread2colrs(COLR *scanline, int len, FILE *fp, int nc, const float wlpt[4]) |
659 |
|
|
{ |
660 |
|
|
COLRV *sclrscan; |
661 |
|
|
int n; |
662 |
|
|
|
663 |
|
|
if (nc < 3) |
664 |
|
|
return(-1); |
665 |
|
|
if (nc == 3) |
666 |
|
|
return(freadcolrs(scanline, len, fp)); |
667 |
|
|
|
668 |
|
|
sclrscan = (COLRV *)tempbuffer(sizeof(COLRV)*(nc+1)*len); |
669 |
|
|
if (sclrscan == NULL || freadscolrs(sclrscan, nc, len, fp) < 0) |
670 |
|
|
return(-1); |
671 |
|
|
for (n = len; n--; ) { |
672 |
|
|
scolr2colr(*scanline++, sclrscan, nc, wlpt); |
673 |
|
|
sclrscan += nc+1; |
674 |
|
|
} |
675 |
|
|
return(0); |
676 |
|
|
} |
677 |
|
|
|
678 |
|
|
|
679 |
greg |
2.31 |
/* write an common-exponent spectral color scanline */ |
680 |
greg |
2.30 |
int |
681 |
greg |
2.35 |
fwritescolrs(const COLRV *sscanline, int nc, int len, FILE *fp) |
682 |
greg |
2.30 |
{ |
683 |
greg |
2.32 |
if (nc < 3) |
684 |
|
|
return(-1); |
685 |
|
|
if (nc == 3) |
686 |
|
|
return(fwritecolrs((COLR *)sscanline, len, fp)); |
687 |
|
|
|
688 |
greg |
2.31 |
if (fwrite(sscanline, nc+1, len, fp) != len) |
689 |
greg |
2.30 |
return(-1); |
690 |
|
|
return(0); |
691 |
|
|
} |
692 |
|
|
|
693 |
|
|
|
694 |
greg |
2.9 |
int |
695 |
greg |
2.32 |
fwritescan( /* write out an RGB or XYZ scanline */ |
696 |
greg |
2.17 |
COLOR *scanline, |
697 |
|
|
int len, |
698 |
|
|
FILE *fp |
699 |
|
|
) |
700 |
greg |
1.1 |
{ |
701 |
greg |
1.14 |
COLR *clrscan; |
702 |
|
|
int n; |
703 |
greg |
2.17 |
COLR *sp; |
704 |
greg |
1.14 |
/* get scanline buffer */ |
705 |
|
|
if ((sp = (COLR *)tempbuffer(len*sizeof(COLR))) == NULL) |
706 |
|
|
return(-1); |
707 |
|
|
clrscan = sp; |
708 |
|
|
/* convert scanline */ |
709 |
|
|
n = len; |
710 |
|
|
while (n-- > 0) { |
711 |
|
|
setcolr(sp[0], scanline[0][RED], |
712 |
greg |
1.1 |
scanline[0][GRN], |
713 |
|
|
scanline[0][BLU]); |
714 |
|
|
scanline++; |
715 |
greg |
1.14 |
sp++; |
716 |
greg |
1.1 |
} |
717 |
greg |
1.14 |
return(fwritecolrs(clrscan, len, fp)); |
718 |
greg |
1.1 |
} |
719 |
|
|
|
720 |
|
|
|
721 |
greg |
2.9 |
int |
722 |
greg |
2.32 |
freadscan( /* read in an RGB or XYZ scanline */ |
723 |
greg |
2.17 |
COLOR *scanline, |
724 |
|
|
int len, |
725 |
|
|
FILE *fp |
726 |
|
|
) |
727 |
greg |
1.1 |
{ |
728 |
greg |
2.17 |
COLR *clrscan; |
729 |
greg |
1.14 |
|
730 |
|
|
if ((clrscan = (COLR *)tempbuffer(len*sizeof(COLR))) == NULL) |
731 |
|
|
return(-1); |
732 |
|
|
if (freadcolrs(clrscan, len, fp) < 0) |
733 |
|
|
return(-1); |
734 |
|
|
/* convert scanline */ |
735 |
greg |
2.40 |
while (len-- > 0) { |
736 |
|
|
colr_color(scanline[0], clrscan[0]); |
737 |
greg |
1.14 |
scanline++; clrscan++; |
738 |
greg |
1.1 |
} |
739 |
|
|
return(0); |
740 |
|
|
} |
741 |
|
|
|
742 |
|
|
|
743 |
greg |
2.30 |
/* read an nc-component color scanline */ |
744 |
|
|
int |
745 |
|
|
freadsscan(COLORV *sscanline, int nc, int len, FILE *fp) |
746 |
|
|
{ |
747 |
greg |
2.35 |
COLRV *tscn = (COLRV *)tempbuffer((nc+1)*len); |
748 |
greg |
2.30 |
int i; |
749 |
|
|
|
750 |
|
|
if (tscn == NULL || freadscolrs(tscn, nc, len, fp) < 0) |
751 |
|
|
return(-1); |
752 |
|
|
for (i = len; i-- > 0; ) { |
753 |
|
|
scolr2scolor(sscanline, tscn, nc); |
754 |
|
|
sscanline += nc; |
755 |
|
|
tscn += nc+1; |
756 |
|
|
} |
757 |
|
|
return(0); |
758 |
|
|
} |
759 |
|
|
|
760 |
|
|
|
761 |
greg |
2.36 |
/* read an nc-component color scanline and return as RGB */ |
762 |
|
|
int |
763 |
|
|
fread2scan(COLOR *scanline, int len, FILE *fp, int nc, const float wlpt[4]) |
764 |
|
|
{ |
765 |
|
|
COLRV *tscn; |
766 |
|
|
int i; |
767 |
|
|
|
768 |
|
|
if (nc < 3) |
769 |
|
|
return(-1); |
770 |
|
|
if (nc == 3) |
771 |
|
|
return(freadscan(scanline, len, fp)); |
772 |
|
|
|
773 |
|
|
tscn = (COLRV *)tempbuffer((nc+1)*len); |
774 |
|
|
if (tscn == NULL || freadscolrs(tscn, nc, len, fp) < 0) |
775 |
|
|
return(-1); |
776 |
|
|
for (i = len; i-- > 0; ) { |
777 |
|
|
scolr2color(*scanline++, tscn, nc, wlpt); |
778 |
|
|
tscn += nc+1; |
779 |
|
|
} |
780 |
|
|
return(0); |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
|
784 |
greg |
2.32 |
/* write an nc-component spectral color scanline */ |
785 |
greg |
2.30 |
int |
786 |
greg |
2.35 |
fwritesscan(const COLORV *sscanline, int nc, int len, FILE *fp) |
787 |
greg |
2.30 |
{ |
788 |
greg |
2.35 |
COLRV *tscn = (COLRV *)tempbuffer((nc+1)*len); |
789 |
greg |
2.30 |
int i; |
790 |
|
|
|
791 |
|
|
if (tscn == NULL) |
792 |
|
|
return(-1); |
793 |
|
|
for (i = 0; i < len; i++) { |
794 |
greg |
2.31 |
scolor2scolr(tscn+i*(nc+1), sscanline, nc); |
795 |
|
|
sscanline += nc; |
796 |
greg |
2.30 |
} |
797 |
greg |
2.31 |
return(fwritescolrs(tscn, nc, len, fp)); |
798 |
greg |
2.30 |
} |
799 |
|
|
|
800 |
|
|
|
801 |
greg |
2.9 |
void |
802 |
greg |
2.17 |
setcolr( /* assign a short color value */ |
803 |
|
|
COLR clr, |
804 |
|
|
double r, |
805 |
|
|
double g, |
806 |
|
|
double b |
807 |
|
|
) |
808 |
greg |
1.1 |
{ |
809 |
|
|
double d; |
810 |
|
|
int e; |
811 |
|
|
|
812 |
|
|
d = r > g ? r : g; |
813 |
|
|
if (b > d) d = b; |
814 |
|
|
|
815 |
greg |
1.4 |
if (d <= 1e-32) { |
816 |
greg |
1.1 |
clr[RED] = clr[GRN] = clr[BLU] = 0; |
817 |
|
|
clr[EXP] = 0; |
818 |
|
|
return; |
819 |
|
|
} |
820 |
|
|
|
821 |
greg |
2.21 |
d = frexp(d, &e) * 256.0 / d; |
822 |
greg |
1.1 |
|
823 |
greg |
2.27 |
clr[RED] = (r > 0) * (int)(r*d); |
824 |
|
|
clr[GRN] = (g > 0) * (int)(g*d); |
825 |
|
|
clr[BLU] = (b > 0) * (int)(b*d); |
826 |
greg |
1.1 |
clr[EXP] = e + COLXS; |
827 |
|
|
} |
828 |
|
|
|
829 |
|
|
|
830 |
greg |
2.9 |
int |
831 |
greg |
2.17 |
bigdiff( /* c1 delta c2 > md? */ |
832 |
greg |
2.35 |
const COLOR c1, |
833 |
|
|
const COLOR c2, |
834 |
greg |
2.17 |
double md |
835 |
|
|
) |
836 |
greg |
1.7 |
{ |
837 |
greg |
2.17 |
int i; |
838 |
greg |
1.7 |
|
839 |
|
|
for (i = 0; i < 3; i++) |
840 |
greg |
2.27 |
if ((colval(c1,i)-colval(c2,i) > md*colval(c2,i)) | |
841 |
|
|
(colval(c2,i)-colval(c1,i) > md*colval(c1,i))) |
842 |
|
|
return(1); |
843 |
|
|
return(0); |
844 |
|
|
} |
845 |
|
|
|
846 |
|
|
|
847 |
|
|
int |
848 |
|
|
sbigsdiff( /* sc1 delta sc2 > md? */ |
849 |
greg |
2.35 |
const SCOLOR c1, |
850 |
|
|
const SCOLOR c2, |
851 |
greg |
2.27 |
double md |
852 |
|
|
) |
853 |
|
|
{ |
854 |
|
|
int i = NCSAMP; |
855 |
|
|
|
856 |
|
|
while (i--) |
857 |
|
|
if ((c1[i]-c2[i] > md*c2[i]) | (c2[i]-c1[i] > md*c1[i])) |
858 |
greg |
1.7 |
return(1); |
859 |
|
|
return(0); |
860 |
|
|
} |