ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bmpfile.c
Revision: 2.1
Committed: Fri Mar 26 03:11:50 2004 UTC (20 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Created ra_bmp program to convert between Radiance pictures and Windows BMP

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2     static const char RCSid[] = "$Id$";
3     #endif
4     /*
5     * Windows and OS/2 BMP file support
6     */
7    
8     /* XXX reading and writing of compressed files currently unsupported */
9    
10     #include <stdio.h>
11     #include <stdlib.h>
12     #include <string.h>
13     #include "bmpfile.h"
14    
15     /* get corresponding error message */
16     const char *
17     BMPerrorMessage(int ec)
18     {
19     switch (ec) {
20     case BIR_OK:
21     return "No error";
22     case BIR_EOF:
23     return "End of BMP image";
24     case BIR_TRUNCATED:
25     return "Truncated BMP image";
26     case BIR_UNSUPPORTED:
27     return "Unsupported BMP feature";
28     case BIR_SEEKERR:
29     return "BMP seek error";
30     }
31     return "Unknown BMP error";
32     }
33    
34     /* compute uncompressed scanline size */
35     static uint32
36     getScanSiz(BMPHeader *hdr)
37     {
38     uint32 scanSiz;
39     /* bytes per scanline */
40     scanSiz = (hdr->bpp*hdr->width + 7) >> 3;
41     if (scanSiz & 3) /* 32-bit word boundary */
42     scanSiz += 4 - (scanSiz & 3);
43    
44     return scanSiz;
45     }
46    
47     /* get next byte from reader */
48     #define rdbyte(c,br) ((br)->fpos += (c=(*(br)->cget)((br)->c_data))!=EOF, c)
49    
50     /* read n bytes */
51     static int
52     rdbytes(char *bp, uint32 n, BMPReader *br)
53     {
54     int c;
55    
56     while (n--) {
57     if (rdbyte(c, br) == EOF)
58     return BIR_TRUNCATED;
59     *bp++ = c;
60     }
61     return BIR_OK;
62     }
63    
64     /* read 32-bit integer in littlendian order */
65     static int32
66     rdint32(BMPReader *br)
67     {
68     int32 i;
69     int c;
70    
71     i = rdbyte(c, br);
72     i |= rdbyte(c, br) << 8;
73     i |= rdbyte(c, br) << 16;
74     i |= rdbyte(c, br) << 24;
75     return i; /* -1 on EOF */
76     }
77    
78     /* read 16-bit unsigned integer in littlendian order */
79     static int
80     rduint16(BMPReader *br)
81     {
82     int i;
83     int c;
84    
85     i = rdbyte(c, br);
86     i |= rdbyte(c, br) << 8;
87     return i; /* -1 on EOF */
88     }
89    
90     /* seek on reader or return 0 (BIR_OK) on success */
91     static int
92     rdseek(uint32 pos, BMPReader *br)
93     {
94     if (pos == br->fpos)
95     return BIR_OK;
96     if (br->seek == NULL || (*br->seek)(pos, br->c_data) != 0)
97     return BIR_SEEKERR;
98     br->fpos = pos;
99     return BIR_OK;
100     }
101    
102     /* open BMP stream for reading and get first scanline */
103     BMPReader *
104     BMPopenReader(int (*cget)(void *), int (*seek)(uint32, void *), void *c_data)
105     {
106     BMPReader *br;
107     uint32 bmPos, hdrSiz;
108     int palLen;
109    
110     if (cget == NULL)
111     return NULL;
112     int magic[2]; /* check magic number */
113     magic[0] = (*cget)(c_data);
114     if (magic[0] != 'B')
115     return NULL;
116     magic[1] = (*cget)(c_data);
117     if (magic[1] != 'M' && magic[1] != 'A')
118     return NULL;
119     br = (BMPReader *)calloc(1, sizeof(BMPReader));
120     if (br == NULL)
121     return NULL;
122     br->cget = cget;
123     br->seek = seek;
124     br->c_data = c_data;
125     br->hdr = (BMPHeader *)malloc(sizeof(BMPHeader));
126     if (br->hdr == NULL)
127     goto err;
128     br->fpos = 2;
129     /* read & verify file header */
130     (void)rdint32(br); /* file size */
131     (void)rdint32(br); /* reserved word */
132     bmPos = rdint32(br); /* offset to bitmap */
133     hdrSiz = rdint32(br) + 14; /* header size */
134     if (hdrSiz < 2 + 6*4 + 2*2 + 6*4)
135     goto err;
136     br->hdr->width = rdint32(br); /* bitmap width */
137     br->hdr->height = rdint32(br); /* bitmap height */
138     if ((br->hdr->width <= 0 | br->hdr->height == 0))
139     goto err;
140     if ((br->hdr->yIsDown = br->hdr->height < 0))
141     br->hdr->height = -br->hdr->height;
142     if (rduint16(br) != 1) /* number of planes */
143     goto err;
144     br->hdr->bpp = rduint16(br); /* bits per pixel */
145     if (br->hdr->bpp != 1 && br->hdr->bpp != 4 &&
146     br->hdr->bpp != 8 && br->hdr->bpp != 24 &&
147     br->hdr->bpp != 32)
148     goto err;
149     br->hdr->compr = rdint32(br); /* compression mode */
150     if (br->hdr->compr != BI_UNCOMPR && br->hdr->compr != BI_RLE8)
151     goto err; /* don't support the rest */
152     (void)rdint32(br); /* bitmap size */
153     br->hdr->hRes = rdint32(br); /* horizontal resolution */
154     br->hdr->vRes = rdint32(br); /* vertical resolution */
155     br->hdr->nColors = rdint32(br); /* # colors used */
156     br->hdr->impColors = rdint32(br); /* # important colors */
157     if (br->hdr->impColors < 0)
158     goto err; /* catch premature EOF */
159     palLen = 0;
160     if (br->hdr->bpp <= 8) {
161     palLen = 1 << br->hdr->bpp;
162     if (br->hdr->nColors <= 0)
163     br->hdr->nColors = palLen;
164     else if (br->hdr->nColors > palLen)
165     goto err;
166     if (br->hdr->impColors <= 0)
167     br->hdr->impColors = br->hdr->nColors;
168     else if (br->hdr->impColors > br->hdr->nColors)
169     goto err;
170     } else if (br->hdr->nColors > 0 || br->hdr->compr != BI_UNCOMPR)
171     goto err;
172     /* extend header */
173     if (bmPos < hdrSiz + sizeof(RGBquad)*palLen)
174     goto err;
175     br->hdr->infoSiz = bmPos - (hdrSiz + sizeof(RGBquad)*palLen);
176     if (palLen > 0 || br->hdr->infoSiz > 0) {
177     br->hdr = (BMPHeader *)realloc((void *)br->hdr,
178     sizeof(BMPHeader) +
179     sizeof(RGBquad)*palLen -
180     sizeof(br->hdr->palette) +
181     br->hdr->infoSiz);
182     if (br->hdr == NULL)
183     goto err;
184     }
185     /* read color palette */
186     if (rdbytes((char *)br->hdr->palette,
187     sizeof(RGBquad)*palLen, br) != BIR_OK)
188     goto err;
189     /* read add'l information */
190     if (rdbytes(BMPinfo(br->hdr), br->hdr->infoSiz, br) != BIR_OK)
191     goto err;
192     /* read first scanline */
193     br->scanline = (uint8 *)calloc(getScanSiz(br->hdr), sizeof(uint8));
194     if (br->scanline == NULL)
195     goto err;
196     br->yscan = -1;
197     if (seek != NULL && br->hdr->compr != BI_UNCOMPR) {
198     BMPReader *newbr = (BMPReader *)realloc((void *)br,
199     sizeof(BMPReader) +
200     sizeof(br->scanpos[0]) *
201     br->hdr->height);
202     if (newbr == NULL)
203     goto err;
204     br = newbr;
205     memset((void *)(br->scanpos + 1), 0,
206     sizeof(br->scanpos[0])*br->hdr->height);
207     }
208     br->scanpos[0] = br->fpos;
209     if (BMPreadScanline(br) != BIR_OK)
210     goto err;
211     return br;
212     err:
213     if (br->hdr != NULL)
214     free((void *)br->hdr);
215     if (br->scanline != NULL)
216     free((void *)br->scanline);
217     free((void *)br);
218     return NULL;
219     }
220    
221     /* determine if image is grayscale */
222     int
223     BMPisGrayscale(const BMPHeader *hdr)
224     {
225     const RGBquad *rgbp;
226     int n;
227    
228     if (hdr == NULL)
229     return -1;
230     if (hdr->bpp > 8) /* assume they had a reason for it */
231     return 0;
232     for (rgbp = hdr->palette, n = hdr->impColors; n-- > 0; rgbp++)
233     if ((rgbp->r != rgbp->g | rgbp->g != rgbp->b))
234     return 0;
235     return 1; /* all colors neutral in map */
236     }
237    
238     /* read and decode next BMP scanline */
239     int
240     BMPreadScanline(BMPReader *br)
241     {
242     if (br->yscan + 1 >= br->hdr->height)
243     return BIR_EOF;
244     br->yscan++; /* prepare to read */
245     if (br->hdr->compr == BI_UNCOMPR)
246     return rdbytes((char *)br->scanline, getScanSiz(br->hdr), br);
247     /* XXX need to perform actual decompression */
248     return BIR_UNSUPPORTED;
249     if (br->seek != NULL)
250     br->scanpos[br->yscan + 1] = br->fpos;
251     return BIR_OK;
252     }
253    
254     /* read a specific scanline */
255     int
256     BMPseekScanline(int y, BMPReader *br)
257     {
258     int rv;
259     /* check arguments */
260     if (br == NULL)
261     return BIR_EOF;
262     if (y < 0)
263     return BIR_SEEKERR;
264     if (y >= br->hdr->height)
265     return BIR_EOF;
266     /* already read? */
267     if (y == br->yscan)
268     return BIR_OK;
269     /* shall we seek? */
270     if (y != br->yscan + 1 && br->seek != NULL) {
271     int yseek;
272     uint32 seekp;
273     if (br->hdr->compr == BI_UNCOMPR) {
274     yseek = y;
275     seekp = br->scanpos[0] + y*getScanSiz(br->hdr);
276     } else {
277     yseek = br->yscan + 1;
278     while (yseek < y && br->scanpos[yseek+1] != 0)
279     ++yseek;
280     if (y < yseek && br->scanpos[yseek=y] == 0)
281     return BIR_SEEKERR;
282     seekp = br->scanpos[yseek];
283     }
284     if ((rv = rdseek(seekp, br)) != BIR_OK)
285     return rv;
286     br->yscan = yseek - 1;
287     } else if (y < br->yscan) /* else we can't back up */
288     return BIR_SEEKERR;
289     /* read until we get there */
290     while (br->yscan < y)
291     if ((rv = BMPreadScanline(br)) != BIR_OK)
292     return rv;
293     return BIR_OK;
294     }
295    
296     /* get ith pixel from last scanline */
297     RGBquad
298     BMPdecodePixel(int i, BMPReader *br)
299     {
300     static const RGBquad black = {0, 0, 0, 0};
301    
302     if ((br == NULL | i < 0) || i >= br->hdr->width)
303     return black;
304    
305     switch (br->hdr->bpp) {
306     case 24: {
307     uint8 *bgr = br->scanline + 3*i;
308     RGBquad cval;
309     cval.b = bgr[0]; cval.g = bgr[1]; cval.r = bgr[2];
310     cval.padding = 0;
311     return cval;
312     }
313     case 32:
314     return ((RGBquad *)br->scanline)[i];
315     case 8:
316     return br->hdr->palette[br->scanline[i]];
317     case 1:
318     return br->hdr->palette[br->scanline[i>>3]>>(i&7) & 1];
319     case 4:
320     return br->hdr->palette[br->scanline[i>>1]>>((i&1)<<2) & 0xf];
321     }
322     return black; /* should never happen */
323     }
324    
325     /* free BMP reader resources */
326     void
327     BMPfreeReader(BMPReader *br)
328     {
329     if (br == NULL)
330     return;
331     free((void *)br->hdr);
332     free((void *)br->scanline);
333     free((void *)br);
334     }
335    
336     /* stdio getc() callback */
337     int
338     stdio_getc(void *p)
339     {
340     if (!p)
341     return EOF;
342     return getc((FILE *)p);
343     }
344    
345     /* stdio putc() callback */
346     void
347     stdio_putc(int c, void *p)
348     {
349     if (p)
350     putc(c, (FILE *)p);
351     }
352    
353     /* stdio fseek() callback */
354     int
355     stdio_fseek(uint32 pos, void *p)
356     {
357     if (!p)
358     return -1;
359     return fseek((FILE *)p, (long)pos, 0);
360     }
361    
362     /* allocate uncompressed (24-bit) RGB header */
363     BMPHeader *
364     BMPtruecolorHeader(int xr, int yr, int infolen)
365     {
366     BMPHeader *hdr;
367    
368     if (xr <= 0 || yr <= 0 || infolen < 0)
369     return NULL;
370     hdr = (BMPHeader *)malloc(sizeof(BMPHeader) - sizeof(hdr->palette) +
371     infolen);
372     if (hdr == NULL)
373     return NULL;
374     hdr->width = xr;
375     hdr->height = yr;
376     hdr->yIsDown = 0; /* default to upwards order */
377     hdr->bpp = 24;
378     hdr->compr = BI_UNCOMPR;
379     hdr->hRes = hdr->vRes = 2835; /* default to 72 ppi */
380     hdr->nColors = hdr->impColors = 0;
381     hdr->infoSiz = infolen;
382     return hdr;
383     }
384    
385     /* allocate color-mapped header (defaults to minimal grayscale) */
386     BMPHeader *
387     BMPmappedHeader(int xr, int yr, int infolen, int ncolors)
388     {
389     int n;
390     BMPHeader *hdr;
391    
392     if (xr <= 0 || yr <= 0 || infolen < 0 || ncolors < 2)
393     return NULL;
394     if (ncolors <= 2)
395     n = 1;
396     else if (ncolors <= 16)
397     n = 4;
398     else if (ncolors <= 256)
399     n = 8;
400     else
401     return NULL;
402     hdr = (BMPHeader *)malloc(sizeof(BMPHeader) +
403     sizeof(RGBquad)*(1<<n) -
404     sizeof(hdr->palette) +
405     infolen);
406     if (hdr == NULL)
407     return NULL;
408     hdr->width = xr;
409     hdr->height = yr;
410     hdr->yIsDown = 0; /* default to upwards order */
411     hdr->bpp = n;
412     hdr->compr = BI_UNCOMPR;
413     hdr->hRes = hdr->vRes = 2835; /* default to 72 ppi */
414     hdr->nColors = ncolors;
415     hdr->impColors = 0; /* says all colors important */
416     hdr->infoSiz = infolen;
417     memset((void *)hdr->palette, 0, sizeof(RGBquad)*(1<<n) + infolen);
418     for (n = ncolors; n--; )
419     hdr->palette[n].r = hdr->palette[n].g =
420     hdr->palette[n].b = n*255/(ncolors-1);
421     return hdr;
422     }
423    
424     /* put byte to writer */
425     #define wrbyte(c,bw) ( (*(bw)->cput)(c,(bw)->c_data), \
426     ++((bw)->fpos) > (bw)->flen ? \
427     ((bw)->flen = (bw)->fpos) : \
428     (bw)->fpos )
429    
430     /* write out a string of bytes */
431     static void
432     wrbytes(char *bp, uint32 n, BMPWriter *bw)
433     {
434     while (n--)
435     wrbyte(*bp++, bw);
436     }
437    
438     /* write 32-bit integer in littlendian order */
439     static void
440     wrint32(int32 i, BMPWriter *bw)
441     {
442     wrbyte(i& 0xff, bw);
443     wrbyte(i>>8 & 0xff, bw);
444     wrbyte(i>>16 & 0xff, bw);
445     wrbyte(i>>24 & 0xff, bw);
446     }
447    
448     /* write 16-bit unsigned integer in littlendian order */
449     static void
450     wruint16(uint16 ui, BMPWriter *bw)
451     {
452     wrbyte(ui & 0xff, bw);
453     wrbyte(ui>>8 & 0xff, bw);
454     }
455    
456     /* seek to the specified file position, returning 0 (BIR_OK) on success */
457     static int
458     wrseek(uint32 pos, BMPWriter *bw)
459     {
460     if (pos == bw->fpos)
461     return BIR_OK;
462     if (bw->seek == NULL) {
463     if (pos < bw->fpos)
464     return BIR_SEEKERR;
465     while (bw->fpos < pos)
466     wrbyte(0, bw);
467     return BIR_OK;
468     }
469     if ((*bw->seek)(pos, bw->c_data) != 0)
470     return BIR_SEEKERR;
471     bw->fpos = pos;
472     if (pos > bw->flen)
473     bw->flen = pos;
474     return BIR_OK;
475     }
476    
477     /* open BMP stream for writing */
478     BMPWriter *
479     BMPopenWriter(void (*cput)(int, void *), int (*seek)(uint32, void *),
480     void *c_data, BMPHeader *hdr)
481     {
482     BMPWriter *bw;
483     uint32 hdrSiz, palSiz, scanSiz, bmSiz;
484     /* check arguments */
485     if (cput == NULL || hdr == NULL)
486     return NULL;
487     if (hdr->width <= 0 || hdr->height <= 0)
488     return NULL;
489     if (hdr->compr != BI_UNCOMPR && hdr->compr != BI_RLE8)
490     return NULL; /* unsupported */
491     if (hdr->bpp > 8 && hdr->compr != BI_UNCOMPR)
492     return NULL;
493     palSiz = BMPpalLen(hdr);
494     if (hdr->nColors > palSiz)
495     return NULL;
496     if (hdr->compr != BI_UNCOMPR && seek == NULL)
497     return NULL;
498     /* compute sizes */
499     hdrSiz = 2 + 6*4 + 2*2 + 6*4;
500     palSiz *= sizeof(RGBquad);
501     scanSiz = getScanSiz(hdr);
502     bmSiz = hdr->height*scanSiz; /* wrong if compressed */
503     /* initialize writer */
504     bw = (BMPWriter *)malloc(sizeof(BMPWriter));
505     if (bw == NULL)
506     return NULL;
507     bw->hdr = hdr;
508     bw->yscan = 0;
509     bw->scanline = (uint8 *)calloc(scanSiz, sizeof(uint8));
510     if (bw->scanline == NULL) {
511     free((void *)bw);
512     return NULL;
513     }
514     bw->fbmp = hdrSiz + palSiz + hdr->infoSiz;
515     bw->fpos = bw->flen = 0;
516     bw->cput = cput;
517     bw->seek = seek;
518     bw->c_data = c_data;
519     /* write out header */
520     wrbyte('B', bw); wrbyte('M', bw); /* magic number */
521     wrint32(bw->fbmp + bmSiz, bw); /* file size */
522     wrint32(0, bw); /* reserved word */
523     wrint32(bw->fbmp, bw); /* offset to bitmap */
524     wrint32(hdrSiz - bw->fpos, bw); /* info header size */
525     wrint32(hdr->width, bw); /* bitmap width */
526     if (hdr->yIsDown) /* bitmap height */
527     wrint32(-hdr->height, bw);
528     else
529     wrint32(hdr->height, bw);
530     wruint16(1, bw); /* number of planes */
531     wruint16(hdr->bpp, bw); /* bits per pixel */
532     wrint32(hdr->compr, bw); /* compression mode */
533     wrint32(bmSiz, bw); /* bitmap size */
534     wrint32(hdr->hRes, bw); /* horizontal resolution */
535     wrint32(hdr->vRes, bw); /* vertical resolution */
536     wrint32(hdr->nColors, bw); /* # colors used */
537     wrint32(hdr->impColors, bw); /* # important colors */
538     /* write out color palette */
539     wrbytes((char *)hdr->palette, palSiz, bw);
540     /* write add'l information */
541     wrbytes(BMPinfo(hdr), hdr->infoSiz, bw);
542     #ifndef NDEBUG
543     if (bw->fpos != bw->fbmp) {
544     fputs("Coding error 1 in BMPopenWriter\n", stderr);
545     exit(1);
546     }
547     #endif
548     return bw;
549     }
550    
551     /* write the current scanline */
552     int
553     BMPwriteScanline(BMPWriter *bw)
554     {
555     if (bw->yscan >= bw->hdr->height)
556     return BIR_EOF;
557     /* writing uncompressed? */
558     if (bw->hdr->compr == BI_UNCOMPR) {
559     uint32 scanSiz = getScanSiz(bw->hdr);
560     uint32 slpos = bw->fbmp + bw->yscan*scanSiz;
561     if (wrseek(slpos, bw) != BIR_OK)
562     return BIR_SEEKERR;
563     wrbytes((char *)bw->scanline, scanSiz, bw);
564     bw->yscan++;
565     return BIR_OK;
566     }
567     /* else write compressed */
568     /* XXX need to do actual compression */
569     return BIR_UNSUPPORTED;
570     /* write file length at end */
571     if (bw->yscan == bw->hdr->height) {
572     if (bw->seek == NULL || (*bw->seek)(2, bw->c_data) != 0)
573     return BIR_SEEKERR;
574     bw->fpos = 2;
575     wrint32(bw->flen, bw);
576     }
577     return BIR_OK;
578     }
579    
580     /* free BMP writer resources */
581     void
582     BMPfreeWriter(BMPWriter *bw)
583     {
584     if (bw == NULL)
585     return;
586     free((void *)bw->hdr);
587     free((void *)bw->scanline);
588     free((void *)bw);
589     }