ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/ra_bmp.c
Revision: 2.15
Committed: Tue Sep 10 20:24:42 2024 UTC (7 months, 3 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.14: +12 -2 lines
Log Message:
feat(ra_bmp,ra_rgbe,ximage): Added quick conversion of spectral inputs to RGB

File Contents

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