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

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.16 static const char RCSid[] = "$Id: ra_bmp.c,v 2.15 2024/09/10 20:24:42 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * program to convert between RADIANCE and Windows BMP file
6     */
7    
8 greg 2.9 #include <math.h>
9 schorsch 2.3
10 greg 2.13 #include "rtio.h"
11 greg 2.16 #include "paths.h"
12 greg 2.1 #include "platform.h"
13     #include "color.h"
14 greg 2.4 #include "tonemap.h"
15 greg 2.1 #include "resolu.h"
16     #include "bmpfile.h"
17    
18 greg 2.9 int bradj = 0; /* brightness adjustment */
19 greg 2.1
20 greg 2.9 double gamcor = 2.2; /* gamma correction value */
21 greg 2.1
22 greg 2.4 static void quiterr(const char *err);
23     static void tmap2bmp(char *fnin, char *fnout, char *expec,
24     RGBPRIMP monpri, double gamval);
25 greg 2.9 static void rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, RGBPRIMP monpri);
26 schorsch 2.3 static void bmp2rad(BMPReader *brd, FILE *rfp, int inv);
27 greg 2.1
28 greg 2.9 static RGBPRIMP rgbinp = stdprims; /* RGB input primitives */
29     static RGBPRIMS myinprims; /* custom primitives holder */
30    
31     static gethfunc headline;
32    
33 greg 2.1
34     int
35     main(int argc, char *argv[])
36     {
37 greg 2.4 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 greg 2.1
45 greg 2.16 fixargv0(argv[0]); /* assigns progname */
46 greg 2.1
47     for (i = 1; i < argc; i++)
48 greg 2.6 if (argv[i][0] == '-' && argv[i][1])
49 greg 2.1 switch (argv[i][1]) {
50     case 'b':
51 greg 2.4 rgbp = NULL;
52 greg 2.1 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 greg 2.4 expec = argv[++i];
59     else
60     bradj = atoi(argv[++i]);
61     break;
62     case 'p':
63     if (argc-i < 9)
64 greg 2.1 goto userr;
65 greg 2.4 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 greg 2.1 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 greg 2.4 /* 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 greg 2.1
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 greg 2.2 /* write scans downward if we can */
135 greg 2.1 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 greg 2.2 BMPcloseInput(rdr);
142 greg 2.1 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 greg 2.9 if (getheader(stdin, headline, NULL) < 0 ||
155 greg 2.1 !fgetsresolu(&rs, stdin))
156     quiterr("bad Radiance picture format");
157     /* initialize BMP header */
158 greg 2.4 if (rgbp == NULL) {
159     hdr = BMPmappedHeader(scanlen(&rs),
160     numscans(&rs), 0, 256);
161 greg 2.10 /*
162 greg 2.2 if (outfile != NULL)
163     hdr->compr = BI_RLE8;
164 greg 2.10 */
165 greg 2.2 } else
166 greg 2.4 hdr = BMPtruecolorHeader(scanlen(&rs),
167     numscans(&rs), 0);
168 greg 2.1 if (hdr == NULL)
169     quiterr("cannot initialize BMP header");
170 greg 2.2 /* set up output direction */
171 greg 2.7 hdr->yIsDown = ((outfile == NULL) | (hdr->compr == BI_RLE8));
172 greg 2.1 /* open BMP output */
173     if (outfile != NULL)
174     wtr = BMPopenOutputFile(outfile, hdr);
175     else
176     wtr = BMPopenOutputStream(stdout, hdr);
177 greg 2.2 if (wtr == NULL)
178     quiterr("cannot allocate writer structure");
179 greg 2.1 /* convert file */
180 greg 2.9 rad2bmp(stdin, wtr, !hdr->yIsDown, rgbp);
181 greg 2.1 /* flush output */
182     if (fflush((FILE *)wtr->c_data) < 0)
183     quiterr("error writing BMP output");
184 greg 2.2 BMPcloseOutput(wtr);
185 greg 2.1 }
186 greg 2.4 return(0); /* success */
187 greg 2.1 userr:
188     fprintf(stderr,
189 greg 2.4 "Usage: %s [-b][-g gamma][-e spec][-p xr yr xg yg xb yb xw yw] [input|- [output]]\n",
190 greg 2.1 progname);
191 greg 2.4 fprintf(stderr,
192     " or: %s -r [-g gamma][-e +/-stops] [input|- [output]]\n",
193     progname);
194     return(1);
195 greg 2.1 }
196    
197     /* print message and exit */
198 schorsch 2.3 static void
199 greg 2.1 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 greg 2.9 /* process header line (don't echo) */
209     static int
210     headline(char *s, void *p)
211     {
212 greg 2.12 char fmt[MAXFMTLEN];
213 greg 2.9
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 greg 2.15 if (!strcmp(fmt,SPECFMT))
222     return(0);
223 greg 2.9 return(-1);
224     }
225     if (isprims(s)) { /* get input primaries */
226     primsval(myinprims, s);
227     rgbinp = myinprims;
228     return(0);
229     }
230 greg 2.15 if (isncomp(s)) {
231     NCSAMP = ncompval(s);
232     return(0);
233     }
234     if (iswlsplit(s)) {
235     wlsplitval(WLPART, s);
236     return(0);
237     }
238 greg 2.9 /* should I grok colcorr also? */
239     return(0);
240     }
241    
242    
243 greg 2.1 /* convert Radiance picture to BMP */
244 schorsch 2.3 static void
245 greg 2.9 rad2bmp(FILE *rfp, BMPWriter *bwr, int inv, RGBPRIMP monpri)
246 greg 2.1 {
247 greg 2.9 int usexfm = 0;
248     COLORMAT xfm;
249 greg 2.1 COLR *scanin;
250 greg 2.9 COLOR cval;
251 greg 2.1 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 greg 2.9 /* set up color conversion */
258     usexfm = (monpri != NULL ? rgbinp != monpri :
259     rgbinp != TM_XYZPRIM && rgbinp != stdprims);
260     if (usexfm) {
261 greg 2.14 RGBPRIMP destpri = monpri != NULL ? monpri : stdprims;
262     double expcomp = pow(2.0, (double)bradj);
263 greg 2.9 if (rgbinp == TM_XYZPRIM)
264 greg 2.14 compxyz2rgbWBmat(xfm, destpri);
265 greg 2.9 else
266 greg 2.14 comprgb2rgbWBmat(xfm, rgbinp, destpri);
267 greg 2.9 for (y = 0; y < 3; y++)
268     for (x = 0; x < 3; x++)
269     xfm[y][x] *= expcomp;
270     }
271 greg 2.1 /* 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 greg 2.15 if (fread2colrs(scanin, bwr->hdr->width, rfp, NCSAMP, WLPART) < 0)
282 greg 2.1 quiterr("error reading Radiance picture");
283 greg 2.9 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 greg 2.1 shiftcolrs(scanin, bwr->hdr->width, bradj);
293 greg 2.9 if (monpri == NULL && rgbinp != TM_XYZPRIM)
294     for (x = bwr->hdr->width; x--; )
295     scanin[x][GRN] = normbright(scanin[x]);
296 greg 2.1 colrs_gambs(scanin, bwr->hdr->width);
297 greg 2.9 if (monpri == NULL)
298 greg 2.1 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 schorsch 2.3 static void
317 greg 2.1 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 greg 2.4
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 greg 2.11 uby8 *pa;
365 greg 2.4 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 greg 2.5 if (fnout != NULL)
395     hdr->compr = BI_RLE8;
396 greg 2.4 } 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 greg 2.11 uby8 *scn = pa + xr*((tmflags & TM_F_BW) ? 1 : 3)*
410 greg 2.7 (yr-1 - wtr->yscan);
411 greg 2.4 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 greg 2.8 if (fnin != NULL)
427     fclose(fp);
428 greg 2.4 free((void *)pa);
429     BMPcloseOutput(wtr);
430     }