ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/Development/ray/src/px/ra_bmp.c
Revision: 2.20
Committed: Sun Sep 14 17:11:29 2025 UTC (4 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.19: +6 -7 lines
Log Message:
fix(ra_bmp): Minor fix -- don't save PRIMARIES for grayscale

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: ra_bmp.c,v 2.19 2025/09/14 15:08:26 greg Exp $";
3 #endif
4 /*
5 * program to convert between RADIANCE and Windows BMP file
6 */
7
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 */
18
19 double gamcor = 2.2; /* gamma correction value */
20
21 char *info = ""; /* information header string */
22 int infolen = 0; /* information header length */
23
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 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) {int _n=strlen(s); memcpy(growInfo(_n), s, _n+1);}
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 {
44 char *inpfile=NULL, *outfile=NULL;
45 char *expec = NULL;
46 int reverse = 0;
47 RGBPRIMP rgbp = stdprims;
48 RGBPRIMS myprims;
49 RESOLU rs;
50 int i;
51
52 fixargv0(argv[0]); /* assigns progname */
53
54 for (i = 1; i < argc; i++)
55 if (argv[i][0] == '-' && argv[i][1])
56 switch (argv[i][1]) {
57 case 'b':
58 rgbp = NULL;
59 break;
60 case 'g':
61 gamcor = atof(argv[++i]);
62 break;
63 case 'e':
64 if (argv[i+1][0] != '+' && argv[i+1][0] != '-')
65 expec = argv[++i];
66 else
67 bradj = atoi(argv[++i]);
68 break;
69 case 'p':
70 if (argc-i < 9)
71 goto userr;
72 myprims[RED][CIEX] = atof(argv[++i]);
73 myprims[RED][CIEY] = atof(argv[++i]);
74 myprims[GRN][CIEX] = atof(argv[++i]);
75 myprims[GRN][CIEY] = atof(argv[++i]);
76 myprims[BLU][CIEX] = atof(argv[++i]);
77 myprims[BLU][CIEY] = atof(argv[++i]);
78 myprims[WHT][CIEX] = atof(argv[++i]);
79 myprims[WHT][CIEY] = atof(argv[++i]);
80 if (rgbp == stdprims)
81 rgbp = myprims;
82 break;
83 case 'r':
84 reverse = !reverse;
85 break;
86 default:
87 goto userr;
88 }
89 else
90 break;
91
92 if (i < argc-2)
93 goto userr;
94
95 SET_FILE_BINARY(stdin);
96 SET_FILE_BINARY(stdout);
97 SET_DEFAULT_BINARY();
98
99 if (i <= argc-1 && strcmp(argv[i], "-"))
100 inpfile = argv[i];
101
102 if (i == argc-2 && strcmp(argv[i+1], "-"))
103 outfile = argv[i+1];
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 }
111 if (reverse) {
112 BMPReader *rdr;
113 /* open BMP file or stream */
114 if (inpfile != NULL)
115 rdr = BMPopenInputFile(inpfile);
116 else
117 rdr = BMPopenInputStream(stdin);
118
119 if (rdr == NULL) {
120 fprintf(stderr, "%s: cannot open or recognize BMP\n",
121 inpfile != NULL ? inpfile : "<stdin>");
122 exit(1);
123 }
124 /* open Radiance output */
125 if (outfile != NULL && freopen(outfile, "w", stdout) == NULL) {
126 fprintf(stderr, "%s: cannot open for output\n",
127 outfile);
128 exit(1);
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');
136 rs.xr = rdr->hdr->width;
137 rs.yr = rdr->hdr->height;
138 rs.rt = YMAJOR;
139 /* write scans downward if we can */
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));
147 /* flush output */
148 BMPcloseInput(rdr);
149 if (fflush(stdout) < 0)
150 quiterr("error writing Radiance output");
151 } else {
152 BMPHeader *hdr;
153 BMPWriter *wtr;
154 /* open Radiance input */
155 if (inpfile != NULL && freopen(inpfile, "r", stdin) == NULL) {
156 fprintf(stderr, "%s: cannot open input file\n",
157 inpfile);
158 exit(1);
159 }
160 /* get/save header info. */
161 if (getheader(stdin, headline, NULL) < 0 ||
162 !fgetsresolu(&rs, stdin))
163 quiterr("bad Radiance picture format");
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), infolen+1, 256);
170 /*
171 if (outfile != NULL)
172 hdr->compr = BI_RLE8;
173 */
174 } else
175 hdr = BMPtruecolorHeader(scanlen(&rs),
176 numscans(&rs), infolen+1);
177 if (hdr == NULL)
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 */
185 if (outfile != NULL)
186 wtr = BMPopenOutputFile(outfile, hdr);
187 else
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);
195 /* flush output */
196 if (fflush((FILE *)wtr->c_data) < 0)
197 quiterr("error writing BMP output");
198 BMPcloseOutput(wtr);
199 }
200 return(0); /* success */
201 userr:
202 fprintf(stderr,
203 "Usage: %s [-b][-g gamma][-e spec][-p xr yr xg yg xb yb xw yw] [input|- [output]]\n",
204 progname);
205 fprintf(stderr,
206 " or: %s -r [-g gamma][-e +/-stops] [input|- [output]]\n",
207 progname);
208 return(1);
209 }
210
211 /* print message and exit */
212 void
213 quiterr(const char *err)
214 {
215 if (err != NULL) {
216 fprintf(stderr, "%s: %s\n", progname, err);
217 exit(1);
218 }
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[196];
293 char *cp = ibuf;
294
295 sprintf(cp, "GAMMA=%.2f\n", gamma);
296 cp += strlen(cp);
297 if (pp != NULL) {
298 sprintf(cp,
299 "%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",
300 PRIMARYSTR,
301 pp[RED][CIEX],pp[RED][CIEY],
302 pp[GRN][CIEX],pp[GRN][CIEY],
303 pp[BLU][CIEX],pp[BLU][CIEY],
304 pp[WHT][CIEX],pp[WHT][CIEY]);
305 /* cp += strlen(cp); */
306 }
307 add2info(ibuf);
308 }
309
310 /* write out Radiance header from BMP info string */
311 void
312 info2rad(char *infs, int len, FILE *fout)
313 {
314 char *cp;
315 /* must fit metadata profile */
316 if (len < 3 || infs[0] == '\n' ||
317 infs[--len] != '\0' || infs[len-1] != '\n')
318 return; /* not what we expected */
319 if (strlen(infs) < len || strstr(infs, "\n\n") != NULL)
320 return; /* also not cool */
321 /* check for gamma */
322 if ((cp = strstr(infs, "GAMMA=")) != NULL) {
323 /* copy what came before */
324 fwrite(infs, 1, cp-infs, fout);
325 cp += 6;
326 gamcor = atof(cp); /* record setting */
327 while (*cp++ != '\n')
328 ;
329 len -= cp - infs;
330 infs = cp; /* & elide from output */
331 }
332 fputs(infs, fout); /* copy the remainder */
333 }
334
335 /* convert Radiance picture to BMP */
336 void
337 rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, RGBPRIMP monpri)
338 {
339 int usexfm = 0;
340 COLORMAT xfm;
341 COLR *scanin;
342 COLOR cval;
343 int y, yend, ystp;
344 int x;
345 /* allocate scanline */
346 scanin = (COLR *)malloc(bwr->hdr->width*sizeof(COLR));
347 if (scanin == NULL)
348 quiterr("out of memory in rad2bmp");
349 /* set up color conversion */
350 usexfm = (monpri != NULL) ? (rgbinp != monpri) :
351 ((rgbinp != TM_XYZPRIM) & (rgbinp != stdprims));
352 if (usexfm) {
353 RGBPRIMP destpri = monpri != NULL ? monpri : stdprims;
354 double expcomp = pow(2.0, (double)bradj);
355 if (rgbinp == TM_XYZPRIM)
356 compxyz2rgbWBmat(xfm, destpri);
357 else
358 comprgb2rgbWBmat(xfm, rgbinp, destpri);
359 for (y = 0; y < 3; y++)
360 for (x = 0; x < 3; x++)
361 xfm[y][x] *= expcomp;
362 }
363 /* convert image */
364 if (inv) {
365 y = bwr->hdr->height - 1;
366 ystp = -1; yend = -1;
367 } else {
368 y = 0;
369 ystp = 1; yend = bwr->hdr->height;
370 }
371 /* convert each scanline */
372 for ( ; y != yend; y += ystp) {
373 if (fread2colrs(scanin, bwr->hdr->width, rfp, NCSAMP, WLPART) < 0)
374 quiterr("error reading Radiance picture");
375 if (usexfm)
376 for (x = bwr->hdr->width; x--; ) {
377 colr_color(cval, scanin[x]);
378 colortrans(cval, xfm, cval);
379 setcolr(scanin[x], colval(cval,RED),
380 colval(cval,GRN),
381 colval(cval,BLU));
382 }
383 else if (bradj)
384 shiftcolrs(scanin, bwr->hdr->width, bradj);
385 if ((monpri == NULL) & (rgbinp != TM_XYZPRIM))
386 for (x = bwr->hdr->width; x--; )
387 scanin[x][GRN] = normbright(scanin[x]);
388 colrs_gambs(scanin, bwr->hdr->width);
389 if (monpri == NULL)
390 for (x = bwr->hdr->width; x--; )
391 bwr->scanline[x] = scanin[x][GRN];
392 else
393 for (x = bwr->hdr->width; x--; ) {
394 bwr->scanline[3*x] = scanin[x][BLU];
395 bwr->scanline[3*x+1] = scanin[x][GRN];
396 bwr->scanline[3*x+2] = scanin[x][RED];
397 }
398 bwr->yscan = y;
399 x = BMPwriteScanline(bwr);
400 if (x != BIR_OK)
401 quiterr(BMPerrorMessage(x));
402 }
403 free(scanin); /* free scanline */
404 }
405
406 /* convert BMP file to Radiance */
407 void
408 bmp2rad(BMPReader *brd, FILE *rfp, int inv)
409 {
410 COLR *scanout;
411 int y, yend, ystp;
412 int x;
413 /* allocate scanline */
414 scanout = (COLR *)malloc(brd->hdr->width*sizeof(COLR));
415 if (scanout == NULL)
416 quiterr("out of memory in bmp2rad");
417 /* convert image */
418 if (inv) {
419 y = brd->hdr->height - 1;
420 ystp = -1; yend = -1;
421 } else {
422 y = 0;
423 ystp = 1; yend = brd->hdr->height;
424 }
425 /* convert each scanline */
426 for ( ; y != yend; y += ystp) {
427 x = BMPseekScanline(y, brd);
428 if (x != BIR_OK)
429 quiterr(BMPerrorMessage(x));
430 for (x = brd->hdr->width; x--; ) {
431 RGBquad rgbq = BMPdecodePixel(x, brd);
432 scanout[x][RED] = rgbq.r;
433 scanout[x][GRN] = rgbq.g;
434 scanout[x][BLU] = rgbq.b;
435 }
436 gambs_colrs(scanout, brd->hdr->width);
437 if (bradj)
438 shiftcolrs(scanout, brd->hdr->width, bradj);
439 if (fwritecolrs(scanout, brd->hdr->width, rfp) < 0)
440 quiterr("error writing Radiance picture");
441 }
442 free(scanout); /* clean up */
443 }
444
445 /* Tone-map and convert Radiance picture */
446 void
447 tmap2bmp(char *fnin, char *fnout, char *expec, RGBPRIMP monpri, double gamval)
448 {
449 int tmflags;
450 BMPHeader *hdr;
451 BMPWriter *wtr;
452 FILE *fp;
453 int xr, yr;
454 uby8 *pa;
455 int i;
456 /* check tone-mapping spec */
457 i = strlen(expec);
458 if (i && !strncmp(expec, "auto", i))
459 tmflags = TM_F_CAMERA;
460 else if (i && !strncmp(expec, "human", i))
461 tmflags = TM_F_HUMAN & ~TM_F_UNIMPL;
462 else if (i && !strncmp(expec, "linear", i))
463 tmflags = TM_F_LINEAR;
464 else
465 quiterr("illegal exposure specification (auto|human|linear)");
466
467 tmflags |= (monpri == NULL)*TM_F_BW;
468 /* open Radiance input */
469 if (fnin == NULL)
470 fp = stdin;
471 else if ((fp = fopen(fnin, "r")) == NULL) {
472 fprintf(stderr, "%s: cannot open\n", fnin);
473 exit(1);
474 }
475 /* tone-map picture */
476 if (tmMapPicture(&pa, &xr, &yr, tmflags,
477 tmflags&TM_F_BW ? stdprims : monpri, gamval,
478 0., 0., fnin, fp) != TM_E_OK)
479 exit(1);
480 /* try to retrieve info */
481 if (fseek(fp, 0L, SEEK_SET) == 0)
482 getheader(fp, headline, NULL);
483 /* add output color space */
484 addBMPcspace(monpri, gamval);
485 /* initialize BMP header */
486 if (tmflags & TM_F_BW) {
487 hdr = BMPmappedHeader(xr, yr, infolen+1, 256);
488 if (fnout != NULL)
489 hdr->compr = BI_RLE8;
490 } else
491 hdr = BMPtruecolorHeader(xr, yr, infolen+1);
492 if (hdr == NULL)
493 quiterr("cannot initialize BMP header");
494
495 strcpy(BMPinfo(hdr), info); /* copy info if any */
496 clearInfo();
497 /* open BMP output */
498 if (fnout != NULL)
499 wtr = BMPopenOutputFile(fnout, hdr);
500 else
501 wtr = BMPopenOutputStream(stdout, hdr);
502 if (wtr == NULL)
503 quiterr("cannot allocate writer structure");
504 /* write to BMP file */
505 while (wtr->yscan < yr) {
506 uby8 *scn = pa + xr*((tmflags & TM_F_BW) ? 1 : 3)*
507 (yr-1 - wtr->yscan);
508 if (tmflags & TM_F_BW)
509 memcpy(wtr->scanline, scn, xr);
510 else
511 for (i = xr; i--; ) {
512 wtr->scanline[3*i] = scn[3*i+BLU];
513 wtr->scanline[3*i+1] = scn[3*i+GRN];
514 wtr->scanline[3*i+2] = scn[3*i+RED];
515 }
516 if ((i = BMPwriteScanline(wtr)) != BIR_OK)
517 quiterr(BMPerrorMessage(i));
518 }
519 /* flush output */
520 if (fflush((FILE *)wtr->c_data) < 0)
521 quiterr("error writing BMP output");
522 /* clean up */
523 if (fnin != NULL)
524 fclose(fp);
525 free(pa);
526 BMPcloseOutput(wtr);
527 }