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.1 by greg, Fri Mar 26 03:11:50 2004 UTC vs.
Revision 2.21 by greg, Mon Oct 4 23:04:59 2021 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>
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 25 | Line 30 | BMPerrorMessage(int ec)
30                  return "Truncated BMP image";
31          case BIR_UNSUPPORTED:
32                  return "Unsupported BMP feature";
33 +        case BIR_RLERROR:
34 +                return "BMP runlength encoding error";
35          case BIR_SEEKERR:
36                  return "BMP seek error";
37          }
38          return "Unknown BMP error";
39   }
40  
41 < /* compute uncompressed scanline size */
42 < static uint32
43 < getScanSiz(BMPHeader *hdr)
41 > /* Check that header is sensible */
42 > static int
43 > BMPheaderOK(const BMPHeader *hdr)
44   {
45 <        uint32  scanSiz;
46 <                                                /* bytes per scanline */
47 <        scanSiz = (hdr->bpp*hdr->width + 7) >> 3;
48 <        if (scanSiz & 3)                        /* 32-bit word boundary */
49 <                scanSiz += 4 - (scanSiz & 3);
50 <
51 <        return scanSiz;
45 >        if (!hdr)
46 >                return 0;
47 >        if ((hdr->width <= 0) | (hdr->height <= 0))
48 >                return 0;
49 >        switch (hdr->bpp) {             /* check compression */
50 >        case 1:
51 >        case 24:
52 >                if (hdr->compr != BI_UNCOMPR)
53 >                        return 0;
54 >                break;
55 >        case 16:
56 >        case 32:
57 >                if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_BITFIELDS))
58 >                        return 0;
59 >                break;
60 >        case 4:
61 >                if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE4))
62 >                        return 0;
63 >                break;
64 >        case 8:
65 >                if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE8))
66 >                        return 0;
67 >                break;
68 >        default:
69 >                return 0;
70 >        }
71 >        if (hdr->compr == BI_BITFIELDS && (BMPbitField(hdr)[0] &
72 >                                BMPbitField(hdr)[1] & BMPbitField(hdr)[2]))
73 >                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  
86 +                                        /* compute uncompressed scan size */
87 + #define getScanSiz(h)   ( ((((h)->bpp*(h)->width+7) >>3) + 3) & ~03 )
88 +
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 61 | 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 75 | 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 87 | 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 99 | 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 130 | Line 173 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
173          (void)rdint32(br);                      /* file size */
174          (void)rdint32(br);                      /* reserved word */
175          bmPos = rdint32(br);                    /* offset to bitmap */
176 <        hdrSiz = rdint32(br) + 14;              /* header size */
176 >        hdrSiz = 2 + 3*4 + rdint32(br);         /* header size */
177          if (hdrSiz < 2 + 6*4 + 2*2 + 6*4)
178                  goto err;
179          br->hdr->width = rdint32(br);           /* bitmap width */
180          br->hdr->height = rdint32(br);          /* bitmap height */
181 <        if ((br->hdr->width <= 0 | br->hdr->height == 0))
181 >        if (((br->hdr->width <= 0) | (br->hdr->height == 0)))
182                  goto err;
183          if ((br->hdr->yIsDown = br->hdr->height < 0))
184                  br->hdr->height = -br->hdr->height;
185          if (rduint16(br) != 1)                  /* number of planes */
186                  goto err;
187          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;
188          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 */
189          (void)rdint32(br);                      /* bitmap size */
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 <        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)
198 >        if (!BMPheaderOK(br->hdr))
199                  goto err;
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) {
206 >        br->hdr->infoSiz = bmPos - (hdrSiz + palSiz);
207 >        if (br->hdr->nColors > 0 || br->hdr->infoSiz > 0) {
208                  br->hdr = (BMPHeader *)realloc((void *)br->hdr,
209                                          sizeof(BMPHeader) +
210 <                                        sizeof(RGBquad)*palLen -
180 <                                        sizeof(br->hdr->palette) +
181 <                                        br->hdr->infoSiz);
210 >                                        palSiz + br->hdr->infoSiz);
211                  if (br->hdr == NULL)
212                          goto err;
213          }
214 <                                                /* read color palette */
215 <        if (rdbytes((char *)br->hdr->palette,
216 <                        sizeof(RGBquad)*palLen, br) != BIR_OK)
214 >                                                /* read colors or fields */
215 >        if (br->hdr->compr == BI_BITFIELDS) {
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, 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 194 | Line 226 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
226          if (br->scanline == NULL)
227                  goto err;
228          br->yscan = -1;
229 <        if (seek != NULL && br->hdr->compr != BI_UNCOMPR) {
229 >        if (seek != NULL && ((br->hdr->compr == BI_RLE8) |
230 >                                        (br->hdr->compr == BI_RLE4))) {
231                  BMPReader       *newbr = (BMPReader *)realloc((void *)br,
232                                                  sizeof(BMPReader) +
233                                                  sizeof(br->scanpos[0]) *
# Line 206 | Line 239 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
239                                  sizeof(br->scanpos[0])*br->hdr->height);
240          }
241          br->scanpos[0] = br->fpos;
242 <        if (BMPreadScanline(br) != BIR_OK)
243 <                goto err;
211 <        return br;
242 >        if (BMPreadScanline(br) == BIR_OK)
243 >                return br;
244   err:
245          if (br->hdr != NULL)
246                  free((void *)br->hdr);
# Line 218 | Line 250 | err:
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 230 | 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   {
274 +        int     n;
275 +        int8    *sp;
276 +
277          if (br->yscan + 1 >= br->hdr->height)
278                  return BIR_EOF;
279          br->yscan++;                    /* prepare to read */
280 <        if (br->hdr->compr == BI_UNCOMPR)
281 <                return rdbytes((char *)br->scanline, getScanSiz(br->hdr), br);
282 < /* XXX need to perform actual decompression */
283 <        return BIR_UNSUPPORTED;
284 <        if (br->seek != NULL)
280 >        n = getScanSiz(br->hdr);        /* reading uncompressed data? */
281 >        if (br->hdr->compr == BI_UNCOMPR || br->hdr->compr == BI_BITFIELDS)
282 >                return rdbytes((char *)br->scanline, n, br);
283 >        /*
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 ill-conceived
289 >         * "delta" (0x0002), which is like a "goto" statement for bitmaps.
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
295 >         * it is required at the end of each scanline, though I haven't
296 >         * found anywhere this is written.  Otherwise, we would read to
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.  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 = (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 >
312 >                if (rdbyte(len, br) == EOF)
313 >                        return BIR_TRUNCATED;
314 >                if (len > 0) {          /* got a run */
315 >                        if (br->hdr->compr == BI_RLE4) {
316 >                                if (len & 1)
317 >                                        return BIR_UNSUPPORTED;
318 >                                len >>= 1;
319 >                        }
320 >                        if (len > n)
321 >                                return BIR_RLERROR;
322 >                        if (rdbyte(val, br) == EOF)
323 >                                return BIR_TRUNCATED;
324 >                        n -= len;
325 >                        while (len--)
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 */
340 >                        return BIR_UNSUPPORTED;
341 >                }
342 >                                        /* absolute mode */
343 >                if (br->hdr->compr == BI_RLE4) {
344 >                        if (len & 1)
345 >                                return BIR_UNSUPPORTED;
346 >                        len >>= 1;
347 >                }
348 >                skipOdd = len & 1;
349 >                if (len > n)
350 >                        return BIR_RLERROR;
351 >                n -= len;
352 >                while (len--) {
353 >                        if (rdbyte(val, br) == EOF)
354 >                                return BIR_TRUNCATED;
355 >                        *sp++ = val;
356 >                }
357 >                if (skipOdd && rdbyte(val, br) == EOF)
358 >                        return BIR_TRUNCATED;
359 >        }
360 >                                        /* verify break at end of line */
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 270 | Line 385 | BMPseekScanline(int y, BMPReader *br)
385          if (y != br->yscan + 1 && br->seek != NULL) {
386                  int     yseek;
387                  uint32  seekp;
388 <                if (br->hdr->compr == BI_UNCOMPR) {
388 >                if (br->hdr->compr == BI_UNCOMPR ||
389 >                                        br->hdr->compr == BI_BITFIELDS) {
390                          yseek = y;
391                          seekp = br->scanpos[0] + y*getScanSiz(br->hdr);
392                  } else {
# Line 293 | 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, BMPReader *br)
414 > BMPdecodePixel(int i, const BMPReader *br)
415   {
416 +        static const uint32     std16mask[3] = {0x7c00, 0x3e0, 0x1f};
417          static const RGBquad    black = {0, 0, 0, 0};
418 +        const uint32            *mask;
419 +        const uint8             *pp;
420 +        uint32                  pval, v;
421 +        RGBquad                 cval;
422          
423 <        if ((br == NULL | i < 0) || i >= br->hdr->width)
423 >        if (((br == NULL) | (i < 0)) || i >= br->hdr->width)
424                  return black;
425  
426 +        cval.padding = 0;
427 +
428          switch (br->hdr->bpp) {
429 <        case 24: {
430 <                uint8   *bgr = br->scanline + 3*i;
431 <                RGBquad cval;
432 <                cval.b = bgr[0]; cval.g = bgr[1]; cval.r = bgr[2];
433 <                cval.padding = 0;
429 >        case 24:
430 >                pp = br->scanline + 3*i;
431 >                cval.b = *pp++;
432 >                cval.g = *pp++;
433 >                cval.r = *pp;
434                  return cval;
312                }
435          case 32:
436 <                return ((RGBquad *)br->scanline)[i];
436 >                if (br->hdr->compr == BI_UNCOMPR)
437 >                        return ((RGBquad *)br->scanline)[i];
438 >                                                /* convert bit fields */
439 >                pp = br->scanline + 4*i;
440 >                pval = *pp++;
441 >                pval |= *pp++ << 8;
442 >                pval |= *pp++ << 16;
443 >                pval |= *pp << 24;
444 >                mask = BMPbitField(br->hdr);
445 >                v = pval & mask[0];
446 >                while (v & ~0xff) v >>= 8;
447 >                cval.r = v;
448 >                v = pval & mask[1];
449 >                while (v & ~0xff) v >>= 8;
450 >                cval.g = v;
451 >                v = pval & mask[2];
452 >                while (v & ~0xff) v >>= 8;
453 >                cval.b = v;
454 >                return cval;
455          case 8:
456                  return br->hdr->palette[br->scanline[i]];
457          case 1:
458 <                return br->hdr->palette[br->scanline[i>>3]>>(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)<<2) & 0xf];
460 >                return br->hdr->palette[br->scanline[i>>1]>>(i&1?4:0) & 0xf];
461 >        case 16:
462 >                pp = br->scanline + 2*i;
463 >                pval = *pp++;
464 >                pval |= *pp++ << 8;
465 >                mask = std16mask;
466 >                if (br->hdr->compr == BI_BITFIELDS)
467 >                        mask = BMPbitField(br->hdr);
468 >                cval.r = ((pval & mask[0]) << 8) / (mask[0] + 1);
469 >                cval.g = ((pval & mask[1]) << 8) / (mask[1] + 1);
470 >                cval.b = ((pval & mask[2]) << 8) / (mask[2] + 1);
471 >                return cval;
472          }
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   {
# Line 359 | 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 382 | Line 533 | BMPtruecolorHeader(int xr, int yr, int infolen)
533          return hdr;
534   }
535  
536 < /* allocate color-mapped header (defaults to minimal grayscale) */
536 > /* Allocate color-mapped header (defaults to minimal grayscale) */
537   BMPHeader *
538   BMPmappedHeader(int xr, int yr, int infolen, int ncolors)
539   {
# Line 400 | Line 551 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
551          else
552                  return NULL;
553          hdr = (BMPHeader *)malloc(sizeof(BMPHeader) +
554 <                                        sizeof(RGBquad)*(1<<n) -
554 >                                        sizeof(RGBquad)*((size_t)1<<n) -
555                                          sizeof(hdr->palette) +
556                                          infolen);
557          if (hdr == NULL)
# Line 409 | Line 560 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
560          hdr->height = yr;
561          hdr->yIsDown = 0;                       /* default to upwards order */
562          hdr->bpp = n;
563 <        hdr->compr = BI_UNCOMPR;
563 >        hdr->compr = BI_UNCOMPR;                /* compression needs seek */
564          hdr->hRes = hdr->vRes = 2835;           /* default to 72 ppi */
565          hdr->nColors = ncolors;
566          hdr->impColors = 0;                     /* says all colors important */
567          hdr->infoSiz = infolen;
568 <        memset((void *)hdr->palette, 0, sizeof(RGBquad)*(1<<n) + infolen);
568 >        memset((void *)hdr->palette, 0, sizeof(RGBquad)*((size_t)1<<n) + infolen);
569          for (n = ncolors; n--; )
570                  hdr->palette[n].r = hdr->palette[n].g =
571                          hdr->palette[n].b = n*255/(ncolors-1);
# Line 423 | Line 574 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
574  
575                                          /* put byte to writer */
576   #define wrbyte(c,bw)    ( (*(bw)->cput)(c,(bw)->c_data), \
577 <                                ++((bw)->fpos) > (bw)->flen ? \
577 >                                ++(bw)->fpos > (bw)->flen ? \
578                                          ((bw)->flen = (bw)->fpos) : \
579                                          (bw)->fpos )
580  
581 < /* write out a string of bytes */
581 > /* Write out a string of bytes */
582   static void
583   wrbytes(char *bp, uint32 n, BMPWriter *bw)
584   {
# Line 435 | Line 586 | wrbytes(char *bp, uint32 n, BMPWriter *bw)
586                  wrbyte(*bp++, bw);
587   }
588  
589 < /* write 32-bit integer in littlendian order */
589 > /* Write 32-bit integer in littlendian order */
590   static void
591   wrint32(int32 i, BMPWriter *bw)
592   {
593          wrbyte(i& 0xff, bw);
594          wrbyte(i>>8 & 0xff, bw);
595          wrbyte(i>>16 & 0xff, bw);
596 <        wrbyte(i>>24  & 0xff, bw);
596 >        wrbyte(i>>24 & 0xff, bw);
597   }
598  
599 < /* write 16-bit unsigned integer in littlendian order */
599 > /* Write 16-bit unsigned integer in littlendian order */
600   static void
601   wruint16(uint16 ui, BMPWriter *bw)
602   {
# Line 453 | Line 604 | wruint16(uint16 ui, BMPWriter *bw)
604          wrbyte(ui>>8 & 0xff, bw);
605   }
606  
607 < /* seek to the specified file position, returning 0 (BIR_OK) on success */
607 > /* Seek to the specified file position, returning 0 (BIR_OK) on success */
608   static int
609   wrseek(uint32 pos, BMPWriter *bw)
610   {
611          if (pos == bw->fpos)
612                  return BIR_OK;
613 <        if (bw->seek == NULL) {
614 <                if (pos < bw->fpos)
464 <                        return BIR_SEEKERR;
465 <                while (bw->fpos < pos)
466 <                        wrbyte(0, bw);
467 <                return BIR_OK;
468 <        }
613 >        if (bw->seek == NULL)
614 >                return BIR_SEEKERR;
615          if ((*bw->seek)(pos, bw->c_data) != 0)
616                  return BIR_SEEKERR;
617          bw->fpos = pos;
# Line 474 | Line 620 | wrseek(uint32 pos, BMPWriter *bw)
620          return BIR_OK;
621   }
622  
623 < /* open BMP stream for writing */
623 > /* Open BMP stream for writing */
624   BMPWriter *
625   BMPopenWriter(void (*cput)(int, void *), int (*seek)(uint32, void *),
626                          void *c_data, BMPHeader *hdr)
# Line 482 | Line 628 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
628          BMPWriter       *bw;
629          uint32          hdrSiz, palSiz, scanSiz, bmSiz;
630                                                  /* check arguments */
631 <        if (cput == NULL || hdr == NULL)
631 >        if (cput == NULL)
632                  return NULL;
633 <        if (hdr->width <= 0 || hdr->height <= 0)
633 >        if (cput == &stdio_putc && c_data == NULL)
634 >                return NULL;                    /* stdio error condition */
635 >        if (!BMPheaderOK(hdr))
636                  return NULL;
637 <        if (hdr->compr != BI_UNCOMPR && hdr->compr != BI_RLE8)
637 >        if ((hdr->bpp == 16) | (hdr->compr == BI_RLE4))
638                  return NULL;                    /* unsupported */
639 <        if (hdr->bpp > 8 && hdr->compr != BI_UNCOMPR)
639 > /* no seek means we may have the wrong file length, but most app's don't care
640 >        if (seek == NULL && ((hdr->compr == BI_RLE8) | (hdr->compr == BI_RLE4)))
641                  return NULL;
642 <        palSiz = BMPpalLen(hdr);
494 <        if (hdr->nColors > palSiz)
495 <                return NULL;
496 <        if (hdr->compr != BI_UNCOMPR && seek == NULL)
497 <                return NULL;
642 > */
643                                                  /* compute sizes */
644          hdrSiz = 2 + 6*4 + 2*2 + 6*4;
645 <        palSiz *= sizeof(RGBquad);
645 >        if (hdr->compr == BI_BITFIELDS)
646 >                hdrSiz += sizeof(uint32)*3;
647 >        palSiz = sizeof(RGBquad)*hdr->nColors;
648          scanSiz = getScanSiz(hdr);
649          bmSiz = hdr->height*scanSiz;            /* wrong if compressed */
650                                                  /* initialize writer */
# Line 547 | Line 694 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
694   #endif
695          return bw;
696   }
697 +
698 + /* Find position of next run of 5 or more identical bytes, or 255 if none */
699 + static int
700 + findNextRun(const int8 *bp, int len)
701 + {
702 +        int     pos, cnt;
703 +                                                /* look for run */
704 +        for (pos = 0; (len > 0) & (pos < 255); pos++, bp++, len--) {
705 +                if (len < 5)                    /* no hope left? */
706 +                        continue;
707 +                cnt = 1;                        /* else let's try it */
708 +                while (bp[cnt] == bp[0])
709 +                        if (++cnt >= 5)
710 +                                return pos;     /* long enough */
711 +        }
712 +        return pos;                             /* didn't find any */
713 + }
714                                  
715 < /* write the current scanline */
715 > /* Write the current scanline */
716   int
717   BMPwriteScanline(BMPWriter *bw)
718   {
719 +        const int8      *sp;
720 +        int             n;
721 +
722          if (bw->yscan >= bw->hdr->height)
723                  return BIR_EOF;
724                                                  /* writing uncompressed? */
725 <        if (bw->hdr->compr == BI_UNCOMPR) {
725 >        if (bw->hdr->compr == BI_UNCOMPR || bw->hdr->compr == BI_BITFIELDS) {
726                  uint32  scanSiz = getScanSiz(bw->hdr);
727                  uint32  slpos = bw->fbmp + bw->yscan*scanSiz;
728                  if (wrseek(slpos, bw) != BIR_OK)
# Line 564 | Line 731 | BMPwriteScanline(BMPWriter *bw)
731                  bw->yscan++;
732                  return BIR_OK;
733          }
734 <                                                /* else write compressed */
735 < /* XXX need to do actual compression */
736 <        return BIR_UNSUPPORTED;
737 <                                                /* write file length at end */
734 >        /*
735 >         * RLE8 Encoding
736 >         *
737 >         * See the notes in BMPreadScanline() on this encoding.  Needless
738 >         * to say, we avoid the nuttier aspects of this specification.
739 >         * We also assume that every scanline ends in a line break
740 >         * (0x0000) except for the last, which ends in a bitmap break
741 >         * (0x0001).  We don't support RLE4 at all; it's too awkward.
742 >         */
743 >        sp = (const int8 *)bw->scanline;
744 >        n = bw->hdr->width;
745 >        while (n > 0) {
746 >                int     cnt, val;
747 >                cnt = findNextRun(sp, n);       /* 0-255 < n */
748 >                if (cnt >= 3) {                 /* output absolute */
749 >                        int     skipOdd = cnt & 1;
750 >                        wrbyte(0, bw);
751 >                        wrbyte(cnt, bw);
752 >                        n -= cnt;
753 >                        while (cnt--)
754 >                                wrbyte(*sp++, bw);
755 >                        if (skipOdd)
756 >                                wrbyte(0, bw);
757 >                }
758 >                if (n <= 0)                     /* was that it? */
759 >                        break;
760 >                val = *sp++;                    /* output run */
761 >                for (cnt = 1; --n && cnt < 255; cnt++, sp++)
762 >                        if (*sp != val)
763 >                                break;
764 >                wrbyte(cnt, bw);
765 >                wrbyte(val, bw);
766 >        }
767 >        bw->yscan++;                            /* write line break or EOD */
768          if (bw->yscan == bw->hdr->height) {
769 <                if (bw->seek == NULL || (*bw->seek)(2, bw->c_data) != 0)
770 <                        return BIR_SEEKERR;
771 <                bw->fpos = 2;
772 <                wrint32(bw->flen, bw);
769 >                wrbyte(0, bw); wrbyte(1, bw);   /* end of bitmap marker */
770 >                if (wrseek(2, bw) != BIR_OK)
771 >                        return BIR_OK;          /* no one may care */
772 >                wrint32(bw->flen, bw);          /* correct file length */
773 >                if (wrseek(34, bw) != BIR_OK)
774 >                        return BIR_OK;
775 >                wrint32(bw->flen-bw->fbmp, bw); /* correct bitmap length */
776 >        } else {
777 >                wrbyte(0, bw); wrbyte(0, bw);   /* end of line marker */
778          }
779          return BIR_OK;
780   }
781  
782 < /* free BMP writer resources */
782 > /* Free BMP writer resources */
783   void
784   BMPfreeWriter(BMPWriter *bw)
785   {

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines