ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bmpfile.c
(Generate patch)

Comparing ray/src/common/bmpfile.c (file contents):
Revision 2.2 by schorsch, Fri Mar 26 21:29:19 2004 UTC vs.
Revision 2.3 by greg, Fri Mar 26 22:58:21 2004 UTC

# Line 5 | Line 5 | static const char RCSid[] = "$Id$";
5   *  Windows and OS/2 BMP file support
6   */
7  
8 /* XXX reading and writing of compressed files currently unsupported */
9
8   #include <stdio.h>
9   #include <stdlib.h>
10   #include <string.h>
# Line 25 | Line 23 | BMPerrorMessage(int ec)
23                  return "Truncated BMP image";
24          case BIR_UNSUPPORTED:
25                  return "Unsupported BMP feature";
26 +        case BIR_RLERROR:
27 +                return "BMP runlength encoding error";
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)
34 > /* check than header is sensible */
35 > static int
36 > BMPheaderOK(const 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;
38 >        if (!hdr)
39 >                return 0;
40 >        if ((hdr->width <= 0 | hdr->height <= 0))
41 >                return 0;
42 >        switch (hdr->bpp) {             /* check compression */
43 >        case 1:
44 >        case 24:
45 >                if (hdr->compr != BI_UNCOMPR)
46 >                        return 0;
47 >                break;
48 >        case 16:
49 >        case 32:
50 >                if ((hdr->compr != BI_UNCOMPR & hdr->compr != BI_BITFIELDS))
51 >                        return 0;
52 >                break;
53 >        case 4:
54 >                if ((hdr->compr != BI_UNCOMPR & hdr->compr != BI_RLE4))
55 >                        return 0;
56 >                break;
57 >        case 8:
58 >                if ((hdr->compr != BI_UNCOMPR & hdr->compr != BI_RLE8))
59 >                        return 0;
60 >                break;
61 >        default:
62 >                return 0;
63 >        }
64 >        if (hdr->compr == BI_BITFIELDS && (BMPbitField(hdr)[0] &
65 >                                BMPbitField(hdr)[1] & BMPbitField(hdr)[2]))
66 >                return 0;
67 >        if ((hdr->nColors < 0 | hdr->impColors < 0))
68 >                return 0;
69 >        if (hdr->impColors > hdr->nColors)
70 >                return 0;
71 >        if (hdr->nColors > BMPpalLen(hdr))
72 >                return 0;
73 >        return 1;
74   }
75  
76 +                                        /* compute uncompressed scan size */
77 + #define getScanSiz(h)   ( (((h)->bpp*(h)->width+7 >>3) + 3) & ~03 )
78 +
79                                          /* get next byte from reader */
80   #define rdbyte(c,br)    ((br)->fpos += (c=(*(br)->cget)((br)->c_data))!=EOF, c)
81  
# Line 130 | Line 162 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
162          (void)rdint32(br);                      /* file size */
163          (void)rdint32(br);                      /* reserved word */
164          bmPos = rdint32(br);                    /* offset to bitmap */
165 <        hdrSiz = rdint32(br) + 14;              /* header size */
165 >        hdrSiz = 2 + 3*4 + rdint32(br);         /* header size */
166          if (hdrSiz < 2 + 6*4 + 2*2 + 6*4)
167                  goto err;
168          br->hdr->width = rdint32(br);           /* bitmap width */
# Line 142 | Line 174 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
174          if (rduint16(br) != 1)                  /* number of planes */
175                  goto err;
176          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;
177          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 */
178          (void)rdint32(br);                      /* bitmap size */
179          br->hdr->hRes = rdint32(br);            /* horizontal resolution */
180          br->hdr->vRes = rdint32(br);            /* vertical resolution */
# Line 156 | Line 182 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
182          br->hdr->impColors = rdint32(br);       /* # important colors */
183          if (br->hdr->impColors < 0)
184                  goto err;                       /* catch premature EOF */
185 <        palLen = 0;
186 <        if (br->hdr->bpp <= 8) {
187 <                palLen = 1 << br->hdr->bpp;
185 >        if (!BMPheaderOK(br->hdr))
186 >                goto err;
187 >        palLen = BMPpalLen(br->hdr);
188 >        if (br->hdr->bpp <= 8) {                /* normalize color counts */
189                  if (br->hdr->nColors <= 0)
190                          br->hdr->nColors = palLen;
164                else if (br->hdr->nColors > palLen)
165                        goto err;
191                  if (br->hdr->impColors <= 0)
192                          br->hdr->impColors = br->hdr->nColors;
193 <                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;
193 >        }
194                                                  /* extend header */
195          if (bmPos < hdrSiz + sizeof(RGBquad)*palLen)
196                  goto err;
# Line 176 | Line 198 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
198          if (palLen > 0 || br->hdr->infoSiz > 0) {
199                  br->hdr = (BMPHeader *)realloc((void *)br->hdr,
200                                          sizeof(BMPHeader) +
201 <                                        sizeof(RGBquad)*palLen -
180 <                                        sizeof(br->hdr->palette) +
201 >                                        sizeof(RGBquad)*palLen +
202                                          br->hdr->infoSiz);
203                  if (br->hdr == NULL)
204                          goto err;
205          }
206 <                                                /* read color palette */
207 <        if (rdbytes((char *)br->hdr->palette,
206 >                                                /* read colors or fields */
207 >        if (br->hdr->compr == BI_BITFIELDS) {
208 >                BMPbitField(br->hdr)[0] = (uint32)rdint32(br);
209 >                BMPbitField(br->hdr)[1] = (uint32)rdint32(br);
210 >                BMPbitField(br->hdr)[2] = (uint32)rdint32(br);
211 >        } else if (rdbytes((char *)br->hdr->palette,
212                          sizeof(RGBquad)*palLen, br) != BIR_OK)
213                  goto err;
214                                                  /* read add'l information */
# Line 194 | Line 219 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
219          if (br->scanline == NULL)
220                  goto err;
221          br->yscan = -1;
222 <        if (seek != NULL && br->hdr->compr != BI_UNCOMPR) {
222 >        if (seek != NULL && (br->hdr->compr == BI_RLE8 |
223 >                                        br->hdr->compr == BI_RLE4)) {
224                  BMPReader       *newbr = (BMPReader *)realloc((void *)br,
225                                                  sizeof(BMPReader) +
226                                                  sizeof(br->scanpos[0]) *
# Line 239 | Line 265 | BMPisGrayscale(const BMPHeader *hdr)
265   int
266   BMPreadScanline(BMPReader *br)
267   {
268 +        int     n;
269 +        int8    *sp;
270 +
271          if (br->yscan + 1 >= br->hdr->height)
272                  return BIR_EOF;
273          br->yscan++;                    /* prepare to read */
274 <        if (br->hdr->compr == BI_UNCOMPR)
275 <                return rdbytes((char *)br->scanline, getScanSiz(br->hdr), br);
276 < /* XXX need to perform actual decompression */
277 <        return BIR_UNSUPPORTED;
278 <        if (br->seek != NULL)
274 >        n = getScanSiz(br->hdr);        /* reading uncompressed data? */
275 >        if (br->hdr->compr == BI_UNCOMPR || br->hdr->compr == BI_BITFIELDS)
276 >                return rdbytes((char *)br->scanline, n, br);
277 >        /*
278 >         * RLE/RLE8 Decoding
279 >         *
280 >         * Certain aspects of this scheme are completely insane, so
281 >         * we don't support them.  Fortunately, they rarely appear.
282 >         * One is the mid-file EOD (0x0001) and another is the insane
283 >         * "delta" (0x0002), which is like a "goto" statement for bitmaps.
284 >         * Whoever thought this up should be shot, then told why
285 >         * it's impossible to support in any reasonable way.
286 >         * Also, RLE4 mode allows runs to stop halfway through a byte,
287 >         * which is likewise uncodeable, so we don't even try.
288 >         * Finally, the scanline break is ambiguous -- we assume here that
289 >         * it is required at the end of each scanline, though I haven't
290 >         * found anywhere this is written.  Otherwise, we would read to
291 >         * the end of the scanline, assuming the next bit of data belongs
292 >         * the following scan.  If a break follows the last pixel, as it
293 >         * seems to in the files I've tested out of Photoshop, you end up
294 >         * painting every other line black.  BTW, I assume any skipped
295 >         * pixels are painted with color 0, which is often black.  Nowhere
296 >         * is it specified what we should assume for missing pixels.  This
297 >         * is undoubtedly the most brain-dead format I've ever encountered.
298 >         */
299 >        sp = br->scanline;
300 >        while (n > 0) {
301 >                int     skipOdd, len, val;
302 >
303 >                if (rdbyte(len, br) == EOF)
304 >                        return BIR_TRUNCATED;
305 >                if (len > 0) {          /* got a run */
306 >                        if (br->hdr->compr == BI_RLE4) {
307 >                                if (len & 1)
308 >                                        return BIR_UNSUPPORTED;
309 >                                len >>= 1;
310 >                        }
311 >                        if (len > n)
312 >                                return BIR_RLERROR;
313 >                        if (rdbyte(val, br) == EOF)
314 >                                return BIR_TRUNCATED;
315 >                        n -= len;
316 >                        while (len--)
317 >                                *sp++ = val;
318 >                        continue;
319 >                }
320 >                switch (rdbyte(len, br)) {
321 >                case EOF:
322 >                        return BIR_TRUNCATED;
323 >                case 0:                 /* end of line */
324 >                        while (n--)
325 >                                *sp++ = 0;
326 >                        continue;
327 >                case 1:                 /* end of bitmap */
328 >                case 2:                 /* delta */
329 >                        return BIR_UNSUPPORTED;
330 >                }
331 >                                        /* absolute mode */
332 >                if (br->hdr->compr == BI_RLE4) {
333 >                        if (len & 1)
334 >                                return BIR_UNSUPPORTED;
335 >                        len >>= 1;
336 >                }
337 >                skipOdd = len & 1;
338 >                if (len > n)
339 >                        return BIR_RLERROR;
340 >                n -= len;
341 >                while (len--) {
342 >                        if (rdbyte(val, br) == EOF)
343 >                                return BIR_TRUNCATED;
344 >                        *sp++ = val;
345 >                }
346 >                if (skipOdd && rdbyte(val, br) == EOF)
347 >                        return BIR_TRUNCATED;
348 >        }
349 >                                        /* verify break at end of line */
350 >        if (rdbyte(n, br) != 0 || (rdbyte(n, br) != 0 &&
351 >                                (n != 1 || br->yscan != br->hdr->height-1)))
352 >                return BIR_RLERROR;
353 >        if (br->seek != NULL)           /* record next scanline position */
354                  br->scanpos[br->yscan + 1] = br->fpos;
355          return BIR_OK;
356   }
# Line 270 | Line 374 | BMPseekScanline(int y, BMPReader *br)
374          if (y != br->yscan + 1 && br->seek != NULL) {
375                  int     yseek;
376                  uint32  seekp;
377 <                if (br->hdr->compr == BI_UNCOMPR) {
377 >                if (br->hdr->compr == BI_UNCOMPR ||
378 >                                        br->hdr->compr == BI_BITFIELDS) {
379                          yseek = y;
380                          seekp = br->scanpos[0] + y*getScanSiz(br->hdr);
381                  } else {
# Line 295 | Line 400 | BMPseekScanline(int y, BMPReader *br)
400  
401   /* get ith pixel from last scanline */
402   RGBquad
403 < BMPdecodePixel(int i, BMPReader *br)
403 > BMPdecodePixel(int i, const BMPReader *br)
404   {
405 +        static const uint32     std16mask[3] = {0x7c00, 0x3e0, 0x1f};
406          static const RGBquad    black = {0, 0, 0, 0};
407 +        const uint32            *mask;
408 +        const uint8             *pp;
409 +        uint32                  pval, v;
410 +        RGBquad                 cval;
411          
412          if (((br == NULL) | (i < 0)) || i >= br->hdr->width)
413                  return black;
414  
415 +        cval.padding = 0;
416 +
417          switch (br->hdr->bpp) {
418 <        case 24: {
419 <                uint8   *bgr = br->scanline + 3*i;
420 <                RGBquad cval;
421 <                cval.b = bgr[0]; cval.g = bgr[1]; cval.r = bgr[2];
422 <                cval.padding = 0;
418 >        case 24:
419 >                pp = br->scanline + 3*i;
420 >                cval.b = *pp++;
421 >                cval.g = *pp++;
422 >                cval.r = *pp;
423                  return cval;
312                }
424          case 32:
425 <                return ((RGBquad *)br->scanline)[i];
425 >                if (br->hdr->compr == BI_UNCOMPR)
426 >                        return ((RGBquad *)br->scanline)[i];
427 >                                                /* convert bit fields */
428 >                pp = br->scanline + 4*i;
429 >                pval = *pp++;
430 >                pval |= *pp++ << 8;
431 >                pval |= *pp++ << 16;
432 >                pval |= *pp << 24;
433 >                mask = BMPbitField(br->hdr);
434 >                v = pval & mask[0];
435 >                while (v & ~0xff) v >>= 8;
436 >                cval.r = v;
437 >                v = pval & mask[1];
438 >                while (v & ~0xff) v >>= 8;
439 >                cval.g = v;
440 >                v = pval & mask[2];
441 >                while (v & ~0xff) v >>= 8;
442 >                cval.b = v;
443 >                return cval;
444          case 8:
445                  return br->hdr->palette[br->scanline[i]];
446          case 1:
447 <                return br->hdr->palette[br->scanline[i>>3]>>(i&7) & 1];
447 >                return br->hdr->palette[br->scanline[i>>3]>>(7-i&7) & 1];
448          case 4:
449 <                return br->hdr->palette[br->scanline[i>>1]>>((i&1)<<2) & 0xf];
449 >                return br->hdr->palette[br->scanline[i>>1]>>(i&1?4:0) & 0xf];
450 >        case 16:
451 >                pp = br->scanline + 2*i;
452 >                pval = *pp++;
453 >                pval |= *pp++ << 8;
454 >                mask = std16mask;
455 >                if (br->hdr->compr == BI_BITFIELDS)
456 >                        mask = BMPbitField(br->hdr);
457 >                cval.r = ((pval & mask[0]) << 8) / (mask[0] + 1);
458 >                cval.g = ((pval & mask[1]) << 8) / (mask[1] + 1);
459 >                cval.b = ((pval & mask[2]) << 8) / (mask[2] + 1);
460 >                return cval;
461          }
462          return black;                           /* should never happen */
463   }
# Line 382 | Line 522 | BMPtruecolorHeader(int xr, int yr, int infolen)
522          return hdr;
523   }
524  
525 < /* allocate color-mapped header (defaults to minimal grayscale) */
525 > /* allocate color-mapped header (defaults minimal grayscale) */
526   BMPHeader *
527   BMPmappedHeader(int xr, int yr, int infolen, int ncolors)
528   {
# Line 423 | Line 563 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
563  
564                                          /* put byte to writer */
565   #define wrbyte(c,bw)    ( (*(bw)->cput)(c,(bw)->c_data), \
566 <                                ++((bw)->fpos) > (bw)->flen ? \
566 >                                ++(bw)->fpos > (bw)->flen ? \
567                                          ((bw)->flen = (bw)->fpos) : \
568                                          (bw)->fpos )
569  
# Line 442 | Line 582 | wrint32(int32 i, BMPWriter *bw)
582          wrbyte(i& 0xff, bw);
583          wrbyte(i>>8 & 0xff, bw);
584          wrbyte(i>>16 & 0xff, bw);
585 <        wrbyte(i>>24  & 0xff, bw);
585 >        wrbyte(i>>24 & 0xff, bw);
586   }
587  
588   /* write 16-bit unsigned integer in littlendian order */
# Line 482 | Line 622 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
622          BMPWriter       *bw;
623          uint32          hdrSiz, palSiz, scanSiz, bmSiz;
624                                                  /* check arguments */
625 <        if (cput == NULL || hdr == NULL)
625 >        if (cput == NULL)
626                  return NULL;
627 <        if (hdr->width <= 0 || hdr->height <= 0)
627 >        if (!BMPheaderOK(hdr))
628                  return NULL;
629 <        if (hdr->compr != BI_UNCOMPR && hdr->compr != BI_RLE8)
629 >        if ((hdr->bpp == 16 | hdr->compr == BI_RLE4))
630                  return NULL;                    /* unsupported */
631 <        if (hdr->bpp > 8 && hdr->compr != BI_UNCOMPR)
631 >        if (seek == NULL && (hdr->compr == BI_RLE8 | hdr->compr == BI_RLE4))
632                  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;
633                                                  /* compute sizes */
634          hdrSiz = 2 + 6*4 + 2*2 + 6*4;
635 <        palSiz *= sizeof(RGBquad);
635 >        if (hdr->compr == BI_BITFIELDS)
636 >                hdrSiz += sizeof(uint32)*3;
637 >        palSiz = sizeof(RGBquad)*BMPpalLen(hdr);
638          scanSiz = getScanSiz(hdr);
639          bmSiz = hdr->height*scanSiz;            /* wrong if compressed */
640                                                  /* initialize writer */
# Line 547 | Line 684 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
684   #endif
685          return bw;
686   }
687 +
688 + /* find position of next run of 5 or more identical bytes, or 255 if none */
689 + static int
690 + findNextRun(const int8 *bp, int len)
691 + {
692 +        int     pos, cnt;
693 +                                                /* look for run */
694 +        for (pos = 0; len > 0 & pos < 255; pos++, bp++, len--) {
695 +                if (len < 5)                    /* no hope left? */
696 +                        continue;
697 +                cnt = 1;                        /* else let's try it */
698 +                while (bp[cnt] == bp[0])
699 +                        if (++cnt >= 5)         /* long enough */
700 +                                return pos;
701 +        }
702 +        return pos;                             /* didn't find any */
703 + }
704                                  
705   /* write the current scanline */
706   int
707   BMPwriteScanline(BMPWriter *bw)
708   {
709 +        const int8      *sp;
710 +        int             n;
711 +
712          if (bw->yscan >= bw->hdr->height)
713                  return BIR_EOF;
714                                                  /* writing uncompressed? */
715 <        if (bw->hdr->compr == BI_UNCOMPR) {
715 >        if (bw->hdr->compr == BI_UNCOMPR || bw->hdr->compr == BI_BITFIELDS) {
716                  uint32  scanSiz = getScanSiz(bw->hdr);
717                  uint32  slpos = bw->fbmp + bw->yscan*scanSiz;
718                  if (wrseek(slpos, bw) != BIR_OK)
# Line 564 | Line 721 | BMPwriteScanline(BMPWriter *bw)
721                  bw->yscan++;
722                  return BIR_OK;
723          }
724 <                                                /* else write compressed */
725 < /* XXX need to do actual compression */
726 <        return BIR_UNSUPPORTED;
727 <                                                /* write file length at end */
724 >        /*
725 >         * RLE8 Encoding
726 >         *
727 >         * See the notes in BMPreadScanline() on this encoding.  Needless
728 >         * to say, we avoid the nuttier aspects of this specification.
729 >         * We also assume that every scanline ends in a line break
730 >         * (0x0000) except for the last, which ends in a bitmap break
731 >         * (0x0001).  We don't support RLE4 at all; it's too awkward.
732 >         */
733 >        sp = bw->scanline;
734 >        n = bw->hdr->width;
735 >        while (n > 0) {
736 >                int     cnt, val;
737 >
738 >                cnt = findNextRun(sp, n);       /* 0-255 < n */
739 >                if (cnt >= 3) {                 /* output non-run */
740 >                        int     skipOdd = cnt & 1;
741 >                        wrbyte(0, bw);
742 >                        wrbyte(cnt, bw);
743 >                        n -= cnt;
744 >                        while (cnt--)
745 >                                wrbyte(*sp++, bw);
746 >                        if (skipOdd)
747 >                                wrbyte(0, bw);
748 >                }
749 >                if (n <= 0)                     /* was that it? */
750 >                        break;
751 >                val = *sp;                      /* output run */
752 >                for (cnt = 1; --n > 0 & *++sp == val; cnt++)
753 >                        ;
754 >                wrbyte(cnt, bw);
755 >                wrbyte(val, bw);
756 >        }
757 >        bw->yscan++;                            /* write file length at end */
758          if (bw->yscan == bw->hdr->height) {
759 +                wrbyte(0, bw); wrbyte(1, bw);   /* end of bitmap marker */
760                  if (bw->seek == NULL || (*bw->seek)(2, bw->c_data) != 0)
761                          return BIR_SEEKERR;
762                  bw->fpos = 2;
763 <                wrint32(bw->flen, bw);
763 >                wrint32(bw->flen, bw);          /* corrected file length */
764 >        } else {
765 >                wrbyte(0, bw); wrbyte(0, bw);   /* end of line marker */
766          }
767          return BIR_OK;
768   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines