ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/bmpfile.c
Revision: 2.6
Committed: Sat Mar 27 16:33:31 2004 UTC (20 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.5: +9 -8 lines
Log Message:
Minor code tweaks

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bmpfile.c,v 2.5 2004/03/27 05:43:37 greg Exp $";
3 #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 case BIR_RLERROR:
27 return "BMP runlength encoding error";
28 case BIR_SEEKERR:
29 return "BMP seek error";
30 }
31 return "Unknown BMP error";
32 }
33
34 /* check than header is sensible */
35 static int
36 BMPheaderOK(const BMPHeader *hdr)
37 {
38 if (!hdr)
39 return 0;
40 if ((hdr->width <= 0) | (hdr->height <= 0))
41 return 0;
42 switch (hdr->bpp) { /* check compression */
43 case 1:
44 case 24:
45 if (hdr->compr != BI_UNCOMPR)
46 return 0;
47 break;
48 case 16:
49 case 32:
50 if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_BITFIELDS))
51 return 0;
52 break;
53 case 4:
54 if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE4))
55 return 0;
56 break;
57 case 8:
58 if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE8))
59 return 0;
60 break;
61 default:
62 return 0;
63 }
64 if (hdr->compr == BI_BITFIELDS && (BMPbitField(hdr)[0] &
65 BMPbitField(hdr)[1] & BMPbitField(hdr)[2]))
66 return 0;
67 if ((hdr->nColors < 0) | (hdr->impColors < 0))
68 return 0;
69 if (hdr->impColors > hdr->nColors)
70 return 0;
71 if (hdr->nColors > BMPpalLen(hdr))
72 return 0;
73 return 1;
74 }
75
76 /* compute uncompressed scan size */
77 #define getScanSiz(h) ( ((((h)->bpp*(h)->width+7) >>3) + 3) & ~03 )
78
79 /* get next byte from reader */
80 #define rdbyte(c,br) ((br)->fpos += (c=(*(br)->cget)((br)->c_data))!=EOF, c)
81
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 hdrSiz = 2 + 3*4 + rdint32(br); /* header size */
166 if (hdrSiz < 2 + 6*4 + 2*2 + 6*4)
167 goto err;
168 br->hdr->width = rdint32(br); /* bitmap width */
169 br->hdr->height = rdint32(br); /* bitmap height */
170 if (((br->hdr->width <= 0) | (br->hdr->height == 0)))
171 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 if (!BMPheaderOK(br->hdr))
186 goto err;
187 palLen = BMPpalLen(br->hdr);
188 if (br->hdr->bpp <= 8) { /* normalize color counts */
189 if (br->hdr->nColors <= 0)
190 br->hdr->nColors = palLen;
191 if (br->hdr->impColors <= 0)
192 br->hdr->impColors = br->hdr->nColors;
193 }
194 /* 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 sizeof(RGBquad)*palLen +
202 br->hdr->infoSiz);
203 if (br->hdr == NULL)
204 goto err;
205 }
206 /* read colors or fields */
207 if (br->hdr->compr == BI_BITFIELDS) {
208 BMPbitField(br->hdr)[0] = (uint32)rdint32(br);
209 BMPbitField(br->hdr)[1] = (uint32)rdint32(br);
210 BMPbitField(br->hdr)[2] = (uint32)rdint32(br);
211 } else if (rdbytes((char *)br->hdr->palette,
212 sizeof(RGBquad)*palLen, br) != BIR_OK)
213 goto err;
214 /* read add'l information */
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 if (seek != NULL && ((br->hdr->compr == BI_RLE8) |
223 (br->hdr->compr == BI_RLE4))) {
224 BMPReader *newbr = (BMPReader *)realloc((void *)br,
225 sizeof(BMPReader) +
226 sizeof(br->scanpos[0]) *
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 for (rgbp = hdr->palette, n = hdr->nColors; n-- > 0; rgbp++)
259 if (((rgbp->r != rgbp->g) | (rgbp->g != rgbp->b)))
260 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 int n;
269 int8 *sp;
270
271 if (br->yscan + 1 >= br->hdr->height)
272 return BIR_EOF;
273 br->yscan++; /* prepare to read */
274 n = getScanSiz(br->hdr); /* reading uncompressed data? */
275 if (br->hdr->compr == BI_UNCOMPR || br->hdr->compr == BI_BITFIELDS)
276 return rdbytes((char *)br->scanline, n, br);
277 /*
278 * RLE/RLE8 Decoding
279 *
280 * Certain aspects of this scheme are completely insane, so
281 * we don't support them. Fortunately, they rarely appear.
282 * One is the mid-file EOD (0x0001) and another is the ill-conceived
283 * "delta" (0x0002), which is like a "goto" statement for bitmaps.
284 * Whoever thought this up should be shot, then told why
285 * it's impossible to support such a scheme in any reasonable way.
286 * Also, RLE4 mode allows runs to stop halfway through a byte,
287 * which is likewise uncodeable, so we don't even try.
288 * Finally, the scanline break is ambiguous -- we assume here that
289 * it is required at the end of each scanline, though I haven't
290 * found anywhere this is written. Otherwise, we would read to
291 * the end of the scanline, assuming the next bit of data belongs
292 * the following scan. If a break follows the last pixel, as it
293 * seems to in the files I've tested out of Photoshop, you end up
294 * painting every other line black. Also, I assume any skipped
295 * pixels are painted with color 0, which is often black. Nowhere
296 * is it specified what we should assume for missing pixels. This
297 * is undoubtedly the most brain-dead format I've ever encountered.
298 */
299 sp = br->scanline;
300 while (n > 0) {
301 int skipOdd, len, val;
302
303 if (rdbyte(len, br) == EOF)
304 return BIR_TRUNCATED;
305 if (len > 0) { /* got a run */
306 if (br->hdr->compr == BI_RLE4) {
307 if (len & 1)
308 return BIR_UNSUPPORTED;
309 len >>= 1;
310 }
311 if (len > n)
312 return BIR_RLERROR;
313 if (rdbyte(val, br) == EOF)
314 return BIR_TRUNCATED;
315 n -= len;
316 while (len--)
317 *sp++ = val;
318 continue;
319 }
320 switch (rdbyte(len, br)) {
321 case EOF:
322 return BIR_TRUNCATED;
323 case 0: /* end of line */
324 while (n--)
325 *sp++ = 0;
326 /* leaves n == -1 as flag for test after loop */
327 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 if (!n && (rdbyte(n, br) != 0 || (rdbyte(n, br) != 0 &&
352 (n != 1 || br->yscan != br->hdr->height-1))))
353 return BIR_RLERROR;
354 if (br->seek != NULL) /* record next scanline position */
355 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 if (br->hdr->compr == BI_UNCOMPR ||
379 br->hdr->compr == BI_BITFIELDS) {
380 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 BMPdecodePixel(int i, const BMPReader *br)
405 {
406 static const uint32 std16mask[3] = {0x7c00, 0x3e0, 0x1f};
407 static const RGBquad black = {0, 0, 0, 0};
408 const uint32 *mask;
409 const uint8 *pp;
410 uint32 pval, v;
411 RGBquad cval;
412
413 if (((br == NULL) | (i < 0)) || i >= br->hdr->width)
414 return black;
415
416 cval.padding = 0;
417
418 switch (br->hdr->bpp) {
419 case 24:
420 pp = br->scanline + 3*i;
421 cval.b = *pp++;
422 cval.g = *pp++;
423 cval.r = *pp;
424 return cval;
425 case 32:
426 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 case 8:
446 return br->hdr->palette[br->scanline[i]];
447 case 1:
448 return br->hdr->palette[br->scanline[i>>3]>>((7-i)&7) & 1];
449 case 4:
450 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 }
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 /* allocate color-mapped header (defaults to minimal grayscale) */
527 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 hdr->compr = BI_UNCOMPR; /* compression needs seek */
554 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 ++(bw)->fpos > (bw)->flen ? \
568 ((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 wrbyte(i>>24 & 0xff, bw);
587 }
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 if (bw->seek == NULL) {
604 if (pos < bw->fpos)
605 return BIR_SEEKERR;
606 while (bw->fpos < pos)
607 wrbyte(0, bw);
608 return BIR_OK;
609 }
610 if ((*bw->seek)(pos, bw->c_data) != 0)
611 return BIR_SEEKERR;
612 bw->fpos = pos;
613 if (pos > bw->flen)
614 bw->flen = pos;
615 return BIR_OK;
616 }
617
618 /* open BMP stream for writing */
619 BMPWriter *
620 BMPopenWriter(void (*cput)(int, void *), int (*seek)(uint32, void *),
621 void *c_data, BMPHeader *hdr)
622 {
623 BMPWriter *bw;
624 uint32 hdrSiz, palSiz, scanSiz, bmSiz;
625 /* check arguments */
626 if (cput == NULL)
627 return NULL;
628 if (!BMPheaderOK(hdr))
629 return NULL;
630 if ((hdr->bpp == 16) | (hdr->compr == BI_RLE4))
631 return NULL; /* unsupported */
632 /* no seek means we may have the wrong file length, but most app's don't care
633 if (seek == NULL && ((hdr->compr == BI_RLE8) | (hdr->compr == BI_RLE4)))
634 return NULL;
635 */
636 /* compute sizes */
637 hdrSiz = 2 + 6*4 + 2*2 + 6*4;
638 if (hdr->compr == BI_BITFIELDS)
639 hdrSiz += sizeof(uint32)*3;
640 palSiz = sizeof(RGBquad)*BMPpalLen(hdr);
641 scanSiz = getScanSiz(hdr);
642 bmSiz = hdr->height*scanSiz; /* wrong if compressed */
643 /* initialize writer */
644 bw = (BMPWriter *)malloc(sizeof(BMPWriter));
645 if (bw == NULL)
646 return NULL;
647 bw->hdr = hdr;
648 bw->yscan = 0;
649 bw->scanline = (uint8 *)calloc(scanSiz, sizeof(uint8));
650 if (bw->scanline == NULL) {
651 free((void *)bw);
652 return NULL;
653 }
654 bw->fbmp = hdrSiz + palSiz + hdr->infoSiz;
655 bw->fpos = bw->flen = 0;
656 bw->cput = cput;
657 bw->seek = seek;
658 bw->c_data = c_data;
659 /* write out header */
660 wrbyte('B', bw); wrbyte('M', bw); /* magic number */
661 wrint32(bw->fbmp + bmSiz, bw); /* file size */
662 wrint32(0, bw); /* reserved word */
663 wrint32(bw->fbmp, bw); /* offset to bitmap */
664 wrint32(hdrSiz - bw->fpos, bw); /* info header size */
665 wrint32(hdr->width, bw); /* bitmap width */
666 if (hdr->yIsDown) /* bitmap height */
667 wrint32(-hdr->height, bw);
668 else
669 wrint32(hdr->height, bw);
670 wruint16(1, bw); /* number of planes */
671 wruint16(hdr->bpp, bw); /* bits per pixel */
672 wrint32(hdr->compr, bw); /* compression mode */
673 wrint32(bmSiz, bw); /* bitmap size */
674 wrint32(hdr->hRes, bw); /* horizontal resolution */
675 wrint32(hdr->vRes, bw); /* vertical resolution */
676 wrint32(hdr->nColors, bw); /* # colors used */
677 wrint32(hdr->impColors, bw); /* # important colors */
678 /* write out color palette */
679 wrbytes((char *)hdr->palette, palSiz, bw);
680 /* write add'l information */
681 wrbytes(BMPinfo(hdr), hdr->infoSiz, bw);
682 #ifndef NDEBUG
683 if (bw->fpos != bw->fbmp) {
684 fputs("Coding error 1 in BMPopenWriter\n", stderr);
685 exit(1);
686 }
687 #endif
688 return bw;
689 }
690
691 /* find position of next run of 5 or more identical bytes, or 255 if none */
692 static int
693 findNextRun(const int8 *bp, int len)
694 {
695 int pos, cnt;
696 /* look for run */
697 for (pos = 0; (len > 0) & (pos < 255); pos++, bp++, len--) {
698 if (len < 5) /* no hope left? */
699 continue;
700 cnt = 1; /* else let's try it */
701 while (bp[cnt] == bp[0])
702 if (++cnt >= 5)
703 return pos; /* long enough */
704 }
705 return pos; /* didn't find any */
706 }
707
708 /* write the current scanline */
709 int
710 BMPwriteScanline(BMPWriter *bw)
711 {
712 const int8 *sp;
713 int n;
714
715 if (bw->yscan >= bw->hdr->height)
716 return BIR_EOF;
717 /* writing uncompressed? */
718 if (bw->hdr->compr == BI_UNCOMPR || bw->hdr->compr == BI_BITFIELDS) {
719 uint32 scanSiz = getScanSiz(bw->hdr);
720 uint32 slpos = bw->fbmp + bw->yscan*scanSiz;
721 if (wrseek(slpos, bw) != BIR_OK)
722 return BIR_SEEKERR;
723 wrbytes((char *)bw->scanline, scanSiz, bw);
724 bw->yscan++;
725 return BIR_OK;
726 }
727 /*
728 * RLE8 Encoding
729 *
730 * See the notes in BMPreadScanline() on this encoding. Needless
731 * to say, we avoid the nuttier aspects of this specification.
732 * We also assume that every scanline ends in a line break
733 * (0x0000) except for the last, which ends in a bitmap break
734 * (0x0001). We don't support RLE4 at all; it's too awkward.
735 */
736 sp = bw->scanline;
737 n = bw->hdr->width;
738 while (n > 0) {
739 int cnt, val;
740
741 cnt = findNextRun(sp, n); /* 0-255 < n */
742 if (cnt >= 3) { /* output non-run */
743 int skipOdd = cnt & 1;
744 wrbyte(0, bw);
745 wrbyte(cnt, bw);
746 n -= cnt;
747 while (cnt--)
748 wrbyte(*sp++, bw);
749 if (skipOdd)
750 wrbyte(0, bw);
751 }
752 if (n <= 0) /* was that it? */
753 break;
754 val = *sp; /* output run */
755 for (cnt = 1; cnt < 255; cnt++)
756 if (!--n | *++sp != val)
757 break;
758 wrbyte(cnt, bw);
759 wrbyte(val, bw);
760 }
761 bw->yscan++; /* write line break or EOD */
762 if (bw->yscan == bw->hdr->height) {
763 wrbyte(0, bw); wrbyte(1, bw); /* end of bitmap marker */
764 if (bw->seek == NULL || (*bw->seek)(2, bw->c_data) != 0)
765 return BIR_OK; /* no one may care */
766 bw->fpos = 2;
767 wrint32(bw->flen-bw->fbmp, bw); /* correct bitmap length */
768 } else {
769 wrbyte(0, bw); wrbyte(0, bw); /* end of line marker */
770 }
771 return BIR_OK;
772 }
773
774 /* free BMP writer resources */
775 void
776 BMPfreeWriter(BMPWriter *bw)
777 {
778 if (bw == NULL)
779 return;
780 free((void *)bw->hdr);
781 free((void *)bw->scanline);
782 free((void *)bw);
783 }