ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/tonemap.c
Revision: 3.11
Committed: Fri Jun 20 00:25:49 2003 UTC (20 years, 10 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.10: +3 -3 lines
Log Message:
Changed instances of "int4" to "int32" and "int2" to "int16"

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: tonemap.c,v 3.10 2003/02/25 02:47:22 greg Exp $";
3 #endif
4 /*
5 * Tone mapping functions.
6 * See tonemap.h for detailed function descriptions.
7 * Added von Kries white-balance calculations 10/01 (GW).
8 *
9 * Externals declared in tonemap.h
10 */
11
12 #include "copyright.h"
13
14 #include <stdio.h>
15 #include <math.h>
16 #include "tmprivat.h"
17 #include "tmerrmsg.h"
18
19 #define exp10(x) exp(M_LN10*(x))
20
21 struct tmStruct *tmTop = NULL; /* current tone mapping stack */
22
23 /* our list of conversion packages */
24 struct tmPackage *tmPkg[TM_MAXPKG];
25 int tmNumPkgs = 0; /* number of registered packages */
26
27 int tmLastError; /* last error incurred by library */
28 char *tmLastFunction; /* error-generating function name */
29
30
31 struct tmStruct *
32 tmInit(flags, monpri, gamval) /* initialize new tone mapping */
33 int flags;
34 RGBPRIMP monpri;
35 double gamval;
36 {
37 COLORMAT cmat;
38 register struct tmStruct *tmnew;
39 register int i;
40 /* allocate structure */
41 tmnew = (struct tmStruct *)malloc(sizeof(struct tmStruct));
42 if (tmnew == NULL)
43 return(NULL);
44
45 tmnew->flags = flags & ~TM_F_UNIMPL;
46 /* set monitor transform */
47 if (monpri == NULL || monpri == stdprims || tmnew->flags & TM_F_BW) {
48 tmnew->monpri = stdprims;
49 tmnew->clf[RED] = rgb2xyzmat[1][0];
50 tmnew->clf[GRN] = rgb2xyzmat[1][1];
51 tmnew->clf[BLU] = rgb2xyzmat[1][2];
52 } else {
53 comprgb2xyzWBmat(cmat, tmnew->monpri=monpri);
54 tmnew->clf[RED] = cmat[1][0];
55 tmnew->clf[GRN] = cmat[1][1];
56 tmnew->clf[BLU] = cmat[1][2];
57 }
58 /* set gamma value */
59 if (gamval < MINGAM)
60 tmnew->mongam = DEFGAM;
61 else
62 tmnew->mongam = gamval;
63 /* set color divisors */
64 for (i = 0; i < 3; i++)
65 tmnew->cdiv[i] = 256.*pow(tmnew->clf[i], 1./tmnew->mongam);
66
67 /* set input transform */
68 tmnew->inppri = tmnew->monpri;
69 tmnew->cmat[0][0] = tmnew->cmat[1][1] = tmnew->cmat[2][2] =
70 tmnew->inpsf = WHTEFFICACY;
71 tmnew->cmat[0][1] = tmnew->cmat[0][2] = tmnew->cmat[1][0] =
72 tmnew->cmat[1][2] = tmnew->cmat[2][0] = tmnew->cmat[2][1] = 0.;
73 tmnew->hbrmin = 10; tmnew->hbrmax = -10;
74 tmnew->histo = NULL;
75 tmnew->mbrmin = 10; tmnew->mbrmax = -10;
76 tmnew->lumap = NULL;
77 /* zero private data */
78 for (i = TM_MAXPKG; i--; )
79 tmnew->pd[i] = NULL;
80 /* make tmnew current */
81 tmnew->tmprev = tmTop;
82 return(tmTop = tmnew);
83 }
84
85
86 int
87 tmSetSpace(pri, sf) /* set input color space for conversions */
88 RGBPRIMP pri;
89 double sf;
90 {
91 static char funcName[] = "tmSetSpace";
92 register int i, j;
93 /* error check */
94 if (tmTop == NULL)
95 returnErr(TM_E_TMINVAL);
96 if (sf <= 1e-12)
97 returnErr(TM_E_ILLEGAL);
98 /* check if no change */
99 if (pri == tmTop->inppri && FEQ(sf, tmTop->inpsf))
100 returnOK;
101 tmTop->inppri = pri; /* let's set it */
102 tmTop->inpsf = sf;
103
104 if (tmTop->flags & TM_F_BW) { /* color doesn't matter */
105 tmTop->monpri = tmTop->inppri; /* eliminate xform */
106 if (tmTop->inppri == TM_XYZPRIM) {
107 tmTop->clf[CIEX] = tmTop->clf[CIEZ] = 0.;
108 tmTop->clf[CIEY] = 1.;
109 } else {
110 comprgb2xyzWBmat(tmTop->cmat, tmTop->monpri);
111 tmTop->clf[RED] = tmTop->cmat[1][0];
112 tmTop->clf[GRN] = tmTop->cmat[1][1];
113 tmTop->clf[BLU] = tmTop->cmat[1][2];
114 }
115 tmTop->cmat[0][0] = tmTop->cmat[1][1] = tmTop->cmat[2][2] =
116 tmTop->inpsf;
117 tmTop->cmat[0][1] = tmTop->cmat[0][2] = tmTop->cmat[1][0] =
118 tmTop->cmat[1][2] = tmTop->cmat[2][0] = tmTop->cmat[2][1] = 0.;
119
120 } else if (tmTop->inppri == TM_XYZPRIM) /* input is XYZ */
121 compxyz2rgbWBmat(tmTop->cmat, tmTop->monpri);
122
123 else { /* input is RGB */
124 if (tmTop->inppri != tmTop->monpri &&
125 PRIMEQ(tmTop->inppri, tmTop->monpri))
126 tmTop->inppri = tmTop->monpri; /* no xform */
127 comprgb2rgbWBmat(tmTop->cmat, tmTop->inppri, tmTop->monpri);
128 }
129 for (i = 0; i < 3; i++)
130 for (j = 0; j < 3; j++)
131 tmTop->cmat[i][j] *= tmTop->inpsf;
132 /* set color divisors */
133 for (i = 0; i < 3; i++)
134 if (tmTop->clf[i] > .001)
135 tmTop->cdiv[i] =
136 256.*pow(tmTop->clf[i], 1./tmTop->mongam);
137 else
138 tmTop->cdiv[i] = 1;
139 /* notify packages */
140 for (i = tmNumPkgs; i--; )
141 if (tmTop->pd[i] != NULL && tmPkg[i]->NewSpace != NULL)
142 (*tmPkg[i]->NewSpace)(tmTop);
143 returnOK;
144 }
145
146
147 void
148 tmClearHisto() /* clear current histogram */
149 {
150 if (tmTop == NULL || tmTop->histo == NULL)
151 return;
152 free((MEM_PTR)tmTop->histo);
153 tmTop->histo = NULL;
154 }
155
156
157 int
158 tmCvColors(ls, cs, scan, len) /* convert float colors */
159 TMbright *ls;
160 BYTE *cs;
161 COLOR *scan;
162 int len;
163 {
164 static char funcName[] = "tmCvColors";
165 static COLOR csmall = {1e-6, 1e-6, 1e-6};
166 COLOR cmon;
167 double lum, slum;
168 register double d;
169 register int i;
170
171 if (tmTop == NULL)
172 returnErr(TM_E_TMINVAL);
173 if (ls == NULL | scan == NULL | len < 0)
174 returnErr(TM_E_ILLEGAL);
175 for (i = len; i--; ) {
176 if (tmNeedMatrix(tmTop)) /* get monitor RGB */
177 colortrans(cmon, tmTop->cmat, scan[i]);
178 else {
179 cmon[RED] = tmTop->inpsf*scan[i][RED];
180 cmon[GRN] = tmTop->inpsf*scan[i][GRN];
181 cmon[BLU] = tmTop->inpsf*scan[i][BLU];
182 }
183 /* world luminance */
184 lum = tmTop->clf[RED]*cmon[RED] +
185 tmTop->clf[GRN]*cmon[GRN] +
186 tmTop->clf[BLU]*cmon[BLU] ;
187 /* check range */
188 if (clipgamut(cmon, lum, CGAMUT_LOWER, csmall, cwhite))
189 lum = tmTop->clf[RED]*cmon[RED] +
190 tmTop->clf[GRN]*cmon[GRN] +
191 tmTop->clf[BLU]*cmon[BLU] ;
192 if (lum < MINLUM) {
193 ls[i] = MINBRT-1; /* bogus value */
194 lum = MINLUM;
195 } else {
196 d = TM_BRTSCALE*log(lum); /* encode it */
197 ls[i] = d>0. ? (int)(d+.5) : (int)(d-.5);
198 }
199 if (cs == TM_NOCHROM) /* no color? */
200 continue;
201 if (tmTop->flags & TM_F_MESOPIC && lum < LMESUPPER) {
202 slum = scotlum(cmon); /* mesopic adj. */
203 if (lum < LMESLOWER)
204 cmon[RED] = cmon[GRN] = cmon[BLU] = slum;
205 else {
206 d = (lum - LMESLOWER)/(LMESUPPER - LMESLOWER);
207 if (tmTop->flags & TM_F_BW)
208 cmon[RED] = cmon[GRN] =
209 cmon[BLU] = d*lum;
210 else
211 scalecolor(cmon, d);
212 d = (1.-d)*slum;
213 cmon[RED] += d;
214 cmon[GRN] += d;
215 cmon[BLU] += d;
216 }
217 } else if (tmTop->flags & TM_F_BW) {
218 cmon[RED] = cmon[GRN] = cmon[BLU] = lum;
219 }
220 d = tmTop->clf[RED]*cmon[RED]/lum;
221 cs[3*i ] = d>=.999 ? 255 :
222 (int)(256.*pow(d, 1./tmTop->mongam));
223 d = tmTop->clf[GRN]*cmon[GRN]/lum;
224 cs[3*i+1] = d>=.999 ? 255 :
225 (int)(256.*pow(d, 1./tmTop->mongam));
226 d = tmTop->clf[BLU]*cmon[BLU]/lum;
227 cs[3*i+2] = d>=.999 ? 255 :
228 (int)(256.*pow(d, 1./tmTop->mongam));
229 }
230 returnOK;
231 }
232
233
234 int
235 tmCvGrays(ls, scan, len) /* convert float gray values */
236 TMbright *ls;
237 float *scan;
238 int len;
239 {
240 static char funcName[] = "tmCvGrays";
241 register double d;
242 register int i;
243
244 if (tmTop == NULL)
245 returnErr(TM_E_TMINVAL);
246 if (ls == NULL | scan == NULL | len < 0)
247 returnErr(TM_E_ILLEGAL);
248 for (i = len; i--; )
249 if (scan[i] <= TM_NOLUM) {
250 ls[i] = TM_NOBRT; /* bogus value */
251 } else {
252 d = TM_BRTSCALE*log(scan[i]); /* encode it */
253 ls[i] = d>0. ? (int)(d+.5) : (int)(d-.5);
254 }
255 returnOK;
256 }
257
258
259 int
260 tmAddHisto(ls, len, wt) /* add values to histogram */
261 register TMbright *ls;
262 int len;
263 int wt;
264 {
265 static char funcName[] = "tmAddHisto";
266 int oldorig=0, oldlen, horig, hlen;
267 register int i, j;
268
269 if (tmTop == NULL)
270 returnErr(TM_E_TMINVAL);
271 if (len < 0)
272 returnErr(TM_E_ILLEGAL);
273 if (len == 0)
274 returnOK;
275 /* first, grow limits */
276 if (tmTop->histo == NULL) {
277 for (i = len; i-- && ls[i] < MINBRT; )
278 ;
279 if (i < 0)
280 returnOK;
281 tmTop->hbrmin = tmTop->hbrmax = ls[i];
282 oldlen = 0;
283 } else {
284 oldorig = (tmTop->hbrmin-MINBRT)/HISTEP;
285 oldlen = (tmTop->hbrmax-MINBRT)/HISTEP + 1 - oldorig;
286 }
287 for (i = len; i--; ) {
288 if ((j = ls[i]) < MINBRT)
289 continue;
290 if (j < tmTop->hbrmin)
291 tmTop->hbrmin = j;
292 else if (j > tmTop->hbrmax)
293 tmTop->hbrmax = j;
294 }
295 horig = (tmTop->hbrmin-MINBRT)/HISTEP;
296 hlen = (tmTop->hbrmax-MINBRT)/HISTEP + 1 - horig;
297 if (hlen > oldlen) { /* (re)allocate histogram */
298 register int *newhist = (int *)calloc(hlen, sizeof(int));
299 if (newhist == NULL)
300 returnErr(TM_E_NOMEM);
301 if (oldlen) { /* copy and free old */
302 for (i = oldlen, j = i+oldorig-horig; i; )
303 newhist[--j] = tmTop->histo[--i];
304 free((MEM_PTR)tmTop->histo);
305 }
306 tmTop->histo = newhist;
307 }
308 if (wt == 0)
309 returnOK;
310 for (i = len; i--; ) /* add in new counts */
311 if (ls[i] >= MINBRT)
312 tmTop->histo[ (ls[i]-MINBRT)/HISTEP - horig ] += wt;
313 returnOK;
314 }
315
316
317 static double
318 htcontrs(La) /* human threshold contrast sensitivity, dL(La) */
319 double La;
320 {
321 double l10La, l10dL;
322 /* formula taken from Ferwerda et al. [SG96] */
323 if (La < 1.148e-4)
324 return(1.38e-3);
325 l10La = log10(La);
326 if (l10La < -1.44) /* rod response regime */
327 l10dL = pow(.405*l10La + 1.6, 2.18) - 2.86;
328 else if (l10La < -.0184)
329 l10dL = l10La - .395;
330 else if (l10La < 1.9) /* cone response regime */
331 l10dL = pow(.249*l10La + .65, 2.7) - .72;
332 else
333 l10dL = l10La - 1.255;
334
335 return(exp10(l10dL));
336 }
337
338
339 static int
340 tmNewMap()
341 {
342 if (tmTop->lumap != NULL && (tmTop->mbrmax - tmTop->mbrmin) !=
343 (tmTop->hbrmax - tmTop->hbrmin)) {
344 free((MEM_PTR)tmTop->lumap);
345 tmTop->lumap = NULL;
346 }
347 tmTop->mbrmin = tmTop->hbrmin;
348 tmTop->mbrmax = tmTop->hbrmax;
349 if (tmTop->mbrmin > tmTop->mbrmax)
350 return 0;
351 if (tmTop->lumap == NULL)
352 tmTop->lumap = (unsigned short *)malloc(sizeof(unsigned short)*
353 (tmTop->mbrmax-tmTop->mbrmin+1));
354 return(tmTop->lumap != NULL);
355 }
356
357
358 int
359 tmFixedMapping(expmult, gamval)
360 double expmult;
361 double gamval;
362 {
363 static char funcName[] = "tmFixedMapping";
364 double d;
365 register int i;
366
367 if (!tmNewMap())
368 returnErr(TM_E_NOMEM);
369 if (expmult <= .0)
370 expmult = 1.;
371 if (gamval < MINGAM)
372 gamval = tmTop->mongam;
373 d = log(expmult/tmTop->inpsf);
374 for (i = tmTop->mbrmax-tmTop->mbrmin+1; i--; )
375 tmTop->lumap[i] = 256. * exp(
376 ( d + (tmTop->mbrmin+i)*(1./TM_BRTSCALE) )
377 / gamval );
378 returnOK;
379 }
380
381
382 int
383 tmComputeMapping(gamval, Lddyn, Ldmax)
384 double gamval;
385 double Lddyn;
386 double Ldmax;
387 {
388 static char funcName[] = "tmComputeMapping";
389 int *histo;
390 float *cumf;
391 int brt0, histlen, threshold, ceiling, trimmings;
392 double logLddyn, Ldmin, Ldavg, Lwavg, Tr, Lw, Ld;
393 int32 histot;
394 double sum;
395 register double d;
396 register int i, j;
397
398 if (tmTop == NULL || tmTop->histo == NULL)
399 returnErr(TM_E_TMINVAL);
400 /* check arguments */
401 if (Lddyn < MINLDDYN) Lddyn = DEFLDDYN;
402 if (Ldmax < MINLDMAX) Ldmax = DEFLDMAX;
403 if (gamval < MINGAM) gamval = tmTop->mongam;
404 /* compute handy values */
405 Ldmin = Ldmax/Lddyn;
406 logLddyn = log(Lddyn);
407 Ldavg = sqrt(Ldmax*Ldmin);
408 i = (tmTop->hbrmin-MINBRT)/HISTEP;
409 brt0 = MINBRT + HISTEP/2 + i*HISTEP;
410 histlen = (tmTop->hbrmax-MINBRT)/HISTEP + 1 - i;
411 /* histogram total and mean */
412 histot = 0; sum = 0;
413 j = brt0 + histlen*HISTEP;
414 for (i = histlen; i--; ) {
415 histot += tmTop->histo[i];
416 sum += (j -= HISTEP) * tmTop->histo[i];
417 }
418 threshold = histot*.025 + .5;
419 if (threshold < 4)
420 returnErr(TM_E_TMFAIL);
421 Lwavg = tmLuminance( (double)sum / histot );
422 /* allocate space for mapping */
423 if (!tmNewMap())
424 returnErr(TM_E_NOMEM);
425 /* use linear tone mapping? */
426 if (tmTop->flags & TM_F_LINEAR)
427 goto linearmap;
428 /* clamp histogram */
429 histo = (int *)malloc(histlen*sizeof(int));
430 cumf = (float *)malloc((histlen+2)*sizeof(float));
431 if (histo == NULL | cumf == NULL)
432 returnErr(TM_E_NOMEM);
433 cumf[histlen+1] = 1.; /* guard for assignment code */
434 for (i = histlen; i--; ) /* make malleable copy */
435 histo[i] = tmTop->histo[i];
436 do { /* iterate to solution */
437 sum = 0; /* cumulative probability */
438 for (i = 0; i < histlen; i++) {
439 cumf[i] = (double)sum/histot;
440 sum += histo[i];
441 }
442 cumf[histlen] = 1.;
443 Tr = histot * (double)(tmTop->hbrmax - tmTop->hbrmin) /
444 ((double)histlen*TM_BRTSCALE) / logLddyn;
445 ceiling = Tr + 1.;
446 trimmings = 0; /* clip to envelope */
447 for (i = histlen; i--; ) {
448 if (tmTop->flags & TM_F_HCONTR) {
449 Lw = tmLuminance(brt0 + i*HISTEP);
450 Ld = Ldmin * exp( logLddyn *
451 .5*(cumf[i]+cumf[i+1]) );
452 ceiling = Tr * (htcontrs(Ld) * Lw) /
453 (htcontrs(Lw) * Ld) + 1.;
454 }
455 if (histo[i] > ceiling) {
456 trimmings += histo[i] - ceiling;
457 histo[i] = ceiling;
458 }
459 }
460 /* check if we're out of data */
461 if ((histot -= trimmings) <= threshold) {
462 free((MEM_PTR)histo);
463 free((MEM_PTR)cumf);
464 goto linearmap;
465 }
466 } while (trimmings > threshold);
467 /* assign tone-mapping */
468 for (i = tmTop->mbrmax-tmTop->mbrmin+1; i--; ) {
469 j = d = (double)i/(tmTop->mbrmax-tmTop->mbrmin)*histlen;
470 d -= (double)j;
471 Ld = Ldmin*exp(logLddyn*((1.-d)*cumf[j]+d*cumf[j+1]));
472 d = (Ld - Ldmin)/(Ldmax - Ldmin);
473 tmTop->lumap[i] = 256.*pow(d, 1./gamval);
474 }
475 free((MEM_PTR)histo); /* clean up and return */
476 free((MEM_PTR)cumf);
477 returnOK;
478 linearmap: /* linear tone-mapping */
479 if (tmTop->flags & TM_F_HCONTR)
480 d = htcontrs(Ldavg) / htcontrs(Lwavg);
481 else
482 d = Ldavg / Lwavg;
483 return(tmFixedMapping(tmTop->inpsf*d/Ldmax, gamval));
484 }
485
486
487 int
488 tmMapPixels(ps, ls, cs, len)
489 register BYTE *ps;
490 TMbright *ls;
491 register BYTE *cs;
492 int len;
493 {
494 static char funcName[] = "tmMapPixels";
495 register int32 li, pv;
496
497 if (tmTop == NULL || tmTop->lumap == NULL)
498 returnErr(TM_E_TMINVAL);
499 if (ps == NULL | ls == NULL | len < 0)
500 returnErr(TM_E_ILLEGAL);
501 while (len--) {
502 if ((li = *ls++) < tmTop->mbrmin) {
503 li = 0;
504 } else {
505 if (li > tmTop->mbrmax)
506 li = tmTop->mbrmax;
507 li = tmTop->lumap[li - tmTop->mbrmin];
508 }
509 if (cs == TM_NOCHROM)
510 *ps++ = li>255 ? 255 : li;
511 else {
512 pv = *cs++ * li / tmTop->cdiv[RED];
513 *ps++ = pv>255 ? 255 : pv;
514 pv = *cs++ * li / tmTop->cdiv[GRN];
515 *ps++ = pv>255 ? 255 : pv;
516 pv = *cs++ * li / tmTop->cdiv[BLU];
517 *ps++ = pv>255 ? 255 : pv;
518 }
519 }
520 returnOK;
521 }
522
523
524 struct tmStruct *
525 tmPop() /* pop top tone mapping off stack */
526 {
527 register struct tmStruct *tms;
528
529 if ((tms = tmTop) != NULL)
530 tmTop = tms->tmprev;
531 return(tms);
532 }
533
534
535 int
536 tmPull(tms) /* pull a tone mapping from stack */
537 register struct tmStruct *tms;
538 {
539 register struct tmStruct *tms2;
540 /* special cases first */
541 if (tms == NULL | tmTop == NULL)
542 return(0);
543 if (tms == tmTop) {
544 tmTop = tms->tmprev;
545 tms->tmprev = NULL;
546 return(1);
547 }
548 for (tms2 = tmTop; tms2->tmprev != NULL; tms2 = tms2->tmprev)
549 if (tms == tms2->tmprev) { /* remove it */
550 tms2->tmprev = tms->tmprev;
551 tms->tmprev = NULL;
552 return(1);
553 }
554 return(0); /* not found on stack */
555 }
556
557
558 struct tmStruct *
559 tmDup() /* duplicate top tone mapping */
560 {
561 int len;
562 register int i;
563 register struct tmStruct *tmnew;
564
565 if (tmTop == NULL) /* anything to duplicate? */
566 return(NULL);
567 tmnew = (struct tmStruct *)malloc(sizeof(struct tmStruct));
568 if (tmnew == NULL)
569 return(NULL);
570 *tmnew = *tmTop; /* copy everything */
571 if (tmnew->histo != NULL) { /* duplicate histogram */
572 len = (tmnew->hbrmax-MINBRT)/HISTEP + 1 -
573 (tmnew->hbrmin-MINBRT)/HISTEP;
574 tmnew->histo = (int *)malloc(len*sizeof(int));
575 if (tmnew->histo != NULL)
576 for (i = len; i--; )
577 tmnew->histo[i] = tmTop->histo[i];
578 }
579 if (tmnew->lumap != NULL) { /* duplicate luminance mapping */
580 len = tmnew->mbrmax-tmnew->mbrmin+1;
581 tmnew->lumap = (unsigned short *)malloc(
582 len*sizeof(unsigned short) );
583 if (tmnew->lumap != NULL)
584 for (i = len; i--; )
585 tmnew->lumap[i] = tmTop->lumap[i];
586 }
587 /* clear package data */
588 for (i = tmNumPkgs; i--; )
589 tmnew->pd[i] = NULL;
590 tmnew->tmprev = tmTop; /* make copy current */
591 return(tmTop = tmnew);
592 }
593
594
595 int
596 tmPush(tms) /* push tone mapping on top of stack */
597 register struct tmStruct *tms;
598 {
599 static char funcName[] = "tmPush";
600 /* check validity */
601 if (tms == NULL)
602 returnErr(TM_E_ILLEGAL);
603 if (tms == tmTop) /* check necessity */
604 returnOK;
605 /* pull if already in stack */
606 (void)tmPull(tms);
607 /* push it on top */
608 tms->tmprev = tmTop;
609 tmTop = tms;
610 returnOK;
611 }
612
613
614 void
615 tmDone(tms) /* done with tone mapping -- destroy it */
616 register struct tmStruct *tms;
617 {
618 register int i;
619 /* NULL arg. is equiv. to tmTop */
620 if (tms == NULL && (tms = tmTop) == NULL)
621 return;
622 /* take out of stack if present */
623 (void)tmPull(tms);
624 /* free tables */
625 if (tms->histo != NULL)
626 free((MEM_PTR)tms->histo);
627 if (tms->lumap != NULL)
628 free((MEM_PTR)tms->lumap);
629 /* free private data */
630 for (i = tmNumPkgs; i--; )
631 if (tms->pd[i] != NULL)
632 (*tmPkg[i]->Free)(tms->pd[i]);
633 free((MEM_PTR)tms); /* free basic structure */
634 }
635
636 /******************** Shared but Private library routines *********************/
637
638 BYTE tmMesofact[BMESUPPER-BMESLOWER];
639
640 void
641 tmMkMesofact() /* build mesopic lookup factor table */
642 {
643 register int i;
644
645 if (tmMesofact[BMESUPPER-BMESLOWER-1])
646 return;
647
648 for (i = BMESLOWER; i < BMESUPPER; i++)
649 tmMesofact[i-BMESLOWER] = 256. *
650 (tmLuminance(i) - LMESLOWER) /
651 (LMESUPPER - LMESLOWER);
652 }
653
654
655 int
656 tmErrorReturn(func, err) /* error return (with message) */
657 char *func;
658 int err;
659 {
660 tmLastFunction = func;
661 tmLastError = err;
662 if (tmTop != NULL && tmTop->flags & TM_F_NOSTDERR)
663 return(err);
664 fputs(func, stderr);
665 fputs(": ", stderr);
666 fputs(tmErrorMessage[err], stderr);
667 fputs("!\n", stderr);
668 return(err);
669 }