ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/ra_bmp.c
Revision: 2.16
Committed: Fri Jun 6 19:11:21 2025 UTC (2 weeks, 6 days ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.15: +3 -4 lines
Log Message:
refactor: Making use of printargs() more consistent with fixargv0()

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: ra_bmp.c,v 2.15 2024/09/10 20:24:42 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 "paths.h"
12 #include "platform.h"
13 #include "color.h"
14 #include "tonemap.h"
15 #include "resolu.h"
16 #include "bmpfile.h"
17
18 int bradj = 0; /* brightness adjustment */
19
20 double gamcor = 2.2; /* gamma correction value */
21
22 static void quiterr(const char *err);
23 static void tmap2bmp(char *fnin, char *fnout, char *expec,
24 RGBPRIMP monpri, double gamval);
25 static void rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, RGBPRIMP monpri);
26 static void bmp2rad(BMPReader *brd, FILE *rfp, int inv);
27
28 static RGBPRIMP rgbinp = stdprims; /* RGB input primitives */
29 static RGBPRIMS myinprims; /* custom primitives holder */
30
31 static gethfunc headline;
32
33
34 int
35 main(int argc, char *argv[])
36 {
37 char *inpfile=NULL, *outfile=NULL;
38 char *expec = NULL;
39 int reverse = 0;
40 RGBPRIMP rgbp = stdprims;
41 RGBPRIMS myprims;
42 RESOLU rs;
43 int i;
44
45 fixargv0(argv[0]); /* assigns progname */
46
47 for (i = 1; i < argc; i++)
48 if (argv[i][0] == '-' && argv[i][1])
49 switch (argv[i][1]) {
50 case 'b':
51 rgbp = NULL;
52 break;
53 case 'g':
54 gamcor = atof(argv[++i]);
55 break;
56 case 'e':
57 if (argv[i+1][0] != '+' && argv[i+1][0] != '-')
58 expec = argv[++i];
59 else
60 bradj = atoi(argv[++i]);
61 break;
62 case 'p':
63 if (argc-i < 9)
64 goto userr;
65 myprims[RED][CIEX] = atof(argv[++i]);
66 myprims[RED][CIEY] = atof(argv[++i]);
67 myprims[GRN][CIEX] = atof(argv[++i]);
68 myprims[GRN][CIEY] = atof(argv[++i]);
69 myprims[BLU][CIEX] = atof(argv[++i]);
70 myprims[BLU][CIEY] = atof(argv[++i]);
71 myprims[WHT][CIEX] = atof(argv[++i]);
72 myprims[WHT][CIEY] = atof(argv[++i]);
73 if (rgbp == stdprims)
74 rgbp = myprims;
75 break;
76 case 'r':
77 reverse = !reverse;
78 break;
79 default:
80 goto userr;
81 }
82 else
83 break;
84
85 if (i < argc-2)
86 goto userr;
87
88 SET_FILE_BINARY(stdin);
89 SET_FILE_BINARY(stdout);
90 SET_DEFAULT_BINARY();
91
92 if (i <= argc-1 && strcmp(argv[i], "-"))
93 inpfile = argv[i];
94
95 if (i == argc-2 && strcmp(argv[i+1], "-"))
96 outfile = argv[i+1];
97 /* check for tone-mapping */
98 if (expec != NULL) {
99 if (reverse)
100 goto userr;
101 tmap2bmp(inpfile, outfile, expec, rgbp, gamcor);
102 return(0);
103 }
104
105 setcolrgam(gamcor); /* set up conversion */
106
107 if (reverse) {
108 BMPReader *rdr;
109 /* open BMP file or stream */
110 if (inpfile != NULL)
111 rdr = BMPopenInputFile(inpfile);
112 else
113 rdr = BMPopenInputStream(stdin);
114
115 if (rdr == NULL) {
116 fprintf(stderr, "%s: cannot open or recognize BMP\n",
117 inpfile != NULL ? inpfile : "<stdin>");
118 exit(1);
119 }
120 /* open Radiance output */
121 if (outfile != NULL && freopen(outfile, "w", stdout) == NULL) {
122 fprintf(stderr, "%s: cannot open for output\n",
123 outfile);
124 exit(1);
125 }
126 /* put Radiance header */
127 newheader("RADIANCE", stdout);
128 printargs(i, argv, stdout);
129 fputformat(COLRFMT, stdout);
130 putchar('\n');
131 rs.xr = rdr->hdr->width;
132 rs.yr = rdr->hdr->height;
133 rs.rt = YMAJOR;
134 /* write scans downward if we can */
135 if (rdr->hdr->yIsDown || inpfile != NULL)
136 rs.rt |= YDECR;
137 fputsresolu(&rs, stdout);
138 /* convert file */
139 bmp2rad(rdr, stdout, !rdr->hdr->yIsDown && inpfile!=NULL);
140 /* flush output */
141 BMPcloseInput(rdr);
142 if (fflush(stdout) < 0)
143 quiterr("error writing Radiance output");
144 } else {
145 BMPHeader *hdr;
146 BMPWriter *wtr;
147 /* open Radiance input */
148 if (inpfile != NULL && freopen(inpfile, "r", stdin) == NULL) {
149 fprintf(stderr, "%s: cannot open input file\n",
150 inpfile);
151 exit(1);
152 }
153 /* get header info. */
154 if (getheader(stdin, headline, NULL) < 0 ||
155 !fgetsresolu(&rs, stdin))
156 quiterr("bad Radiance picture format");
157 /* initialize BMP header */
158 if (rgbp == NULL) {
159 hdr = BMPmappedHeader(scanlen(&rs),
160 numscans(&rs), 0, 256);
161 /*
162 if (outfile != NULL)
163 hdr->compr = BI_RLE8;
164 */
165 } else
166 hdr = BMPtruecolorHeader(scanlen(&rs),
167 numscans(&rs), 0);
168 if (hdr == NULL)
169 quiterr("cannot initialize BMP header");
170 /* set up output direction */
171 hdr->yIsDown = ((outfile == NULL) | (hdr->compr == BI_RLE8));
172 /* open BMP output */
173 if (outfile != NULL)
174 wtr = BMPopenOutputFile(outfile, hdr);
175 else
176 wtr = BMPopenOutputStream(stdout, hdr);
177 if (wtr == NULL)
178 quiterr("cannot allocate writer structure");
179 /* convert file */
180 rad2bmp(stdin, wtr, !hdr->yIsDown, rgbp);
181 /* flush output */
182 if (fflush((FILE *)wtr->c_data) < 0)
183 quiterr("error writing BMP output");
184 BMPcloseOutput(wtr);
185 }
186 return(0); /* success */
187 userr:
188 fprintf(stderr,
189 "Usage: %s [-b][-g gamma][-e spec][-p xr yr xg yg xb yb xw yw] [input|- [output]]\n",
190 progname);
191 fprintf(stderr,
192 " or: %s -r [-g gamma][-e +/-stops] [input|- [output]]\n",
193 progname);
194 return(1);
195 }
196
197 /* print message and exit */
198 static void
199 quiterr(const char *err)
200 {
201 if (err != NULL) {
202 fprintf(stderr, "%s: %s\n", progname, err);
203 exit(1);
204 }
205 exit(0);
206 }
207
208 /* process header line (don't echo) */
209 static int
210 headline(char *s, void *p)
211 {
212 char fmt[MAXFMTLEN];
213
214 if (formatval(fmt, s)) { /* check if format string */
215 if (!strcmp(fmt,COLRFMT))
216 return(0);
217 if (!strcmp(fmt,CIEFMT)) {
218 rgbinp = TM_XYZPRIM;
219 return(0);
220 }
221 if (!strcmp(fmt,SPECFMT))
222 return(0);
223 return(-1);
224 }
225 if (isprims(s)) { /* get input primaries */
226 primsval(myinprims, s);
227 rgbinp = myinprims;
228 return(0);
229 }
230 if (isncomp(s)) {
231 NCSAMP = ncompval(s);
232 return(0);
233 }
234 if (iswlsplit(s)) {
235 wlsplitval(WLPART, s);
236 return(0);
237 }
238 /* should I grok colcorr also? */
239 return(0);
240 }
241
242
243 /* convert Radiance picture to BMP */
244 static void
245 rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, RGBPRIMP monpri)
246 {
247 int usexfm = 0;
248 COLORMAT xfm;
249 COLR *scanin;
250 COLOR cval;
251 int y, yend, ystp;
252 int x;
253 /* allocate scanline */
254 scanin = (COLR *)malloc(bwr->hdr->width*sizeof(COLR));
255 if (scanin == NULL)
256 quiterr("out of memory in rad2bmp");
257 /* set up color conversion */
258 usexfm = (monpri != NULL ? rgbinp != monpri :
259 rgbinp != TM_XYZPRIM && rgbinp != stdprims);
260 if (usexfm) {
261 RGBPRIMP destpri = monpri != NULL ? monpri : stdprims;
262 double expcomp = pow(2.0, (double)bradj);
263 if (rgbinp == TM_XYZPRIM)
264 compxyz2rgbWBmat(xfm, destpri);
265 else
266 comprgb2rgbWBmat(xfm, rgbinp, destpri);
267 for (y = 0; y < 3; y++)
268 for (x = 0; x < 3; x++)
269 xfm[y][x] *= expcomp;
270 }
271 /* convert image */
272 if (inv) {
273 y = bwr->hdr->height - 1;
274 ystp = -1; yend = -1;
275 } else {
276 y = 0;
277 ystp = 1; yend = bwr->hdr->height;
278 }
279 /* convert each scanline */
280 for ( ; y != yend; y += ystp) {
281 if (fread2colrs(scanin, bwr->hdr->width, rfp, NCSAMP, WLPART) < 0)
282 quiterr("error reading Radiance picture");
283 if (usexfm)
284 for (x = bwr->hdr->width; x--; ) {
285 colr_color(cval, scanin[x]);
286 colortrans(cval, xfm, cval);
287 setcolr(scanin[x], colval(cval,RED),
288 colval(cval,GRN),
289 colval(cval,BLU));
290 }
291 else if (bradj)
292 shiftcolrs(scanin, bwr->hdr->width, bradj);
293 if (monpri == NULL && rgbinp != TM_XYZPRIM)
294 for (x = bwr->hdr->width; x--; )
295 scanin[x][GRN] = normbright(scanin[x]);
296 colrs_gambs(scanin, bwr->hdr->width);
297 if (monpri == NULL)
298 for (x = bwr->hdr->width; x--; )
299 bwr->scanline[x] = scanin[x][GRN];
300 else
301 for (x = bwr->hdr->width; x--; ) {
302 bwr->scanline[3*x] = scanin[x][BLU];
303 bwr->scanline[3*x+1] = scanin[x][GRN];
304 bwr->scanline[3*x+2] = scanin[x][RED];
305 }
306 bwr->yscan = y;
307 x = BMPwriteScanline(bwr);
308 if (x != BIR_OK)
309 quiterr(BMPerrorMessage(x));
310 }
311 /* free scanline */
312 free((void *)scanin);
313 }
314
315 /* convert BMP file to Radiance */
316 static void
317 bmp2rad(BMPReader *brd, FILE *rfp, int inv)
318 {
319 COLR *scanout;
320 int y, yend, ystp;
321 int x;
322 /* allocate scanline */
323 scanout = (COLR *)malloc(brd->hdr->width*sizeof(COLR));
324 if (scanout == NULL)
325 quiterr("out of memory in bmp2rad");
326 /* convert image */
327 if (inv) {
328 y = brd->hdr->height - 1;
329 ystp = -1; yend = -1;
330 } else {
331 y = 0;
332 ystp = 1; yend = brd->hdr->height;
333 }
334 /* convert each scanline */
335 for ( ; y != yend; y += ystp) {
336 x = BMPseekScanline(y, brd);
337 if (x != BIR_OK)
338 quiterr(BMPerrorMessage(x));
339 for (x = brd->hdr->width; x--; ) {
340 RGBquad rgbq = BMPdecodePixel(x, brd);
341 scanout[x][RED] = rgbq.r;
342 scanout[x][GRN] = rgbq.g;
343 scanout[x][BLU] = rgbq.b;
344 }
345 gambs_colrs(scanout, brd->hdr->width);
346 if (bradj)
347 shiftcolrs(scanout, brd->hdr->width, bradj);
348 if (fwritecolrs(scanout, brd->hdr->width, rfp) < 0)
349 quiterr("error writing Radiance picture");
350 }
351 /* clean up */
352 free((void *)scanout);
353 }
354
355 /* Tone-map and convert Radiance picture */
356 static void
357 tmap2bmp(char *fnin, char *fnout, char *expec, RGBPRIMP monpri, double gamval)
358 {
359 int tmflags;
360 BMPHeader *hdr;
361 BMPWriter *wtr;
362 FILE *fp;
363 int xr, yr;
364 uby8 *pa;
365 int i;
366 /* check tone-mapping spec */
367 i = strlen(expec);
368 if (i && !strncmp(expec, "auto", i))
369 tmflags = TM_F_CAMERA;
370 else if (i && !strncmp(expec, "human", i))
371 tmflags = TM_F_HUMAN & ~TM_F_UNIMPL;
372 else if (i && !strncmp(expec, "linear", i))
373 tmflags = TM_F_LINEAR;
374 else
375 quiterr("illegal exposure specification (auto|human|linear)");
376 if (monpri == NULL) {
377 tmflags |= TM_F_BW;
378 monpri = stdprims;
379 }
380 /* open Radiance input */
381 if (fnin == NULL)
382 fp = stdin;
383 else if ((fp = fopen(fnin, "r")) == NULL) {
384 fprintf(stderr, "%s: cannot open\n", fnin);
385 exit(1);
386 }
387 /* tone-map picture */
388 if (tmMapPicture(&pa, &xr, &yr, tmflags, monpri, gamval,
389 0., 0., fnin, fp) != TM_E_OK)
390 exit(1);
391 /* initialize BMP header */
392 if (tmflags & TM_F_BW) {
393 hdr = BMPmappedHeader(xr, yr, 0, 256);
394 if (fnout != NULL)
395 hdr->compr = BI_RLE8;
396 } else
397 hdr = BMPtruecolorHeader(xr, yr, 0);
398 if (hdr == NULL)
399 quiterr("cannot initialize BMP header");
400 /* open BMP output */
401 if (fnout != NULL)
402 wtr = BMPopenOutputFile(fnout, hdr);
403 else
404 wtr = BMPopenOutputStream(stdout, hdr);
405 if (wtr == NULL)
406 quiterr("cannot allocate writer structure");
407 /* write to BMP file */
408 while (wtr->yscan < yr) {
409 uby8 *scn = pa + xr*((tmflags & TM_F_BW) ? 1 : 3)*
410 (yr-1 - wtr->yscan);
411 if (tmflags & TM_F_BW)
412 memcpy((void *)wtr->scanline, (void *)scn, xr);
413 else
414 for (i = xr; i--; ) {
415 wtr->scanline[3*i] = scn[3*i+BLU];
416 wtr->scanline[3*i+1] = scn[3*i+GRN];
417 wtr->scanline[3*i+2] = scn[3*i+RED];
418 }
419 if ((i = BMPwriteScanline(wtr)) != BIR_OK)
420 quiterr(BMPerrorMessage(i));
421 }
422 /* flush output */
423 if (fflush((FILE *)wtr->c_data) < 0)
424 quiterr("error writing BMP output");
425 /* clean up */
426 if (fnin != NULL)
427 fclose(fp);
428 free((void *)pa);
429 BMPcloseOutput(wtr);
430 }