ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/ra_bmp.c
Revision: 2.17
Committed: Sat Jun 7 05:09:46 2025 UTC (2 weeks, 1 day ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.16: +1 -2 lines
Log Message:
refactor: Put some declarations into "paths.h" and included in "platform.h"

File Contents

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