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

Comparing ray/src/common/bmpfile.c (file contents):
Revision 2.4 by schorsch, Fri Mar 26 23:30:05 2004 UTC vs.
Revision 2.22 by greg, Fri Sep 12 19:31:15 2025 UTC

# Line 10 | Line 10 | static const char RCSid[] = "$Id$";
10   #include <string.h>
11   #include "bmpfile.h"
12  
13 < /* get corresponding error message */
13 > #ifdef getc_unlocked            /* avoid horrendous overhead of flockfile */
14 > #undef getc
15 > #undef putc
16 > #define getc    getc_unlocked
17 > #define putc    putc_unlocked
18 > #endif
19 >
20 > /* Get corresponding error message */
21   const char *
22   BMPerrorMessage(int ec)
23   {
# Line 31 | Line 38 | BMPerrorMessage(int ec)
38          return "Unknown BMP error";
39   }
40  
41 < /* check than header is sensible */
41 > /* Check that header is sensible */
42   static int
43   BMPheaderOK(const BMPHeader *hdr)
44   {
# Line 64 | Line 71 | BMPheaderOK(const BMPHeader *hdr)
71          if (hdr->compr == BI_BITFIELDS && (BMPbitField(hdr)[0] &
72                                  BMPbitField(hdr)[1] & BMPbitField(hdr)[2]))
73                  return 0;
74 <        if ((hdr->nColors < 0) | (hdr->impColors < 0))
75 <                return 0;
76 <        if (hdr->impColors > hdr->nColors)
77 <                return 0;
78 <        if (hdr->nColors > BMPpalLen(hdr))
79 <                return 0;
74 >        if (hdr->bpp > 8) {
75 >                if (hdr->nColors != 0)
76 >                        return 0;
77 >        } else {
78 >                if ((hdr->nColors < 0) | (hdr->nColors > 1<<hdr->bpp))
79 >                        return 0;
80 >                if ((hdr->impColors < 0) | (hdr->impColors > hdr->nColors))
81 >                        return 0;
82 >        }
83          return 1;
84   }
85  
# Line 79 | Line 89 | BMPheaderOK(const BMPHeader *hdr)
89                                          /* get next byte from reader */
90   #define rdbyte(c,br)    ((br)->fpos += (c=(*(br)->cget)((br)->c_data))!=EOF, c)
91  
92 < /* read n bytes */
92 > /* Read n bytes */
93   static int
94   rdbytes(char *bp, uint32 n, BMPReader *br)
95   {
# Line 93 | Line 103 | rdbytes(char *bp, uint32 n, BMPReader *br)
103          return BIR_OK;
104   }
105  
106 < /* read 32-bit integer in littlendian order */
106 > /* Read 32-bit integer in littlendian order */
107   static int32
108   rdint32(BMPReader *br)
109   {
# Line 107 | Line 117 | rdint32(BMPReader *br)
117          return i;                       /* -1 on EOF */
118   }
119  
120 < /* read 16-bit unsigned integer in littlendian order */
120 > /* Read 16-bit unsigned integer in littlendian order */
121   static int
122   rduint16(BMPReader *br)
123   {
# Line 119 | Line 129 | rduint16(BMPReader *br)
129          return i;                       /* -1 on EOF */
130   }
131  
132 < /* seek on reader or return 0 (BIR_OK) on success */
132 > /* Seek on reader or return 0 (BIR_OK) on success */
133   static int
134   rdseek(uint32 pos, BMPReader *br)
135   {
# Line 131 | Line 141 | rdseek(uint32 pos, BMPReader *br)
141          return BIR_OK;
142   }
143  
144 < /* open BMP stream for reading and get first scanline */
144 > /* Open BMP stream for reading and get first scanline */
145   BMPReader *
146   BMPopenReader(int (*cget)(void *), int (*seek)(uint32, void *), void *c_data)
147   {
148          BMPReader       *br;
149 <        uint32          bmPos, hdrSiz;
150 <        int             palLen;
149 >        uint32          bmPos, hdrSiz, palSiz;
150 >        int             magic[2];               /* check magic number */
151  
152          if (cget == NULL)
153                  return NULL;
154 <        int     magic[2];               /* check magic number */
154 >        if (cget == &stdio_getc && c_data == NULL)
155 >                return NULL;                    /* stdio error condition */
156          magic[0] = (*cget)(c_data);
157          if (magic[0] != 'B')
158                  return NULL;
# Line 179 | Line 190 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
190          br->hdr->hRes = rdint32(br);            /* horizontal resolution */
191          br->hdr->vRes = rdint32(br);            /* vertical resolution */
192          br->hdr->nColors = rdint32(br);         /* # colors used */
193 +        if (!br->hdr->nColors && br->hdr->bpp <= 8)
194 +                br->hdr->nColors = 1<<br->hdr->bpp;
195          br->hdr->impColors = rdint32(br);       /* # important colors */
196          if (br->hdr->impColors < 0)
197                  goto err;                       /* catch premature EOF */
198          if (!BMPheaderOK(br->hdr))
199                  goto err;
200 <        palLen = BMPpalLen(br->hdr);
201 <        if (br->hdr->bpp <= 8) {                /* normalize color counts */
202 <                if (br->hdr->nColors <= 0)
190 <                        br->hdr->nColors = palLen;
191 <                if (br->hdr->impColors <= 0)
192 <                        br->hdr->impColors = br->hdr->nColors;
193 <        }
200 >        palSiz = sizeof(RGBquad)*br->hdr->nColors;
201 >        if (br->hdr->impColors <= 0)
202 >                br->hdr->impColors = br->hdr->nColors;
203                                                  /* extend header */
204 <        if (bmPos < hdrSiz + sizeof(RGBquad)*palLen)
204 >        if (bmPos < hdrSiz + palSiz)
205                  goto err;
206 <        br->hdr->infoSiz = bmPos - (hdrSiz + sizeof(RGBquad)*palLen);
207 <        if (palLen > 0 || br->hdr->infoSiz > 0) {
208 <                br->hdr = (BMPHeader *)realloc((void *)br->hdr,
206 >        br->hdr->infoSiz = bmPos - (hdrSiz + palSiz);
207 >        if (br->hdr->nColors > 0 || br->hdr->infoSiz > 0) {
208 >                br->hdr = (BMPHeader *)realloc(br->hdr,
209                                          sizeof(BMPHeader) +
210 <                                        sizeof(RGBquad)*palLen +
202 <                                        br->hdr->infoSiz);
210 >                                        palSiz + br->hdr->infoSiz);
211                  if (br->hdr == NULL)
212                          goto err;
213          }
# Line 208 | Line 216 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
216                  BMPbitField(br->hdr)[0] = (uint32)rdint32(br);
217                  BMPbitField(br->hdr)[1] = (uint32)rdint32(br);
218                  BMPbitField(br->hdr)[2] = (uint32)rdint32(br);
219 <        } else if (rdbytes((char *)br->hdr->palette,
212 <                        sizeof(RGBquad)*palLen, br) != BIR_OK)
219 >        } else if (rdbytes((char *)br->hdr->palette, palSiz, br) != BIR_OK)
220                  goto err;
221                                                  /* read add'l information */
222          if (rdbytes(BMPinfo(br->hdr), br->hdr->infoSiz, br) != BIR_OK)
# Line 221 | Line 228 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
228          br->yscan = -1;
229          if (seek != NULL && ((br->hdr->compr == BI_RLE8) |
230                                          (br->hdr->compr == BI_RLE4))) {
231 <                BMPReader       *newbr = (BMPReader *)realloc((void *)br,
231 >                BMPReader       *newbr = (BMPReader *)realloc(br,
232                                                  sizeof(BMPReader) +
233                                                  sizeof(br->scanpos[0]) *
234                                                          br->hdr->height);
235                  if (newbr == NULL)
236                          goto err;
237                  br = newbr;
238 <                memset((void *)(br->scanpos + 1), 0,
238 >                memset(br->scanpos+1, 0,
239                                  sizeof(br->scanpos[0])*br->hdr->height);
240          }
241          br->scanpos[0] = br->fpos;
242 <        if (BMPreadScanline(br) != BIR_OK)
243 <                goto err;
237 <        return br;
242 >        if (BMPreadScanline(br) == BIR_OK)
243 >                return br;
244   err:
245          if (br->hdr != NULL)
246 <                free((void *)br->hdr);
246 >                free(br->hdr);
247          if (br->scanline != NULL)
248 <                free((void *)br->scanline);
249 <        free((void *)br);
248 >                free(br->scanline);
249 >        free(br);
250          return NULL;
251   }
252  
253 < /* determine if image is grayscale */
253 > /* Determine if image is grayscale */
254   int
255   BMPisGrayscale(const BMPHeader *hdr)
256   {
# Line 256 | Line 262 | BMPisGrayscale(const BMPHeader *hdr)
262          if (hdr->bpp > 8)               /* assume they had a reason for it */
263                  return 0;
264          for (rgbp = hdr->palette, n = hdr->impColors; n-- > 0; rgbp++)
265 <                if (((rgbp->r != rgbp->g) | (rgbp->g != rgbp->b)))
265 >                if ((rgbp->r != rgbp->g) | (rgbp->g != rgbp->b))
266                          return 0;
267          return 1;                       /* all colors neutral in map */
268   }
269  
270 < /* read and decode next BMP scanline */
270 > /* Read and decode next BMP scanline */
271   int
272   BMPreadScanline(BMPReader *br)
273   {
# Line 275 | Line 281 | BMPreadScanline(BMPReader *br)
281          if (br->hdr->compr == BI_UNCOMPR || br->hdr->compr == BI_BITFIELDS)
282                  return rdbytes((char *)br->scanline, n, br);
283          /*
284 <         * RLE/RLE8 Decoding
284 >         * RLE4/RLE8 Decoding
285           *
286           * Certain aspects of this scheme are completely insane, so
287           * we don't support them.  Fortunately, they rarely appear.
288 <         * One is the mid-file EOD (0x0001) and another is the insane
288 >         * One is the mid-file EOD (0x0001) and another is the ill-conceived
289           * "delta" (0x0002), which is like a "goto" statement for bitmaps.
290 <         * Whoever thought this up should be shot, then told why
291 <         * it's impossible to support in any reasonable way.
290 >         * Whoever thought this up should be wrestled to the ground and told
291 >         * why it's impossible to support such a scheme in any reasonable way.
292           * Also, RLE4 mode allows runs to stop halfway through a byte,
293           * which is likewise uncodeable, so we don't even try.
294           * Finally, the scanline break is ambiguous -- we assume here that
# Line 291 | Line 297 | BMPreadScanline(BMPReader *br)
297           * the end of the scanline, assuming the next bit of data belongs
298           * the following scan.  If a break follows the last pixel, as it
299           * seems to in the files I've tested out of Photoshop, you end up
300 <         * painting every other line black.  BTW, I assume any skipped
300 >         * painting every other line black.  Also, I assume any skipped
301           * pixels are painted with color 0, which is often black.  Nowhere
302           * is it specified what we should assume for missing pixels.  This
303           * is undoubtedly the most brain-dead format I've ever encountered.
304           */
305 <        sp = br->scanline;
305 >        sp = (int8 *)br->scanline;
306 >        n = br->hdr->width;
307 >        if (br->hdr->compr == BI_RLE4)
308 >                n = (n + 1) >> 1;
309          while (n > 0) {
310                  int     skipOdd, len, val;
311  
# Line 317 | Line 326 | BMPreadScanline(BMPReader *br)
326                                  *sp++ = val;
327                          continue;
328                  }
329 +                                        /* check for escape */
330                  switch (rdbyte(len, br)) {
331                  case EOF:
332                          return BIR_TRUNCATED;
333                  case 0:                 /* end of line */
334                          while (n--)
335                                  *sp++ = 0;
336 +                        /* leaves n == -1 as flag for test after loop */
337                          continue;
338                  case 1:                 /* end of bitmap */
339                  case 2:                 /* delta */
# Line 347 | Line 358 | BMPreadScanline(BMPReader *br)
358                          return BIR_TRUNCATED;
359          }
360                                          /* verify break at end of line */
361 <        if (rdbyte(n, br) != 0 || (rdbyte(n, br) != 0 &&
362 <                                (n != 1 || br->yscan != br->hdr->height-1)))
361 >        if (!n && (rdbyte(n, br) != 0 || (rdbyte(n, br) != 0 &&
362 >                                (n != 1 || br->yscan != br->hdr->height-1))))
363                  return BIR_RLERROR;
364          if (br->seek != NULL)           /* record next scanline position */
365                  br->scanpos[br->yscan + 1] = br->fpos;
366          return BIR_OK;
367   }
368  
369 < /* read a specific scanline */
369 > /* Read a specific scanline */
370   int
371   BMPseekScanline(int y, BMPReader *br)
372   {
# Line 398 | Line 409 | BMPseekScanline(int y, BMPReader *br)
409          return BIR_OK;
410   }
411  
412 < /* get ith pixel from last scanline */
412 > /* Get ith pixel from last scanline */
413   RGBquad
414   BMPdecodePixel(int i, const BMPReader *br)
415   {
# Line 444 | Line 455 | BMPdecodePixel(int i, const BMPReader *br)
455          case 8:
456                  return br->hdr->palette[br->scanline[i]];
457          case 1:
458 <                return br->hdr->palette[br->scanline[i>>3]>>((7-i)&7) & 1];
458 >                return br->hdr->palette[br->scanline[i>>3]>>(7-(i&7)) & 1];
459          case 4:
460                  return br->hdr->palette[br->scanline[i>>1]>>(i&1?4:0) & 0xf];
461          case 16:
# Line 462 | Line 473 | BMPdecodePixel(int i, const BMPReader *br)
473          return black;                           /* should never happen */
474   }
475  
476 < /* free BMP reader resources */
476 > /* Free BMP reader resources */
477   void
478   BMPfreeReader(BMPReader *br)
479   {
480          if (br == NULL)
481                  return;
482 <        free((void *)br->hdr);
483 <        free((void *)br->scanline);
484 <        free((void *)br);
482 >        free(br->hdr);
483 >        free(br->scanline);
484 >        free(br);
485   }
486  
487   /* stdio getc() callback */
# Line 499 | Line 510 | stdio_fseek(uint32 pos, void *p)
510          return fseek((FILE *)p, (long)pos, 0);
511   }
512  
513 < /* allocate uncompressed (24-bit) RGB header */
513 > /* Allocate uncompressed (24-bit) RGB header */
514   BMPHeader *
515   BMPtruecolorHeader(int xr, int yr, int infolen)
516   {
# Line 518 | Line 529 | BMPtruecolorHeader(int xr, int yr, int infolen)
529          hdr->compr = BI_UNCOMPR;
530          hdr->hRes = hdr->vRes = 2835;           /* default to 72 ppi */
531          hdr->nColors = hdr->impColors = 0;
532 <        hdr->infoSiz = infolen;
532 >        if ((hdr->infoSiz = infolen) > 0)
533 >                memset(BMPinfo(hdr), 0, hdr->infoSiz);
534          return hdr;
535   }
536  
537 < /* allocate color-mapped header (defaults minimal grayscale) */
537 > /* Allocate color-mapped header (defaults to minimal grayscale) */
538   BMPHeader *
539   BMPmappedHeader(int xr, int yr, int infolen, int ncolors)
540   {
# Line 540 | Line 552 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
552          else
553                  return NULL;
554          hdr = (BMPHeader *)malloc(sizeof(BMPHeader) +
555 <                                        sizeof(RGBquad)*(1<<n) -
555 >                                        sizeof(RGBquad)*((size_t)1<<n) -
556                                          sizeof(hdr->palette) +
557                                          infolen);
558          if (hdr == NULL)
# Line 549 | Line 561 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
561          hdr->height = yr;
562          hdr->yIsDown = 0;                       /* default to upwards order */
563          hdr->bpp = n;
564 <        hdr->compr = BI_UNCOMPR;
564 >        hdr->compr = BI_UNCOMPR;                /* compression needs seek */
565          hdr->hRes = hdr->vRes = 2835;           /* default to 72 ppi */
566          hdr->nColors = ncolors;
567          hdr->impColors = 0;                     /* says all colors important */
568          hdr->infoSiz = infolen;
569 <        memset((void *)hdr->palette, 0, sizeof(RGBquad)*(1<<n) + infolen);
569 >        memset(hdr->palette, 0, sizeof(RGBquad)*((size_t)1<<n) + infolen);
570          for (n = ncolors; n--; )
571                  hdr->palette[n].r = hdr->palette[n].g =
572                          hdr->palette[n].b = n*255/(ncolors-1);
# Line 567 | Line 579 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
579                                          ((bw)->flen = (bw)->fpos) : \
580                                          (bw)->fpos )
581  
582 < /* write out a string of bytes */
582 > /* Write out a string of bytes */
583   static void
584   wrbytes(char *bp, uint32 n, BMPWriter *bw)
585   {
# Line 575 | Line 587 | wrbytes(char *bp, uint32 n, BMPWriter *bw)
587                  wrbyte(*bp++, bw);
588   }
589  
590 < /* write 32-bit integer in littlendian order */
590 > /* Write 32-bit integer in littlendian order */
591   static void
592   wrint32(int32 i, BMPWriter *bw)
593   {
# Line 585 | Line 597 | wrint32(int32 i, BMPWriter *bw)
597          wrbyte(i>>24 & 0xff, bw);
598   }
599  
600 < /* write 16-bit unsigned integer in littlendian order */
600 > /* Write 16-bit unsigned integer in littlendian order */
601   static void
602   wruint16(uint16 ui, BMPWriter *bw)
603   {
# Line 593 | Line 605 | wruint16(uint16 ui, BMPWriter *bw)
605          wrbyte(ui>>8 & 0xff, bw);
606   }
607  
608 < /* seek to the specified file position, returning 0 (BIR_OK) on success */
608 > /* Seek to the specified file position, returning 0 (BIR_OK) on success */
609   static int
610   wrseek(uint32 pos, BMPWriter *bw)
611   {
612          if (pos == bw->fpos)
613                  return BIR_OK;
614 <        if (bw->seek == NULL) {
615 <                if (pos < bw->fpos)
604 <                        return BIR_SEEKERR;
605 <                while (bw->fpos < pos)
606 <                        wrbyte(0, bw);
607 <                return BIR_OK;
608 <        }
614 >        if (bw->seek == NULL)
615 >                return BIR_SEEKERR;
616          if ((*bw->seek)(pos, bw->c_data) != 0)
617                  return BIR_SEEKERR;
618          bw->fpos = pos;
# Line 614 | Line 621 | wrseek(uint32 pos, BMPWriter *bw)
621          return BIR_OK;
622   }
623  
624 < /* open BMP stream for writing */
624 > /* Open BMP stream for writing */
625   BMPWriter *
626   BMPopenWriter(void (*cput)(int, void *), int (*seek)(uint32, void *),
627                          void *c_data, BMPHeader *hdr)
# Line 624 | Line 631 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
631                                                  /* check arguments */
632          if (cput == NULL)
633                  return NULL;
634 +        if (cput == &stdio_putc && c_data == NULL)
635 +                return NULL;                    /* stdio error condition */
636          if (!BMPheaderOK(hdr))
637                  return NULL;
638          if ((hdr->bpp == 16) | (hdr->compr == BI_RLE4))
639                  return NULL;                    /* unsupported */
640 + /* no seek means we may have the wrong file length, but most app's don't care
641          if (seek == NULL && ((hdr->compr == BI_RLE8) | (hdr->compr == BI_RLE4)))
642                  return NULL;
643 + */
644                                                  /* compute sizes */
645          hdrSiz = 2 + 6*4 + 2*2 + 6*4;
646          if (hdr->compr == BI_BITFIELDS)
647                  hdrSiz += sizeof(uint32)*3;
648 <        palSiz = sizeof(RGBquad)*BMPpalLen(hdr);
648 >        palSiz = sizeof(RGBquad)*hdr->nColors;
649          scanSiz = getScanSiz(hdr);
650          bmSiz = hdr->height*scanSiz;            /* wrong if compressed */
651                                                  /* initialize writer */
# Line 645 | Line 656 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
656          bw->yscan = 0;
657          bw->scanline = (uint8 *)calloc(scanSiz, sizeof(uint8));
658          if (bw->scanline == NULL) {
659 <                free((void *)bw);
659 >                free(bw);
660                  return NULL;
661          }
662          bw->fbmp = hdrSiz + palSiz + hdr->infoSiz;
# Line 685 | Line 696 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
696          return bw;
697   }
698  
699 < /* find position of next run of 5 or more identical bytes, or 255 if none */
699 > /* Find position of next run of 5 or more identical bytes, or 255 if none */
700   static int
701   findNextRun(const int8 *bp, int len)
702   {
# Line 696 | Line 707 | findNextRun(const int8 *bp, int len)
707                          continue;
708                  cnt = 1;                        /* else let's try it */
709                  while (bp[cnt] == bp[0])
710 <                        if (++cnt >= 5)         /* long enough */
711 <                                return pos;
710 >                        if (++cnt >= 5)
711 >                                return pos;     /* long enough */
712          }
713          return pos;                             /* didn't find any */
714   }
715                                  
716 < /* write the current scanline */
716 > /* Write the current scanline */
717   int
718   BMPwriteScanline(BMPWriter *bw)
719   {
# Line 730 | Line 741 | BMPwriteScanline(BMPWriter *bw)
741           * (0x0000) except for the last, which ends in a bitmap break
742           * (0x0001).  We don't support RLE4 at all; it's too awkward.
743           */
744 <        sp = bw->scanline;
744 >        sp = (const int8 *)bw->scanline;
745          n = bw->hdr->width;
746          while (n > 0) {
747                  int     cnt, val;
737
748                  cnt = findNextRun(sp, n);       /* 0-255 < n */
749 <                if (cnt >= 3) {                 /* output non-run */
749 >                if (cnt >= 3) {                 /* output absolute */
750                          int     skipOdd = cnt & 1;
751                          wrbyte(0, bw);
752                          wrbyte(cnt, bw);
# Line 748 | Line 758 | BMPwriteScanline(BMPWriter *bw)
758                  }
759                  if (n <= 0)                     /* was that it? */
760                          break;
761 <                val = *sp;                      /* output run */
762 <                for (cnt = 1; (--n > 0) & (*++sp == val); cnt++)
763 <                        ;
761 >                val = *sp++;                    /* output run */
762 >                for (cnt = 1; --n && cnt < 255; cnt++, sp++)
763 >                        if (*sp != val)
764 >                                break;
765                  wrbyte(cnt, bw);
766                  wrbyte(val, bw);
767          }
768 <        bw->yscan++;                            /* write file length at end */
768 >        bw->yscan++;                            /* write line break or EOD */
769          if (bw->yscan == bw->hdr->height) {
770                  wrbyte(0, bw); wrbyte(1, bw);   /* end of bitmap marker */
771 <                if (bw->seek == NULL || (*bw->seek)(2, bw->c_data) != 0)
772 <                        return BIR_SEEKERR;
773 <                bw->fpos = 2;
774 <                wrint32(bw->flen, bw);          /* corrected file length */
771 >                if (wrseek(2, bw) != BIR_OK)
772 >                        return BIR_OK;          /* no one may care */
773 >                wrint32(bw->flen, bw);          /* correct file length */
774 >                if (wrseek(34, bw) != BIR_OK)
775 >                        return BIR_OK;
776 >                wrint32(bw->flen-bw->fbmp, bw); /* correct bitmap length */
777          } else {
778                  wrbyte(0, bw); wrbyte(0, bw);   /* end of line marker */
779          }
780          return BIR_OK;
781   }
782  
783 < /* free BMP writer resources */
783 > /* Free BMP writer resources */
784   void
785   BMPfreeWriter(BMPWriter *bw)
786   {
787          if (bw == NULL)
788                  return;
789 <        free((void *)bw->hdr);
790 <        free((void *)bw->scanline);
791 <        free((void *)bw);
789 >        free(bw->hdr);
790 >        free(bw->scanline);
791 >        free(bw);
792   }

Diff Legend

Removed lines
+ Added lines
< Changed lines (old)
> Changed lines (new)