5 |
|
* program to convert between RADIANCE and Windows BMP file |
6 |
|
*/ |
7 |
|
|
8 |
< |
#include <stdio.h> |
9 |
< |
#include <string.h> |
8 |
> |
#include <math.h> |
9 |
|
|
10 |
+ |
#include "rtio.h" |
11 |
|
#include "platform.h" |
12 |
|
#include "color.h" |
13 |
|
#include "tonemap.h" |
14 |
|
#include "resolu.h" |
15 |
|
#include "bmpfile.h" |
16 |
|
|
17 |
< |
int bradj = 0; /* brightness adjustment */ |
17 |
> |
int bradj = 0; /* brightness adjustment */ |
18 |
|
|
19 |
< |
double gamcor = 2.2; /* gamma correction value */ |
19 |
> |
double gamcor = 2.2; /* gamma correction value */ |
20 |
|
|
21 |
< |
char *progname; |
21 |
> |
char *info = ""; /* information header string */ |
22 |
> |
int infolen = 0; /* information header length */ |
23 |
|
|
24 |
< |
static void quiterr(const char *err); |
25 |
< |
static void tmap2bmp(char *fnin, char *fnout, char *expec, |
24 |
> |
extern void quiterr(const char *err); |
25 |
> |
extern void addBMPcspace(RGBPRIMP pp, double gamma); |
26 |
> |
extern void tmap2bmp(char *fnin, char *fnout, char *expec, |
27 |
|
RGBPRIMP monpri, double gamval); |
28 |
< |
static void rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, int gry); |
29 |
< |
static void bmp2rad(BMPReader *brd, FILE *rfp, int inv); |
28 |
> |
extern void rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, RGBPRIMP monpri); |
29 |
> |
extern void bmp2rad(BMPReader *brd, FILE *rfp, int inv); |
30 |
> |
extern void info2rad(char *infs, int len, FILE *fout); |
31 |
> |
extern char *growInfo(int n); |
32 |
> |
extern gethfunc headline; |
33 |
|
|
34 |
+ |
#define add2info(s) strcpy(growInfo(strlen(s)), s) |
35 |
+ |
#define clearInfo() growInfo(-infolen) |
36 |
|
|
37 |
+ |
RGBPRIMP rgbinp = stdprims; /* RGB input primitives */ |
38 |
+ |
RGBPRIMS myinprims; /* custom primitives holder */ |
39 |
+ |
|
40 |
+ |
|
41 |
|
int |
42 |
|
main(int argc, char *argv[]) |
43 |
|
{ |
49 |
|
RESOLU rs; |
50 |
|
int i; |
51 |
|
|
52 |
< |
progname = argv[0]; |
52 |
> |
fixargv0(argv[0]); /* assigns progname */ |
53 |
|
|
54 |
|
for (i = 1; i < argc; i++) |
55 |
|
if (argv[i][0] == '-' && argv[i][1]) |
101 |
|
|
102 |
|
if (i == argc-2 && strcmp(argv[i+1], "-")) |
103 |
|
outfile = argv[i+1]; |
104 |
< |
/* check for tone-mapping */ |
105 |
< |
if (expec != NULL) { |
104 |
> |
|
105 |
> |
if (expec != NULL) { /* check for tone-mapping */ |
106 |
|
if (reverse) |
107 |
|
goto userr; |
108 |
|
tmap2bmp(inpfile, outfile, expec, rgbp, gamcor); |
109 |
|
return(0); |
110 |
|
} |
100 |
– |
|
101 |
– |
setcolrgam(gamcor); /* set up conversion */ |
102 |
– |
|
111 |
|
if (reverse) { |
112 |
|
BMPReader *rdr; |
113 |
|
/* open BMP file or stream */ |
129 |
|
} |
130 |
|
/* put Radiance header */ |
131 |
|
newheader("RADIANCE", stdout); |
132 |
+ |
info2rad(BMPinfo(rdr->hdr), rdr->hdr->infoSiz, stdout); |
133 |
|
printargs(i, argv, stdout); |
134 |
|
fputformat(COLRFMT, stdout); |
135 |
|
putchar('\n'); |
140 |
|
if (rdr->hdr->yIsDown || inpfile != NULL) |
141 |
|
rs.rt |= YDECR; |
142 |
|
fputsresolu(&rs, stdout); |
143 |
+ |
/* set up conversion */ |
144 |
+ |
setcolrgam(gamcor); |
145 |
|
/* convert file */ |
146 |
< |
bmp2rad(rdr, stdout, !rdr->hdr->yIsDown && inpfile!=NULL); |
146 |
> |
bmp2rad(rdr, stdout, !rdr->hdr->yIsDown & (inpfile!=NULL)); |
147 |
|
/* flush output */ |
148 |
|
BMPcloseInput(rdr); |
149 |
|
if (fflush(stdout) < 0) |
157 |
|
inpfile); |
158 |
|
exit(1); |
159 |
|
} |
160 |
< |
/* get header info. */ |
161 |
< |
if (checkheader(stdin, COLRFMT, NULL) < 0 || |
162 |
< |
!fgetsresolu(&rs, stdin)) |
160 |
> |
/* get/save header info. */ |
161 |
> |
if (getheader(stdin, headline, NULL) < 0 || |
162 |
> |
!fgetsresolu(&rs, stdin)) |
163 |
|
quiterr("bad Radiance picture format"); |
164 |
< |
/* initialize BMP header */ |
164 |
> |
/* record color space */ |
165 |
> |
addBMPcspace(rgbp, gamcor); |
166 |
> |
/* open output/write BMP header */ |
167 |
|
if (rgbp == NULL) { |
168 |
|
hdr = BMPmappedHeader(scanlen(&rs), |
169 |
< |
numscans(&rs), 0, 256); |
169 |
> |
numscans(&rs), infolen+1, 256); |
170 |
> |
/* |
171 |
|
if (outfile != NULL) |
172 |
|
hdr->compr = BI_RLE8; |
173 |
+ |
*/ |
174 |
|
} else |
175 |
|
hdr = BMPtruecolorHeader(scanlen(&rs), |
176 |
< |
numscans(&rs), 0); |
176 |
> |
numscans(&rs), infolen+1); |
177 |
|
if (hdr == NULL) |
178 |
< |
quiterr("cannot initialize BMP header"); |
178 |
> |
quiterr("cannot create BMP output"); |
179 |
> |
/* copy info to BMP header */ |
180 |
> |
strcpy(BMPinfo(hdr), info); |
181 |
> |
clearInfo(); |
182 |
|
/* set up output direction */ |
183 |
|
hdr->yIsDown = ((outfile == NULL) | (hdr->compr == BI_RLE8)); |
184 |
|
/* open BMP output */ |
188 |
|
wtr = BMPopenOutputStream(stdout, hdr); |
189 |
|
if (wtr == NULL) |
190 |
|
quiterr("cannot allocate writer structure"); |
191 |
+ |
/* set up conversion */ |
192 |
+ |
setcolrgam(gamcor); |
193 |
|
/* convert file */ |
194 |
< |
rad2bmp(stdin, wtr, !hdr->yIsDown, rgbp==NULL); |
194 |
> |
rad2bmp(stdin, wtr, !hdr->yIsDown, rgbp); |
195 |
|
/* flush output */ |
196 |
|
if (fflush((FILE *)wtr->c_data) < 0) |
197 |
|
quiterr("error writing BMP output"); |
209 |
|
} |
210 |
|
|
211 |
|
/* print message and exit */ |
212 |
< |
static void |
212 |
> |
void |
213 |
|
quiterr(const char *err) |
214 |
|
{ |
215 |
|
if (err != NULL) { |
219 |
|
exit(0); |
220 |
|
} |
221 |
|
|
222 |
+ |
/* grow (or shrink) saved info header string */ |
223 |
+ |
char * |
224 |
+ |
growInfo(int n) |
225 |
+ |
{ |
226 |
+ |
char *ns = NULL; |
227 |
+ |
|
228 |
+ |
if (infolen + n <= 0) { |
229 |
+ |
if (info) free(info); |
230 |
+ |
info = ""; |
231 |
+ |
infolen = 0; |
232 |
+ |
return(NULL); |
233 |
+ |
} |
234 |
+ |
if (infolen) |
235 |
+ |
info = (char *)realloc(info, infolen+n+1); |
236 |
+ |
else |
237 |
+ |
info = (char *)malloc(n+1); |
238 |
+ |
|
239 |
+ |
if (info == NULL) |
240 |
+ |
quiterr("out of memory in growInfo()"); |
241 |
+ |
|
242 |
+ |
if (n > 0) memset(ns = info+infolen, 0, n+1); |
243 |
+ |
|
244 |
+ |
infolen += n; |
245 |
+ |
return(ns); |
246 |
+ |
} |
247 |
+ |
|
248 |
+ |
/* process header line (don't echo) */ |
249 |
+ |
int |
250 |
+ |
headline(char *s, void *p) |
251 |
+ |
{ |
252 |
+ |
char fmt[MAXFMTLEN]; |
253 |
+ |
|
254 |
+ |
if (isheadid(s)) /* skip header magic ID */ |
255 |
+ |
return(0); |
256 |
+ |
if (formatval(fmt, s)) { /* check if format string */ |
257 |
+ |
if (!strcmp(fmt,COLRFMT)) |
258 |
+ |
return(0); |
259 |
+ |
if (!strcmp(fmt,CIEFMT)) { |
260 |
+ |
rgbinp = TM_XYZPRIM; |
261 |
+ |
return(0); |
262 |
+ |
} |
263 |
+ |
if (!strcmp(fmt,SPECFMT)) |
264 |
+ |
return(0); |
265 |
+ |
return(-1); |
266 |
+ |
} |
267 |
+ |
if (isprims(s)) { /* get input primaries */ |
268 |
+ |
primsval(myinprims, s); |
269 |
+ |
rgbinp = myinprims; |
270 |
+ |
return(0); |
271 |
+ |
} |
272 |
+ |
if (isexpos(s)) |
273 |
+ |
return(0); /* ignore this on input */ |
274 |
+ |
if (!strncmp(s, "GAMMA=", 6)) |
275 |
+ |
return(0); /* should not be here! */ |
276 |
+ |
if (isncomp(s)) { |
277 |
+ |
NCSAMP = ncompval(s); |
278 |
+ |
return(0); |
279 |
+ |
} |
280 |
+ |
if (iswlsplit(s)) { |
281 |
+ |
wlsplitval(WLPART, s); |
282 |
+ |
return(0); |
283 |
+ |
} |
284 |
+ |
add2info(s); /* else save info string */ |
285 |
+ |
return(1); |
286 |
+ |
} |
287 |
+ |
|
288 |
+ |
/* add BMP output color space to info string */ |
289 |
+ |
void |
290 |
+ |
addBMPcspace(RGBPRIMP pp, double gamma) |
291 |
+ |
{ |
292 |
+ |
char ibuf[128]; |
293 |
+ |
|
294 |
+ |
if (pp != NULL) { |
295 |
+ |
sprintf(ibuf, |
296 |
+ |
"%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n", |
297 |
+ |
PRIMARYSTR, |
298 |
+ |
pp[RED][CIEX],pp[RED][CIEY], |
299 |
+ |
pp[GRN][CIEX],pp[GRN][CIEY], |
300 |
+ |
pp[BLU][CIEX],pp[BLU][CIEY], |
301 |
+ |
pp[WHT][CIEX],pp[WHT][CIEY]); |
302 |
+ |
add2info(ibuf); |
303 |
+ |
} |
304 |
+ |
sprintf(ibuf, "GAMMA=%.2f\n", gamma); |
305 |
+ |
add2info(ibuf); |
306 |
+ |
} |
307 |
+ |
|
308 |
+ |
/* write out Radiance header from BMP info string */ |
309 |
+ |
void |
310 |
+ |
info2rad(char *infs, int len, FILE *fout) |
311 |
+ |
{ |
312 |
+ |
char *cp; |
313 |
+ |
/* must fit metadata profile */ |
314 |
+ |
if (len < 3 || infs[0] == '\n' || |
315 |
+ |
infs[--len] != '\0' || infs[len-1] != '\n') |
316 |
+ |
return; /* not what we expected */ |
317 |
+ |
if (strlen(infs) < len || strstr(infs, "\n\n") != NULL) |
318 |
+ |
return; /* also not cool */ |
319 |
+ |
/* check for gamma */ |
320 |
+ |
if ((cp = strstr(infs, "GAMMA=")) != NULL) { |
321 |
+ |
/* copy what came before */ |
322 |
+ |
fwrite(infs, cp-infs, 1, fout); |
323 |
+ |
cp += 6; |
324 |
+ |
gamcor = atof(cp); /* record setting */ |
325 |
+ |
while (*cp++ != '\n') |
326 |
+ |
; |
327 |
+ |
len -= cp - infs; |
328 |
+ |
infs = cp; /* & elide from output */ |
329 |
+ |
} |
330 |
+ |
fputs(infs, fout); /* copy the remainder */ |
331 |
+ |
} |
332 |
+ |
|
333 |
|
/* convert Radiance picture to BMP */ |
334 |
< |
static void |
335 |
< |
rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, int gry) |
334 |
> |
void |
335 |
> |
rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, RGBPRIMP monpri) |
336 |
|
{ |
337 |
+ |
int usexfm = 0; |
338 |
+ |
COLORMAT xfm; |
339 |
|
COLR *scanin; |
340 |
+ |
COLOR cval; |
341 |
|
int y, yend, ystp; |
342 |
|
int x; |
343 |
|
/* allocate scanline */ |
344 |
|
scanin = (COLR *)malloc(bwr->hdr->width*sizeof(COLR)); |
345 |
|
if (scanin == NULL) |
346 |
|
quiterr("out of memory in rad2bmp"); |
347 |
+ |
/* set up color conversion */ |
348 |
+ |
usexfm = (monpri != NULL) ? (rgbinp != monpri) : |
349 |
+ |
((rgbinp != TM_XYZPRIM) & (rgbinp != stdprims)); |
350 |
+ |
if (usexfm) { |
351 |
+ |
RGBPRIMP destpri = monpri != NULL ? monpri : stdprims; |
352 |
+ |
double expcomp = pow(2.0, (double)bradj); |
353 |
+ |
if (rgbinp == TM_XYZPRIM) |
354 |
+ |
compxyz2rgbWBmat(xfm, destpri); |
355 |
+ |
else |
356 |
+ |
comprgb2rgbWBmat(xfm, rgbinp, destpri); |
357 |
+ |
for (y = 0; y < 3; y++) |
358 |
+ |
for (x = 0; x < 3; x++) |
359 |
+ |
xfm[y][x] *= expcomp; |
360 |
+ |
} |
361 |
|
/* convert image */ |
362 |
|
if (inv) { |
363 |
|
y = bwr->hdr->height - 1; |
368 |
|
} |
369 |
|
/* convert each scanline */ |
370 |
|
for ( ; y != yend; y += ystp) { |
371 |
< |
if (freadcolrs(scanin, bwr->hdr->width, rfp) < 0) |
371 |
> |
if (fread2colrs(scanin, bwr->hdr->width, rfp, NCSAMP, WLPART) < 0) |
372 |
|
quiterr("error reading Radiance picture"); |
373 |
< |
if (bradj) |
373 |
> |
if (usexfm) |
374 |
> |
for (x = bwr->hdr->width; x--; ) { |
375 |
> |
colr_color(cval, scanin[x]); |
376 |
> |
colortrans(cval, xfm, cval); |
377 |
> |
setcolr(scanin[x], colval(cval,RED), |
378 |
> |
colval(cval,GRN), |
379 |
> |
colval(cval,BLU)); |
380 |
> |
} |
381 |
> |
else if (bradj) |
382 |
|
shiftcolrs(scanin, bwr->hdr->width, bradj); |
383 |
< |
for (x = gry ? bwr->hdr->width : 0; x--; ) |
384 |
< |
scanin[x][GRN] = normbright(scanin[x]); |
383 |
> |
if (monpri == NULL && rgbinp != TM_XYZPRIM) |
384 |
> |
for (x = bwr->hdr->width; x--; ) |
385 |
> |
scanin[x][GRN] = normbright(scanin[x]); |
386 |
|
colrs_gambs(scanin, bwr->hdr->width); |
387 |
< |
if (gry) |
387 |
> |
if (monpri == NULL) |
388 |
|
for (x = bwr->hdr->width; x--; ) |
389 |
|
bwr->scanline[x] = scanin[x][GRN]; |
390 |
|
else |
398 |
|
if (x != BIR_OK) |
399 |
|
quiterr(BMPerrorMessage(x)); |
400 |
|
} |
401 |
< |
/* free scanline */ |
245 |
< |
free((void *)scanin); |
401 |
> |
free(scanin); /* free scanline */ |
402 |
|
} |
403 |
|
|
404 |
|
/* convert BMP file to Radiance */ |
405 |
< |
static void |
405 |
> |
void |
406 |
|
bmp2rad(BMPReader *brd, FILE *rfp, int inv) |
407 |
|
{ |
408 |
|
COLR *scanout; |
437 |
|
if (fwritecolrs(scanout, brd->hdr->width, rfp) < 0) |
438 |
|
quiterr("error writing Radiance picture"); |
439 |
|
} |
440 |
< |
/* clean up */ |
285 |
< |
free((void *)scanout); |
440 |
> |
free(scanout); /* clean up */ |
441 |
|
} |
442 |
|
|
443 |
|
/* Tone-map and convert Radiance picture */ |
444 |
< |
static void |
444 |
> |
void |
445 |
|
tmap2bmp(char *fnin, char *fnout, char *expec, RGBPRIMP monpri, double gamval) |
446 |
|
{ |
447 |
|
int tmflags; |
449 |
|
BMPWriter *wtr; |
450 |
|
FILE *fp; |
451 |
|
int xr, yr; |
452 |
< |
BYTE *pa; |
452 |
> |
uby8 *pa; |
453 |
|
int i; |
454 |
|
/* check tone-mapping spec */ |
455 |
|
i = strlen(expec); |
476 |
|
if (tmMapPicture(&pa, &xr, &yr, tmflags, monpri, gamval, |
477 |
|
0., 0., fnin, fp) != TM_E_OK) |
478 |
|
exit(1); |
479 |
+ |
/* try to retrieve info */ |
480 |
+ |
if (fseek(fp, 0L, SEEK_SET) == 0) |
481 |
+ |
getheader(fp, headline, NULL); |
482 |
+ |
/* add output color space */ |
483 |
+ |
addBMPcspace(monpri, gamval); |
484 |
|
/* initialize BMP header */ |
485 |
|
if (tmflags & TM_F_BW) { |
486 |
< |
hdr = BMPmappedHeader(xr, yr, 0, 256); |
486 |
> |
hdr = BMPmappedHeader(xr, yr, infolen+1, 256); |
487 |
|
if (fnout != NULL) |
488 |
|
hdr->compr = BI_RLE8; |
489 |
|
} else |
490 |
< |
hdr = BMPtruecolorHeader(xr, yr, 0); |
490 |
> |
hdr = BMPtruecolorHeader(xr, yr, infolen+1); |
491 |
|
if (hdr == NULL) |
492 |
|
quiterr("cannot initialize BMP header"); |
493 |
+ |
|
494 |
+ |
strcpy(BMPinfo(hdr), info); /* copy info if any */ |
495 |
+ |
clearInfo(); |
496 |
|
/* open BMP output */ |
497 |
|
if (fnout != NULL) |
498 |
|
wtr = BMPopenOutputFile(fnout, hdr); |
502 |
|
quiterr("cannot allocate writer structure"); |
503 |
|
/* write to BMP file */ |
504 |
|
while (wtr->yscan < yr) { |
505 |
< |
BYTE *scn = pa + xr*((tmflags & TM_F_BW) ? 1 : 3)* |
505 |
> |
uby8 *scn = pa + xr*((tmflags & TM_F_BW) ? 1 : 3)* |
506 |
|
(yr-1 - wtr->yscan); |
507 |
|
if (tmflags & TM_F_BW) |
508 |
< |
memcpy((void *)wtr->scanline, (void *)scn, xr); |
508 |
> |
memcpy(wtr->scanline, scn, xr); |
509 |
|
else |
510 |
|
for (i = xr; i--; ) { |
511 |
|
wtr->scanline[3*i] = scn[3*i+BLU]; |
521 |
|
/* clean up */ |
522 |
|
if (fnin != NULL) |
523 |
|
fclose(fp); |
524 |
< |
free((void *)pa); |
524 |
> |
free(pa); |
525 |
|
BMPcloseOutput(wtr); |
526 |
|
} |