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.10 by greg, Fri Apr 30 17:00:29 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>
11   #include "bmpfile.h"
12  
13 + #ifdef getc_unlocked            /* avoid horrendous overhead of flockfile */
14 + #define getc    getc_unlocked
15 + #define putc    putc_unlocked
16 + #endif
17 +
18   /* get corresponding error message */
19   const char *
20   BMPerrorMessage(int ec)
# Line 25 | Line 28 | BMPerrorMessage(int ec)
28                  return "Truncated BMP image";
29          case BIR_UNSUPPORTED:
30                  return "Unsupported BMP feature";
31 +        case BIR_RLERROR:
32 +                return "BMP runlength encoding error";
33          case BIR_SEEKERR:
34                  return "BMP seek error";
35          }
36          return "Unknown BMP error";
37   }
38  
39 < /* compute uncompressed scanline size */
40 < static uint32
41 < getScanSiz(BMPHeader *hdr)
39 > /* check than header is sensible */
40 > static int
41 > BMPheaderOK(const BMPHeader *hdr)
42   {
43 <        uint32  scanSiz;
44 <                                                /* bytes per scanline */
45 <        scanSiz = (hdr->bpp*hdr->width + 7) >> 3;
46 <        if (scanSiz & 3)                        /* 32-bit word boundary */
47 <                scanSiz += 4 - (scanSiz & 3);
48 <
49 <        return scanSiz;
43 >        if (!hdr)
44 >                return 0;
45 >        if ((hdr->width <= 0) | (hdr->height <= 0))
46 >                return 0;
47 >        switch (hdr->bpp) {             /* check compression */
48 >        case 1:
49 >        case 24:
50 >                if (hdr->compr != BI_UNCOMPR)
51 >                        return 0;
52 >                break;
53 >        case 16:
54 >        case 32:
55 >                if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_BITFIELDS))
56 >                        return 0;
57 >                break;
58 >        case 4:
59 >                if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE4))
60 >                        return 0;
61 >                break;
62 >        case 8:
63 >                if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE8))
64 >                        return 0;
65 >                break;
66 >        default:
67 >                return 0;
68 >        }
69 >        if (hdr->compr == BI_BITFIELDS && (BMPbitField(hdr)[0] &
70 >                                BMPbitField(hdr)[1] & BMPbitField(hdr)[2]))
71 >                return 0;
72 >        if ((hdr->nColors < 0) | (hdr->impColors < 0))
73 >                return 0;
74 >        if (hdr->impColors > hdr->nColors)
75 >                return 0;
76 >        if (hdr->nColors > BMPpalLen(hdr))
77 >                return 0;
78 >        return 1;
79   }
80  
81 +                                        /* compute uncompressed scan size */
82 + #define getScanSiz(h)   ( ((((h)->bpp*(h)->width+7) >>3) + 3) & ~03 )
83 +
84                                          /* get next byte from reader */
85   #define rdbyte(c,br)    ((br)->fpos += (c=(*(br)->cget)((br)->c_data))!=EOF, c)
86  
# Line 130 | Line 167 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
167          (void)rdint32(br);                      /* file size */
168          (void)rdint32(br);                      /* reserved word */
169          bmPos = rdint32(br);                    /* offset to bitmap */
170 <        hdrSiz = rdint32(br) + 14;              /* header size */
170 >        hdrSiz = 2 + 3*4 + rdint32(br);         /* header size */
171          if (hdrSiz < 2 + 6*4 + 2*2 + 6*4)
172                  goto err;
173          br->hdr->width = rdint32(br);           /* bitmap width */
# Line 142 | Line 179 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
179          if (rduint16(br) != 1)                  /* number of planes */
180                  goto err;
181          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;
182          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 */
183          (void)rdint32(br);                      /* bitmap size */
184          br->hdr->hRes = rdint32(br);            /* horizontal resolution */
185          br->hdr->vRes = rdint32(br);            /* vertical resolution */
# Line 156 | Line 187 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
187          br->hdr->impColors = rdint32(br);       /* # important colors */
188          if (br->hdr->impColors < 0)
189                  goto err;                       /* catch premature EOF */
190 <        palLen = 0;
191 <        if (br->hdr->bpp <= 8) {
192 <                palLen = 1 << br->hdr->bpp;
190 >        if (!BMPheaderOK(br->hdr))
191 >                goto err;
192 >        palLen = BMPpalLen(br->hdr);
193 >        if (br->hdr->bpp <= 8) {                /* normalize color counts */
194                  if (br->hdr->nColors <= 0)
195                          br->hdr->nColors = palLen;
164                else if (br->hdr->nColors > palLen)
165                        goto err;
196                  if (br->hdr->impColors <= 0)
197                          br->hdr->impColors = br->hdr->nColors;
198 <                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;
198 >        }
199                                                  /* extend header */
200          if (bmPos < hdrSiz + sizeof(RGBquad)*palLen)
201                  goto err;
# Line 176 | Line 203 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
203          if (palLen > 0 || br->hdr->infoSiz > 0) {
204                  br->hdr = (BMPHeader *)realloc((void *)br->hdr,
205                                          sizeof(BMPHeader) +
206 <                                        sizeof(RGBquad)*palLen -
180 <                                        sizeof(br->hdr->palette) +
206 >                                        sizeof(RGBquad)*palLen +
207                                          br->hdr->infoSiz);
208                  if (br->hdr == NULL)
209                          goto err;
210          }
211 <                                                /* read color palette */
212 <        if (rdbytes((char *)br->hdr->palette,
211 >                                                /* read colors or fields */
212 >        if (br->hdr->compr == BI_BITFIELDS) {
213 >                BMPbitField(br->hdr)[0] = (uint32)rdint32(br);
214 >                BMPbitField(br->hdr)[1] = (uint32)rdint32(br);
215 >                BMPbitField(br->hdr)[2] = (uint32)rdint32(br);
216 >        } else if (rdbytes((char *)br->hdr->palette,
217                          sizeof(RGBquad)*palLen, br) != BIR_OK)
218                  goto err;
219                                                  /* read add'l information */
# Line 194 | Line 224 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
224          if (br->scanline == NULL)
225                  goto err;
226          br->yscan = -1;
227 <        if (seek != NULL && br->hdr->compr != BI_UNCOMPR) {
227 >        if (seek != NULL && ((br->hdr->compr == BI_RLE8) |
228 >                                        (br->hdr->compr == BI_RLE4))) {
229                  BMPReader       *newbr = (BMPReader *)realloc((void *)br,
230                                                  sizeof(BMPReader) +
231                                                  sizeof(br->scanpos[0]) *
# Line 206 | Line 237 | BMPopenReader(int (*cget)(void *), int (*seek)(uint32,
237                                  sizeof(br->scanpos[0])*br->hdr->height);
238          }
239          br->scanpos[0] = br->fpos;
240 <        if (BMPreadScanline(br) != BIR_OK)
241 <                goto err;
211 <        return br;
240 >        if (BMPreadScanline(br) == BIR_OK)
241 >                return br;
242   err:
243          if (br->hdr != NULL)
244                  free((void *)br->hdr);
# Line 229 | Line 259 | BMPisGrayscale(const BMPHeader *hdr)
259                  return -1;
260          if (hdr->bpp > 8)               /* assume they had a reason for it */
261                  return 0;
262 <        for (rgbp = hdr->palette, n = hdr->impColors; n-- > 0; rgbp++)
262 >        for (rgbp = hdr->palette, n = hdr->nColors; n-- > 0; rgbp++)
263                  if (((rgbp->r != rgbp->g) | (rgbp->g != rgbp->b)))
264                          return 0;
265          return 1;                       /* all colors neutral in map */
# Line 239 | Line 269 | BMPisGrayscale(const BMPHeader *hdr)
269   int
270   BMPreadScanline(BMPReader *br)
271   {
272 +        int     n;
273 +        int8    *sp;
274 +
275          if (br->yscan + 1 >= br->hdr->height)
276                  return BIR_EOF;
277          br->yscan++;                    /* prepare to read */
278 <        if (br->hdr->compr == BI_UNCOMPR)
279 <                return rdbytes((char *)br->scanline, getScanSiz(br->hdr), br);
280 < /* XXX need to perform actual decompression */
281 <        return BIR_UNSUPPORTED;
282 <        if (br->seek != NULL)
278 >        n = getScanSiz(br->hdr);        /* reading uncompressed data? */
279 >        if (br->hdr->compr == BI_UNCOMPR || br->hdr->compr == BI_BITFIELDS)
280 >                return rdbytes((char *)br->scanline, n, br);
281 >        /*
282 >         * RLE4/RLE8 Decoding
283 >         *
284 >         * Certain aspects of this scheme are completely insane, so
285 >         * we don't support them.  Fortunately, they rarely appear.
286 >         * One is the mid-file EOD (0x0001) and another is the ill-conceived
287 >         * "delta" (0x0002), which is like a "goto" statement for bitmaps.
288 >         * Whoever thought this up should be wrestled to the ground and told
289 >         * why it's impossible to support such a scheme in any reasonable way.
290 >         * Also, RLE4 mode allows runs to stop halfway through a byte,
291 >         * which is likewise uncodeable, so we don't even try.
292 >         * Finally, the scanline break is ambiguous -- we assume here that
293 >         * it is required at the end of each scanline, though I haven't
294 >         * found anywhere this is written.  Otherwise, we would read to
295 >         * the end of the scanline, assuming the next bit of data belongs
296 >         * the following scan.  If a break follows the last pixel, as it
297 >         * seems to in the files I've tested out of Photoshop, you end up
298 >         * painting every other line black.  Also, I assume any skipped
299 >         * pixels are painted with color 0, which is often black.  Nowhere
300 >         * is it specified what we should assume for missing pixels.  This
301 >         * is undoubtedly the most brain-dead format I've ever encountered.
302 >         */
303 >        sp = br->scanline;
304 >        n = br->hdr->width;
305 >        if (br->hdr->compr == BI_RLE4)
306 >                n = (n + 1) >> 1;
307 >        while (n > 0) {
308 >                int     skipOdd, len, val;
309 >
310 >                if (rdbyte(len, br) == EOF)
311 >                        return BIR_TRUNCATED;
312 >                if (len > 0) {          /* got a run */
313 >                        if (br->hdr->compr == BI_RLE4) {
314 >                                if (len & 1)
315 >                                        return BIR_UNSUPPORTED;
316 >                                len >>= 1;
317 >                        }
318 >                        if (len > n)
319 >                                return BIR_RLERROR;
320 >                        if (rdbyte(val, br) == EOF)
321 >                                return BIR_TRUNCATED;
322 >                        n -= len;
323 >                        while (len--)
324 >                                *sp++ = val;
325 >                        continue;
326 >                }
327 >                                        /* check for escape */
328 >                switch (rdbyte(len, br)) {
329 >                case EOF:
330 >                        return BIR_TRUNCATED;
331 >                case 0:                 /* end of line */
332 >                        while (n--)
333 >                                *sp++ = 0;
334 >                        /* leaves n == -1 as flag for test after loop */
335 >                        continue;
336 >                case 1:                 /* end of bitmap */
337 >                case 2:                 /* delta */
338 >                        return BIR_UNSUPPORTED;
339 >                }
340 >                                        /* absolute mode */
341 >                if (br->hdr->compr == BI_RLE4) {
342 >                        if (len & 1)
343 >                                return BIR_UNSUPPORTED;
344 >                        len >>= 1;
345 >                }
346 >                skipOdd = len & 1;
347 >                if (len > n)
348 >                        return BIR_RLERROR;
349 >                n -= len;
350 >                while (len--) {
351 >                        if (rdbyte(val, br) == EOF)
352 >                                return BIR_TRUNCATED;
353 >                        *sp++ = val;
354 >                }
355 >                if (skipOdd && rdbyte(val, br) == EOF)
356 >                        return BIR_TRUNCATED;
357 >        }
358 >                                        /* verify break at end of line */
359 >        if (!n && (rdbyte(n, br) != 0 || (rdbyte(n, br) != 0 &&
360 >                                (n != 1 || br->yscan != br->hdr->height-1))))
361 >                return BIR_RLERROR;
362 >        if (br->seek != NULL)           /* record next scanline position */
363                  br->scanpos[br->yscan + 1] = br->fpos;
364          return BIR_OK;
365   }
# Line 270 | Line 383 | BMPseekScanline(int y, BMPReader *br)
383          if (y != br->yscan + 1 && br->seek != NULL) {
384                  int     yseek;
385                  uint32  seekp;
386 <                if (br->hdr->compr == BI_UNCOMPR) {
386 >                if (br->hdr->compr == BI_UNCOMPR ||
387 >                                        br->hdr->compr == BI_BITFIELDS) {
388                          yseek = y;
389                          seekp = br->scanpos[0] + y*getScanSiz(br->hdr);
390                  } else {
# Line 295 | Line 409 | BMPseekScanline(int y, BMPReader *br)
409  
410   /* get ith pixel from last scanline */
411   RGBquad
412 < BMPdecodePixel(int i, BMPReader *br)
412 > BMPdecodePixel(int i, const BMPReader *br)
413   {
414 +        static const uint32     std16mask[3] = {0x7c00, 0x3e0, 0x1f};
415          static const RGBquad    black = {0, 0, 0, 0};
416 +        const uint32            *mask;
417 +        const uint8             *pp;
418 +        uint32                  pval, v;
419 +        RGBquad                 cval;
420          
421          if (((br == NULL) | (i < 0)) || i >= br->hdr->width)
422                  return black;
423  
424 +        cval.padding = 0;
425 +
426          switch (br->hdr->bpp) {
427 <        case 24: {
428 <                uint8   *bgr = br->scanline + 3*i;
429 <                RGBquad cval;
430 <                cval.b = bgr[0]; cval.g = bgr[1]; cval.r = bgr[2];
431 <                cval.padding = 0;
427 >        case 24:
428 >                pp = br->scanline + 3*i;
429 >                cval.b = *pp++;
430 >                cval.g = *pp++;
431 >                cval.r = *pp;
432                  return cval;
312                }
433          case 32:
434 <                return ((RGBquad *)br->scanline)[i];
434 >                if (br->hdr->compr == BI_UNCOMPR)
435 >                        return ((RGBquad *)br->scanline)[i];
436 >                                                /* convert bit fields */
437 >                pp = br->scanline + 4*i;
438 >                pval = *pp++;
439 >                pval |= *pp++ << 8;
440 >                pval |= *pp++ << 16;
441 >                pval |= *pp << 24;
442 >                mask = BMPbitField(br->hdr);
443 >                v = pval & mask[0];
444 >                while (v & ~0xff) v >>= 8;
445 >                cval.r = v;
446 >                v = pval & mask[1];
447 >                while (v & ~0xff) v >>= 8;
448 >                cval.g = v;
449 >                v = pval & mask[2];
450 >                while (v & ~0xff) v >>= 8;
451 >                cval.b = v;
452 >                return cval;
453          case 8:
454                  return br->hdr->palette[br->scanline[i]];
455          case 1:
456 <                return br->hdr->palette[br->scanline[i>>3]>>(i&7) & 1];
456 >                return br->hdr->palette[br->scanline[i>>3]>>((7-i)&7) & 1];
457          case 4:
458 <                return br->hdr->palette[br->scanline[i>>1]>>((i&1)<<2) & 0xf];
458 >                return br->hdr->palette[br->scanline[i>>1]>>(i&1?4:0) & 0xf];
459 >        case 16:
460 >                pp = br->scanline + 2*i;
461 >                pval = *pp++;
462 >                pval |= *pp++ << 8;
463 >                mask = std16mask;
464 >                if (br->hdr->compr == BI_BITFIELDS)
465 >                        mask = BMPbitField(br->hdr);
466 >                cval.r = ((pval & mask[0]) << 8) / (mask[0] + 1);
467 >                cval.g = ((pval & mask[1]) << 8) / (mask[1] + 1);
468 >                cval.b = ((pval & mask[2]) << 8) / (mask[2] + 1);
469 >                return cval;
470          }
471          return black;                           /* should never happen */
472   }
# Line 409 | Line 558 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
558          hdr->height = yr;
559          hdr->yIsDown = 0;                       /* default to upwards order */
560          hdr->bpp = n;
561 <        hdr->compr = BI_UNCOMPR;
561 >        hdr->compr = BI_UNCOMPR;                /* compression needs seek */
562          hdr->hRes = hdr->vRes = 2835;           /* default to 72 ppi */
563          hdr->nColors = ncolors;
564          hdr->impColors = 0;                     /* says all colors important */
# Line 423 | Line 572 | BMPmappedHeader(int xr, int yr, int infolen, int ncolo
572  
573                                          /* put byte to writer */
574   #define wrbyte(c,bw)    ( (*(bw)->cput)(c,(bw)->c_data), \
575 <                                ++((bw)->fpos) > (bw)->flen ? \
575 >                                ++(bw)->fpos > (bw)->flen ? \
576                                          ((bw)->flen = (bw)->fpos) : \
577                                          (bw)->fpos )
578  
# Line 442 | Line 591 | wrint32(int32 i, BMPWriter *bw)
591          wrbyte(i& 0xff, bw);
592          wrbyte(i>>8 & 0xff, bw);
593          wrbyte(i>>16 & 0xff, bw);
594 <        wrbyte(i>>24  & 0xff, bw);
594 >        wrbyte(i>>24 & 0xff, bw);
595   }
596  
597   /* write 16-bit unsigned integer in littlendian order */
# Line 459 | Line 608 | wrseek(uint32 pos, BMPWriter *bw)
608   {
609          if (pos == bw->fpos)
610                  return BIR_OK;
611 <        if (bw->seek == NULL) {
612 <                if (pos < bw->fpos)
464 <                        return BIR_SEEKERR;
465 <                while (bw->fpos < pos)
466 <                        wrbyte(0, bw);
467 <                return BIR_OK;
468 <        }
611 >        if (bw->seek == NULL)
612 >                return BIR_SEEKERR;
613          if ((*bw->seek)(pos, bw->c_data) != 0)
614                  return BIR_SEEKERR;
615          bw->fpos = pos;
# Line 482 | Line 626 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
626          BMPWriter       *bw;
627          uint32          hdrSiz, palSiz, scanSiz, bmSiz;
628                                                  /* check arguments */
629 <        if (cput == NULL || hdr == NULL)
629 >        if (cput == NULL)
630                  return NULL;
631 <        if (hdr->width <= 0 || hdr->height <= 0)
631 >        if (!BMPheaderOK(hdr))
632                  return NULL;
633 <        if (hdr->compr != BI_UNCOMPR && hdr->compr != BI_RLE8)
633 >        if ((hdr->bpp == 16) | (hdr->compr == BI_RLE4))
634                  return NULL;                    /* unsupported */
635 <        if (hdr->bpp > 8 && hdr->compr != BI_UNCOMPR)
635 > /* no seek means we may have the wrong file length, but most app's don't care
636 >        if (seek == NULL && ((hdr->compr == BI_RLE8) | (hdr->compr == BI_RLE4)))
637                  return NULL;
638 <        palSiz = BMPpalLen(hdr);
494 <        if (hdr->nColors > palSiz)
495 <                return NULL;
496 <        if (hdr->compr != BI_UNCOMPR && seek == NULL)
497 <                return NULL;
638 > */
639                                                  /* compute sizes */
640          hdrSiz = 2 + 6*4 + 2*2 + 6*4;
641 <        palSiz *= sizeof(RGBquad);
641 >        if (hdr->compr == BI_BITFIELDS)
642 >                hdrSiz += sizeof(uint32)*3;
643 >        palSiz = sizeof(RGBquad)*BMPpalLen(hdr);
644          scanSiz = getScanSiz(hdr);
645          bmSiz = hdr->height*scanSiz;            /* wrong if compressed */
646                                                  /* initialize writer */
# Line 547 | Line 690 | BMPopenWriter(void (*cput)(int, void *), int (*seek)(u
690   #endif
691          return bw;
692   }
693 +
694 + /* find position of next run of 5 or more identical bytes, or 255 if none */
695 + static int
696 + findNextRun(const int8 *bp, int len)
697 + {
698 +        int     pos, cnt;
699 +                                                /* look for run */
700 +        for (pos = 0; (len > 0) & (pos < 255); pos++, bp++, len--) {
701 +                if (len < 5)                    /* no hope left? */
702 +                        continue;
703 +                cnt = 1;                        /* else let's try it */
704 +                while (bp[cnt] == bp[0])
705 +                        if (++cnt >= 5)
706 +                                return pos;     /* long enough */
707 +        }
708 +        return pos;                             /* didn't find any */
709 + }
710                                  
711   /* write the current scanline */
712   int
713   BMPwriteScanline(BMPWriter *bw)
714   {
715 +        const int8      *sp;
716 +        int             n;
717 +
718          if (bw->yscan >= bw->hdr->height)
719                  return BIR_EOF;
720                                                  /* writing uncompressed? */
721 <        if (bw->hdr->compr == BI_UNCOMPR) {
721 >        if (bw->hdr->compr == BI_UNCOMPR || bw->hdr->compr == BI_BITFIELDS) {
722                  uint32  scanSiz = getScanSiz(bw->hdr);
723                  uint32  slpos = bw->fbmp + bw->yscan*scanSiz;
724                  if (wrseek(slpos, bw) != BIR_OK)
# Line 564 | Line 727 | BMPwriteScanline(BMPWriter *bw)
727                  bw->yscan++;
728                  return BIR_OK;
729          }
730 <                                                /* else write compressed */
731 < /* XXX need to do actual compression */
732 <        return BIR_UNSUPPORTED;
733 <                                                /* write file length at end */
730 >        /*
731 >         * RLE8 Encoding
732 >         *
733 >         * See the notes in BMPreadScanline() on this encoding.  Needless
734 >         * to say, we avoid the nuttier aspects of this specification.
735 >         * We also assume that every scanline ends in a line break
736 >         * (0x0000) except for the last, which ends in a bitmap break
737 >         * (0x0001).  We don't support RLE4 at all; it's too awkward.
738 >         */
739 >        sp = bw->scanline;
740 >        n = bw->hdr->width;
741 >        while (n > 0) {
742 >                int     cnt, val;
743 >                cnt = findNextRun(sp, n);       /* 0-255 < n */
744 >                if (cnt >= 3) {                 /* output absolute */
745 >                        int     skipOdd = cnt & 1;
746 >                        wrbyte(0, bw);
747 >                        wrbyte(cnt, bw);
748 >                        n -= cnt;
749 >                        while (cnt--)
750 >                                wrbyte(*sp++, bw);
751 >                        if (skipOdd)
752 >                                wrbyte(0, bw);
753 >                }
754 >                if (n <= 0)                     /* was that it? */
755 >                        break;
756 >                val = *sp;                      /* output run */
757 >                for (cnt = 1; --n && cnt < 255; cnt++)
758 >                        if (*++sp != val)
759 >                                break;
760 >                wrbyte(cnt, bw);
761 >                wrbyte(val, bw);
762 >        }
763 >        bw->yscan++;                            /* write line break or EOD */
764          if (bw->yscan == bw->hdr->height) {
765 <                if (bw->seek == NULL || (*bw->seek)(2, bw->c_data) != 0)
766 <                        return BIR_SEEKERR;
767 <                bw->fpos = 2;
768 <                wrint32(bw->flen, bw);
765 >                wrbyte(0, bw); wrbyte(1, bw);   /* end of bitmap marker */
766 >                if (wrseek(2, bw) != BIR_OK)
767 >                        return BIR_OK;          /* no one may care */
768 >                wrint32(bw->flen, bw);          /* correct file length */
769 >                if (wrseek(34, bw) != BIR_OK)
770 >                        return BIR_OK;
771 >                wrint32(bw->flen-bw->fbmp, bw); /* correct bitmap length */
772 >        } else {
773 >                wrbyte(0, bw); wrbyte(0, bw);   /* end of line marker */
774          }
775          return BIR_OK;
776   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines