ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bmpfile.c
Revision: 2.8
Committed: Sat Apr 10 02:54:44 2004 UTC (20 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.7: +7 -10 lines
Log Message:
Minor fix in writing of bitmap length

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.8 static const char RCSid[] = "$Id: bmpfile.c,v 2.7 2004/03/30 16:24:25 schorsch Exp $";
3 greg 2.1 #endif
4     /*
5     * Windows and OS/2 BMP file support
6     */
7    
8     #include <stdio.h>
9     #include <stdlib.h>
10     #include <string.h>
11     #include "bmpfile.h"
12    
13     /* get corresponding error message */
14     const char *
15     BMPerrorMessage(int ec)
16     {
17     switch (ec) {
18     case BIR_OK:
19     return "No error";
20     case BIR_EOF:
21     return "End of BMP image";
22     case BIR_TRUNCATED:
23     return "Truncated BMP image";
24     case BIR_UNSUPPORTED:
25     return "Unsupported BMP feature";
26 greg 2.3 case BIR_RLERROR:
27     return "BMP runlength encoding error";
28 greg 2.1 case BIR_SEEKERR:
29     return "BMP seek error";
30     }
31     return "Unknown BMP error";
32     }
33    
34 greg 2.3 /* check than header is sensible */
35     static int
36     BMPheaderOK(const BMPHeader *hdr)
37     {
38     if (!hdr)
39     return 0;
40 schorsch 2.4 if ((hdr->width <= 0) | (hdr->height <= 0))
41 greg 2.3 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 schorsch 2.4 if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_BITFIELDS))
51 greg 2.3 return 0;
52     break;
53     case 4:
54 schorsch 2.4 if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE4))
55 greg 2.3 return 0;
56     break;
57     case 8:
58 schorsch 2.4 if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE8))
59 greg 2.3 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 schorsch 2.4 if ((hdr->nColors < 0) | (hdr->impColors < 0))
68 greg 2.3 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 greg 2.1
76 greg 2.3 /* compute uncompressed scan size */
77 schorsch 2.4 #define getScanSiz(h) ( ((((h)->bpp*(h)->width+7) >>3) + 3) & ~03 )
78 greg 2.1
79     /* get next byte from reader */
80     #define rdbyte(c,br) ((br)->fpos += (c=(*(br)->cget)((br)->c_data))!=EOF, c)
81    
82     /* read n bytes */
83     static int
84     rdbytes(char *bp, uint32 n, BMPReader *br)
85     {
86     int c;
87    
88     while (n--) {
89     if (rdbyte(c, br) == EOF)
90     return BIR_TRUNCATED;
91     *bp++ = c;
92     }
93     return BIR_OK;
94     }
95    
96     /* read 32-bit integer in littlendian order */
97     static int32
98     rdint32(BMPReader *br)
99     {
100     int32 i;
101     int c;
102    
103     i = rdbyte(c, br);
104     i |= rdbyte(c, br) << 8;
105     i |= rdbyte(c, br) << 16;
106     i |= rdbyte(c, br) << 24;
107     return i; /* -1 on EOF */
108     }
109    
110     /* read 16-bit unsigned integer in littlendian order */
111     static int
112     rduint16(BMPReader *br)
113     {
114     int i;
115     int c;
116    
117     i = rdbyte(c, br);
118     i |= rdbyte(c, br) << 8;
119     return i; /* -1 on EOF */
120     }
121    
122     /* seek on reader or return 0 (BIR_OK) on success */
123     static int
124     rdseek(uint32 pos, BMPReader *br)
125     {
126     if (pos == br->fpos)
127     return BIR_OK;
128     if (br->seek == NULL || (*br->seek)(pos, br->c_data) != 0)
129     return BIR_SEEKERR;
130     br->fpos = pos;
131     return BIR_OK;
132     }
133    
134     /* open BMP stream for reading and get first scanline */
135     BMPReader *
136     BMPopenReader(int (*cget)(void *), int (*seek)(uint32, void *), void *c_data)
137     {
138     BMPReader *br;
139     uint32 bmPos, hdrSiz;
140     int palLen;
141    
142     if (cget == NULL)
143     return NULL;
144     int magic[2]; /* check magic number */
145     magic[0] = (*cget)(c_data);
146     if (magic[0] != 'B')
147     return NULL;
148     magic[1] = (*cget)(c_data);
149     if (magic[1] != 'M' && magic[1] != 'A')
150     return NULL;
151     br = (BMPReader *)calloc(1, sizeof(BMPReader));
152     if (br == NULL)
153     return NULL;
154     br->cget = cget;
155     br->seek = seek;
156     br->c_data = c_data;
157     br->hdr = (BMPHeader *)malloc(sizeof(BMPHeader));
158     if (br->hdr == NULL)
159     goto err;
160     br->fpos = 2;
161     /* read & verify file header */
162     (void)rdint32(br); /* file size */
163     (void)rdint32(br); /* reserved word */
164     bmPos = rdint32(br); /* offset to bitmap */
165 greg 2.3 hdrSiz = 2 + 3*4 + rdint32(br); /* header size */
166 greg 2.1 if (hdrSiz < 2 + 6*4 + 2*2 + 6*4)
167     goto err;
168     br->hdr->width = rdint32(br); /* bitmap width */
169     br->hdr->height = rdint32(br); /* bitmap height */
170 schorsch 2.2 if (((br->hdr->width <= 0) | (br->hdr->height == 0)))
171 greg 2.1 goto err;
172     if ((br->hdr->yIsDown = br->hdr->height < 0))
173     br->hdr->height = -br->hdr->height;
174     if (rduint16(br) != 1) /* number of planes */
175     goto err;
176     br->hdr->bpp = rduint16(br); /* bits per pixel */
177     br->hdr->compr = rdint32(br); /* compression mode */
178     (void)rdint32(br); /* bitmap size */
179     br->hdr->hRes = rdint32(br); /* horizontal resolution */
180     br->hdr->vRes = rdint32(br); /* vertical resolution */
181     br->hdr->nColors = rdint32(br); /* # colors used */
182     br->hdr->impColors = rdint32(br); /* # important colors */
183     if (br->hdr->impColors < 0)
184     goto err; /* catch premature EOF */
185 greg 2.3 if (!BMPheaderOK(br->hdr))
186     goto err;
187     palLen = BMPpalLen(br->hdr);
188     if (br->hdr->bpp <= 8) { /* normalize color counts */
189 greg 2.1 if (br->hdr->nColors <= 0)
190     br->hdr->nColors = palLen;
191     if (br->hdr->impColors <= 0)
192     br->hdr->impColors = br->hdr->nColors;
193 greg 2.3 }
194 greg 2.1 /* extend header */
195     if (bmPos < hdrSiz + sizeof(RGBquad)*palLen)
196     goto err;
197     br->hdr->infoSiz = bmPos - (hdrSiz + sizeof(RGBquad)*palLen);
198     if (palLen > 0 || br->hdr->infoSiz > 0) {
199     br->hdr = (BMPHeader *)realloc((void *)br->hdr,
200     sizeof(BMPHeader) +
201 greg 2.3 sizeof(RGBquad)*palLen +
202 greg 2.1 br->hdr->infoSiz);
203     if (br->hdr == NULL)
204     goto err;
205     }
206 greg 2.3 /* 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 greg 2.1 sizeof(RGBquad)*palLen, br) != BIR_OK)
213     goto err;
214     /* read add'l information */
215     if (rdbytes(BMPinfo(br->hdr), br->hdr->infoSiz, br) != BIR_OK)
216     goto err;
217     /* read first scanline */
218     br->scanline = (uint8 *)calloc(getScanSiz(br->hdr), sizeof(uint8));
219     if (br->scanline == NULL)
220     goto err;
221     br->yscan = -1;
222 schorsch 2.4 if (seek != NULL && ((br->hdr->compr == BI_RLE8) |
223     (br->hdr->compr == BI_RLE4))) {
224 greg 2.1 BMPReader *newbr = (BMPReader *)realloc((void *)br,
225     sizeof(BMPReader) +
226     sizeof(br->scanpos[0]) *
227     br->hdr->height);
228     if (newbr == NULL)
229     goto err;
230     br = newbr;
231     memset((void *)(br->scanpos + 1), 0,
232     sizeof(br->scanpos[0])*br->hdr->height);
233     }
234     br->scanpos[0] = br->fpos;
235     if (BMPreadScanline(br) != BIR_OK)
236     goto err;
237     return br;
238     err:
239     if (br->hdr != NULL)
240     free((void *)br->hdr);
241     if (br->scanline != NULL)
242     free((void *)br->scanline);
243     free((void *)br);
244     return NULL;
245     }
246    
247     /* determine if image is grayscale */
248     int
249     BMPisGrayscale(const BMPHeader *hdr)
250     {
251     const RGBquad *rgbp;
252     int n;
253    
254     if (hdr == NULL)
255     return -1;
256     if (hdr->bpp > 8) /* assume they had a reason for it */
257     return 0;
258 greg 2.6 for (rgbp = hdr->palette, n = hdr->nColors; n-- > 0; rgbp++)
259 schorsch 2.2 if (((rgbp->r != rgbp->g) | (rgbp->g != rgbp->b)))
260 greg 2.1 return 0;
261     return 1; /* all colors neutral in map */
262     }
263    
264     /* read and decode next BMP scanline */
265     int
266     BMPreadScanline(BMPReader *br)
267     {
268 greg 2.3 int n;
269     int8 *sp;
270    
271 greg 2.1 if (br->yscan + 1 >= br->hdr->height)
272     return BIR_EOF;
273     br->yscan++; /* prepare to read */
274 greg 2.3 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 greg 2.6 * One is the mid-file EOD (0x0001) and another is the ill-conceived
283 greg 2.3 * "delta" (0x0002), which is like a "goto" statement for bitmaps.
284     * Whoever thought this up should be shot, then told why
285 greg 2.6 * it's impossible to support such a scheme in any reasonable way.
286 greg 2.3 * 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 greg 2.6 * painting every other line black. Also, I assume any skipped
295 greg 2.3 * 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 greg 2.6 /* leaves n == -1 as flag for test after loop */
327 greg 2.3 continue;
328     case 1: /* end of bitmap */
329     case 2: /* delta */
330     return BIR_UNSUPPORTED;
331     }
332     /* absolute mode */
333     if (br->hdr->compr == BI_RLE4) {
334     if (len & 1)
335     return BIR_UNSUPPORTED;
336     len >>= 1;
337     }
338     skipOdd = len & 1;
339     if (len > n)
340     return BIR_RLERROR;
341     n -= len;
342     while (len--) {
343     if (rdbyte(val, br) == EOF)
344     return BIR_TRUNCATED;
345     *sp++ = val;
346     }
347     if (skipOdd && rdbyte(val, br) == EOF)
348     return BIR_TRUNCATED;
349     }
350     /* verify break at end of line */
351 greg 2.6 if (!n && (rdbyte(n, br) != 0 || (rdbyte(n, br) != 0 &&
352     (n != 1 || br->yscan != br->hdr->height-1))))
353 greg 2.3 return BIR_RLERROR;
354     if (br->seek != NULL) /* record next scanline position */
355 greg 2.1 br->scanpos[br->yscan + 1] = br->fpos;
356     return BIR_OK;
357     }
358    
359     /* read a specific scanline */
360     int
361     BMPseekScanline(int y, BMPReader *br)
362     {
363     int rv;
364     /* check arguments */
365     if (br == NULL)
366     return BIR_EOF;
367     if (y < 0)
368     return BIR_SEEKERR;
369     if (y >= br->hdr->height)
370     return BIR_EOF;
371     /* already read? */
372     if (y == br->yscan)
373     return BIR_OK;
374     /* shall we seek? */
375     if (y != br->yscan + 1 && br->seek != NULL) {
376     int yseek;
377     uint32 seekp;
378 greg 2.3 if (br->hdr->compr == BI_UNCOMPR ||
379     br->hdr->compr == BI_BITFIELDS) {
380 greg 2.1 yseek = y;
381     seekp = br->scanpos[0] + y*getScanSiz(br->hdr);
382     } else {
383     yseek = br->yscan + 1;
384     while (yseek < y && br->scanpos[yseek+1] != 0)
385     ++yseek;
386     if (y < yseek && br->scanpos[yseek=y] == 0)
387     return BIR_SEEKERR;
388     seekp = br->scanpos[yseek];
389     }
390     if ((rv = rdseek(seekp, br)) != BIR_OK)
391     return rv;
392     br->yscan = yseek - 1;
393     } else if (y < br->yscan) /* else we can't back up */
394     return BIR_SEEKERR;
395     /* read until we get there */
396     while (br->yscan < y)
397     if ((rv = BMPreadScanline(br)) != BIR_OK)
398     return rv;
399     return BIR_OK;
400     }
401    
402     /* get ith pixel from last scanline */
403     RGBquad
404 greg 2.3 BMPdecodePixel(int i, const BMPReader *br)
405 greg 2.1 {
406 greg 2.3 static const uint32 std16mask[3] = {0x7c00, 0x3e0, 0x1f};
407 greg 2.1 static const RGBquad black = {0, 0, 0, 0};
408 greg 2.3 const uint32 *mask;
409     const uint8 *pp;
410     uint32 pval, v;
411     RGBquad cval;
412 greg 2.1
413 schorsch 2.2 if (((br == NULL) | (i < 0)) || i >= br->hdr->width)
414 greg 2.1 return black;
415    
416 greg 2.3 cval.padding = 0;
417    
418 greg 2.1 switch (br->hdr->bpp) {
419 greg 2.3 case 24:
420     pp = br->scanline + 3*i;
421     cval.b = *pp++;
422     cval.g = *pp++;
423     cval.r = *pp;
424 greg 2.1 return cval;
425     case 32:
426 greg 2.3 if (br->hdr->compr == BI_UNCOMPR)
427     return ((RGBquad *)br->scanline)[i];
428     /* convert bit fields */
429     pp = br->scanline + 4*i;
430     pval = *pp++;
431     pval |= *pp++ << 8;
432     pval |= *pp++ << 16;
433     pval |= *pp << 24;
434     mask = BMPbitField(br->hdr);
435     v = pval & mask[0];
436     while (v & ~0xff) v >>= 8;
437     cval.r = v;
438     v = pval & mask[1];
439     while (v & ~0xff) v >>= 8;
440     cval.g = v;
441     v = pval & mask[2];
442     while (v & ~0xff) v >>= 8;
443     cval.b = v;
444     return cval;
445 greg 2.1 case 8:
446     return br->hdr->palette[br->scanline[i]];
447     case 1:
448 schorsch 2.4 return br->hdr->palette[br->scanline[i>>3]>>((7-i)&7) & 1];
449 greg 2.1 case 4:
450 greg 2.3 return br->hdr->palette[br->scanline[i>>1]>>(i&1?4:0) & 0xf];
451     case 16:
452     pp = br->scanline + 2*i;
453     pval = *pp++;
454     pval |= *pp++ << 8;
455     mask = std16mask;
456     if (br->hdr->compr == BI_BITFIELDS)
457     mask = BMPbitField(br->hdr);
458     cval.r = ((pval & mask[0]) << 8) / (mask[0] + 1);
459     cval.g = ((pval & mask[1]) << 8) / (mask[1] + 1);
460     cval.b = ((pval & mask[2]) << 8) / (mask[2] + 1);
461     return cval;
462 greg 2.1 }
463     return black; /* should never happen */
464     }
465    
466     /* free BMP reader resources */
467     void
468     BMPfreeReader(BMPReader *br)
469     {
470     if (br == NULL)
471     return;
472     free((void *)br->hdr);
473     free((void *)br->scanline);
474     free((void *)br);
475     }
476    
477     /* stdio getc() callback */
478     int
479     stdio_getc(void *p)
480     {
481     if (!p)
482     return EOF;
483     return getc((FILE *)p);
484     }
485    
486     /* stdio putc() callback */
487     void
488     stdio_putc(int c, void *p)
489     {
490     if (p)
491     putc(c, (FILE *)p);
492     }
493    
494     /* stdio fseek() callback */
495     int
496     stdio_fseek(uint32 pos, void *p)
497     {
498     if (!p)
499     return -1;
500     return fseek((FILE *)p, (long)pos, 0);
501     }
502    
503     /* allocate uncompressed (24-bit) RGB header */
504     BMPHeader *
505     BMPtruecolorHeader(int xr, int yr, int infolen)
506     {
507     BMPHeader *hdr;
508    
509     if (xr <= 0 || yr <= 0 || infolen < 0)
510     return NULL;
511     hdr = (BMPHeader *)malloc(sizeof(BMPHeader) - sizeof(hdr->palette) +
512     infolen);
513     if (hdr == NULL)
514     return NULL;
515     hdr->width = xr;
516     hdr->height = yr;
517     hdr->yIsDown = 0; /* default to upwards order */
518     hdr->bpp = 24;
519     hdr->compr = BI_UNCOMPR;
520     hdr->hRes = hdr->vRes = 2835; /* default to 72 ppi */
521     hdr->nColors = hdr->impColors = 0;
522     hdr->infoSiz = infolen;
523     return hdr;
524     }
525    
526 greg 2.5 /* allocate color-mapped header (defaults to minimal grayscale) */
527 greg 2.1 BMPHeader *
528     BMPmappedHeader(int xr, int yr, int infolen, int ncolors)
529     {
530     int n;
531     BMPHeader *hdr;
532    
533     if (xr <= 0 || yr <= 0 || infolen < 0 || ncolors < 2)
534     return NULL;
535     if (ncolors <= 2)
536     n = 1;
537     else if (ncolors <= 16)
538     n = 4;
539     else if (ncolors <= 256)
540     n = 8;
541     else
542     return NULL;
543     hdr = (BMPHeader *)malloc(sizeof(BMPHeader) +
544     sizeof(RGBquad)*(1<<n) -
545     sizeof(hdr->palette) +
546     infolen);
547     if (hdr == NULL)
548     return NULL;
549     hdr->width = xr;
550     hdr->height = yr;
551     hdr->yIsDown = 0; /* default to upwards order */
552     hdr->bpp = n;
553 greg 2.5 hdr->compr = BI_UNCOMPR; /* compression needs seek */
554 greg 2.1 hdr->hRes = hdr->vRes = 2835; /* default to 72 ppi */
555     hdr->nColors = ncolors;
556     hdr->impColors = 0; /* says all colors important */
557     hdr->infoSiz = infolen;
558     memset((void *)hdr->palette, 0, sizeof(RGBquad)*(1<<n) + infolen);
559     for (n = ncolors; n--; )
560     hdr->palette[n].r = hdr->palette[n].g =
561     hdr->palette[n].b = n*255/(ncolors-1);
562     return hdr;
563     }
564    
565     /* put byte to writer */
566     #define wrbyte(c,bw) ( (*(bw)->cput)(c,(bw)->c_data), \
567 greg 2.3 ++(bw)->fpos > (bw)->flen ? \
568 greg 2.1 ((bw)->flen = (bw)->fpos) : \
569     (bw)->fpos )
570    
571     /* write out a string of bytes */
572     static void
573     wrbytes(char *bp, uint32 n, BMPWriter *bw)
574     {
575     while (n--)
576     wrbyte(*bp++, bw);
577     }
578    
579     /* write 32-bit integer in littlendian order */
580     static void
581     wrint32(int32 i, BMPWriter *bw)
582     {
583     wrbyte(i& 0xff, bw);
584     wrbyte(i>>8 & 0xff, bw);
585     wrbyte(i>>16 & 0xff, bw);
586 greg 2.3 wrbyte(i>>24 & 0xff, bw);
587 greg 2.1 }
588    
589     /* write 16-bit unsigned integer in littlendian order */
590     static void
591     wruint16(uint16 ui, BMPWriter *bw)
592     {
593     wrbyte(ui & 0xff, bw);
594     wrbyte(ui>>8 & 0xff, bw);
595     }
596    
597     /* seek to the specified file position, returning 0 (BIR_OK) on success */
598     static int
599     wrseek(uint32 pos, BMPWriter *bw)
600     {
601     if (pos == bw->fpos)
602     return BIR_OK;
603 greg 2.8 if (bw->seek == NULL)
604     return BIR_SEEKERR;
605 greg 2.1 if ((*bw->seek)(pos, bw->c_data) != 0)
606     return BIR_SEEKERR;
607     bw->fpos = pos;
608     if (pos > bw->flen)
609     bw->flen = pos;
610     return BIR_OK;
611     }
612    
613     /* open BMP stream for writing */
614     BMPWriter *
615     BMPopenWriter(void (*cput)(int, void *), int (*seek)(uint32, void *),
616     void *c_data, BMPHeader *hdr)
617     {
618     BMPWriter *bw;
619     uint32 hdrSiz, palSiz, scanSiz, bmSiz;
620     /* check arguments */
621 greg 2.3 if (cput == NULL)
622 greg 2.1 return NULL;
623 greg 2.3 if (!BMPheaderOK(hdr))
624 greg 2.1 return NULL;
625 schorsch 2.4 if ((hdr->bpp == 16) | (hdr->compr == BI_RLE4))
626 greg 2.1 return NULL; /* unsupported */
627 greg 2.5 /* no seek means we may have the wrong file length, but most app's don't care
628 schorsch 2.4 if (seek == NULL && ((hdr->compr == BI_RLE8) | (hdr->compr == BI_RLE4)))
629 greg 2.1 return NULL;
630 greg 2.5 */
631 greg 2.1 /* compute sizes */
632     hdrSiz = 2 + 6*4 + 2*2 + 6*4;
633 greg 2.3 if (hdr->compr == BI_BITFIELDS)
634     hdrSiz += sizeof(uint32)*3;
635     palSiz = sizeof(RGBquad)*BMPpalLen(hdr);
636 greg 2.1 scanSiz = getScanSiz(hdr);
637     bmSiz = hdr->height*scanSiz; /* wrong if compressed */
638     /* initialize writer */
639     bw = (BMPWriter *)malloc(sizeof(BMPWriter));
640     if (bw == NULL)
641     return NULL;
642     bw->hdr = hdr;
643     bw->yscan = 0;
644     bw->scanline = (uint8 *)calloc(scanSiz, sizeof(uint8));
645     if (bw->scanline == NULL) {
646     free((void *)bw);
647     return NULL;
648     }
649     bw->fbmp = hdrSiz + palSiz + hdr->infoSiz;
650     bw->fpos = bw->flen = 0;
651     bw->cput = cput;
652     bw->seek = seek;
653     bw->c_data = c_data;
654     /* write out header */
655     wrbyte('B', bw); wrbyte('M', bw); /* magic number */
656     wrint32(bw->fbmp + bmSiz, bw); /* file size */
657     wrint32(0, bw); /* reserved word */
658     wrint32(bw->fbmp, bw); /* offset to bitmap */
659     wrint32(hdrSiz - bw->fpos, bw); /* info header size */
660     wrint32(hdr->width, bw); /* bitmap width */
661     if (hdr->yIsDown) /* bitmap height */
662     wrint32(-hdr->height, bw);
663     else
664     wrint32(hdr->height, bw);
665     wruint16(1, bw); /* number of planes */
666     wruint16(hdr->bpp, bw); /* bits per pixel */
667     wrint32(hdr->compr, bw); /* compression mode */
668     wrint32(bmSiz, bw); /* bitmap size */
669     wrint32(hdr->hRes, bw); /* horizontal resolution */
670     wrint32(hdr->vRes, bw); /* vertical resolution */
671     wrint32(hdr->nColors, bw); /* # colors used */
672     wrint32(hdr->impColors, bw); /* # important colors */
673     /* write out color palette */
674     wrbytes((char *)hdr->palette, palSiz, bw);
675     /* write add'l information */
676     wrbytes(BMPinfo(hdr), hdr->infoSiz, bw);
677     #ifndef NDEBUG
678     if (bw->fpos != bw->fbmp) {
679     fputs("Coding error 1 in BMPopenWriter\n", stderr);
680     exit(1);
681     }
682     #endif
683     return bw;
684     }
685 greg 2.3
686     /* find position of next run of 5 or more identical bytes, or 255 if none */
687     static int
688     findNextRun(const int8 *bp, int len)
689     {
690     int pos, cnt;
691     /* look for run */
692 schorsch 2.4 for (pos = 0; (len > 0) & (pos < 255); pos++, bp++, len--) {
693 greg 2.3 if (len < 5) /* no hope left? */
694     continue;
695     cnt = 1; /* else let's try it */
696     while (bp[cnt] == bp[0])
697 greg 2.5 if (++cnt >= 5)
698     return pos; /* long enough */
699 greg 2.3 }
700     return pos; /* didn't find any */
701     }
702 greg 2.1
703     /* write the current scanline */
704     int
705     BMPwriteScanline(BMPWriter *bw)
706     {
707 greg 2.3 const int8 *sp;
708     int n;
709    
710 greg 2.1 if (bw->yscan >= bw->hdr->height)
711     return BIR_EOF;
712     /* writing uncompressed? */
713 greg 2.3 if (bw->hdr->compr == BI_UNCOMPR || bw->hdr->compr == BI_BITFIELDS) {
714 greg 2.1 uint32 scanSiz = getScanSiz(bw->hdr);
715     uint32 slpos = bw->fbmp + bw->yscan*scanSiz;
716     if (wrseek(slpos, bw) != BIR_OK)
717     return BIR_SEEKERR;
718     wrbytes((char *)bw->scanline, scanSiz, bw);
719     bw->yscan++;
720     return BIR_OK;
721     }
722 greg 2.3 /*
723     * RLE8 Encoding
724     *
725     * See the notes in BMPreadScanline() on this encoding. Needless
726     * to say, we avoid the nuttier aspects of this specification.
727     * We also assume that every scanline ends in a line break
728     * (0x0000) except for the last, which ends in a bitmap break
729     * (0x0001). We don't support RLE4 at all; it's too awkward.
730     */
731     sp = bw->scanline;
732     n = bw->hdr->width;
733     while (n > 0) {
734     int cnt, val;
735    
736     cnt = findNextRun(sp, n); /* 0-255 < n */
737     if (cnt >= 3) { /* output non-run */
738     int skipOdd = cnt & 1;
739     wrbyte(0, bw);
740     wrbyte(cnt, bw);
741     n -= cnt;
742     while (cnt--)
743     wrbyte(*sp++, bw);
744     if (skipOdd)
745     wrbyte(0, bw);
746     }
747     if (n <= 0) /* was that it? */
748     break;
749     val = *sp; /* output run */
750 greg 2.5 for (cnt = 1; cnt < 255; cnt++)
751 schorsch 2.7 if ((!--n) | (*++sp != val))
752 greg 2.5 break;
753 greg 2.3 wrbyte(cnt, bw);
754     wrbyte(val, bw);
755     }
756 greg 2.5 bw->yscan++; /* write line break or EOD */
757 greg 2.1 if (bw->yscan == bw->hdr->height) {
758 greg 2.3 wrbyte(0, bw); wrbyte(1, bw); /* end of bitmap marker */
759 greg 2.8 if (wrseek(2, bw) != BIR_OK)
760 greg 2.5 return BIR_OK; /* no one may care */
761 greg 2.8 wrint32(bw->flen, bw); /* correct file length */
762     if (wrseek(34, bw) != BIR_OK)
763     return BIR_OK;
764 greg 2.6 wrint32(bw->flen-bw->fbmp, bw); /* correct bitmap length */
765 greg 2.3 } else {
766     wrbyte(0, bw); wrbyte(0, bw); /* end of line marker */
767 greg 2.1 }
768     return BIR_OK;
769     }
770    
771     /* free BMP writer resources */
772     void
773     BMPfreeWriter(BMPWriter *bw)
774     {
775     if (bw == NULL)
776     return;
777     free((void *)bw->hdr);
778     free((void *)bw->scanline);
779     free((void *)bw);
780     }