ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bmpfile.c
Revision: 2.2
Committed: Fri Mar 26 21:29:19 2004 UTC (20 years, 1 month ago) by schorsch
Content type: text/plain
Branch: MAIN
Changes since 2.1: +4 -4 lines
Log Message:
Small fixes to reduce warnings. Added bmpfile.c to SConscript.

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bmpfile.c,v 2.1 2004/03/26 03:11:50 greg Exp $";
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 }