Back to index

radiance  4R0+20100331
bmpfile.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char RCSid[] = "$Id: bmpfile.c,v 2.15 2005/05/10 01:08:43 greg Exp $";
00003 #endif
00004 /*
00005  *  Windows and OS/2 BMP file support
00006  */
00007 
00008 #include <stdio.h>
00009 #include <stdlib.h>
00010 #include <string.h>
00011 #include "bmpfile.h"
00012 
00013 #ifdef getc_unlocked        /* avoid horrendous overhead of flockfile */
00014 #undef getc
00015 #undef putc
00016 #define getc    getc_unlocked
00017 #define putc    putc_unlocked
00018 #endif
00019 
00020 /* get corresponding error message */
00021 const char *
00022 BMPerrorMessage(int ec)
00023 {
00024        switch (ec) {
00025        case BIR_OK:
00026               return "No error";
00027        case BIR_EOF:
00028               return "End of BMP image";
00029        case BIR_TRUNCATED:
00030               return "Truncated BMP image";
00031        case BIR_UNSUPPORTED:
00032               return "Unsupported BMP feature";
00033        case BIR_RLERROR:
00034               return "BMP runlength encoding error";
00035        case BIR_SEEKERR:
00036               return "BMP seek error";
00037        }
00038        return "Unknown BMP error";
00039 }
00040 
00041 /* check than header is sensible */
00042 static int
00043 BMPheaderOK(const BMPHeader *hdr)
00044 {
00045        if (!hdr)
00046               return 0;
00047        if ((hdr->width <= 0) | (hdr->height <= 0))
00048               return 0;
00049        switch (hdr->bpp) {         /* check compression */
00050        case 1:
00051        case 24:
00052               if (hdr->compr != BI_UNCOMPR)
00053                      return 0;
00054               break;
00055        case 16:
00056        case 32:
00057               if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_BITFIELDS))
00058                      return 0;
00059               break;
00060        case 4:
00061               if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE4))
00062                      return 0;
00063               break;
00064        case 8:
00065               if ((hdr->compr != BI_UNCOMPR) & (hdr->compr != BI_RLE8))
00066                      return 0;
00067               break;
00068        default:
00069               return 0;
00070        }
00071        if (hdr->compr == BI_BITFIELDS && (BMPbitField(hdr)[0] &
00072                             BMPbitField(hdr)[1] & BMPbitField(hdr)[2]))
00073               return 0;
00074        if (hdr->bpp > 8) {
00075               if (hdr->nColors != 0)
00076                      return 0;
00077        } else {
00078               if ((hdr->nColors < 0) | (hdr->nColors > 1<<hdr->bpp))
00079                      return 0;
00080               if ((hdr->impColors < 0) | (hdr->impColors > hdr->nColors))
00081                      return 0;
00082        }
00083        return 1;
00084 }
00085 
00086                                    /* compute uncompressed scan size */
00087 #define getScanSiz(h)   ( ((((h)->bpp*(h)->width+7) >>3) + 3) & ~03 )
00088 
00089                                    /* get next byte from reader */
00090 #define rdbyte(c,br)    ((br)->fpos += (c=(*(br)->cget)((br)->c_data))!=EOF, c)
00091 
00092 /* read n bytes */
00093 static int
00094 rdbytes(char *bp, uint32 n, BMPReader *br)
00095 {
00096        int     c;
00097 
00098        while (n--) {
00099               if (rdbyte(c, br) == EOF)
00100                      return BIR_TRUNCATED;
00101               *bp++ = c;
00102        }
00103        return BIR_OK;
00104 }
00105 
00106 /* read 32-bit integer in littlendian order */
00107 static int32
00108 rdint32(BMPReader *br)
00109 {
00110        int32   i;
00111        int     c;
00112 
00113        i = rdbyte(c, br);
00114        i |= rdbyte(c, br) << 8;
00115        i |= rdbyte(c, br) << 16;
00116        i |= rdbyte(c, br) << 24;
00117        return i;                   /* -1 on EOF */
00118 }
00119 
00120 /* read 16-bit unsigned integer in littlendian order */
00121 static int
00122 rduint16(BMPReader *br)
00123 {
00124        int     i;
00125        int     c;
00126        
00127        i = rdbyte(c, br);
00128        i |= rdbyte(c, br) << 8;
00129        return i;                   /* -1 on EOF */
00130 }
00131 
00132 /* seek on reader or return 0 (BIR_OK) on success */
00133 static int
00134 rdseek(uint32 pos, BMPReader *br)
00135 {
00136        if (pos == br->fpos)
00137               return BIR_OK;
00138        if (br->seek == NULL || (*br->seek)(pos, br->c_data) != 0)
00139               return BIR_SEEKERR;
00140        br->fpos = pos;
00141        return BIR_OK;
00142 }
00143 
00144 /* open BMP stream for reading and get first scanline */
00145 BMPReader *
00146 BMPopenReader(int (*cget)(void *), int (*seek)(uint32, void *), void *c_data)
00147 {
00148        BMPReader       *br;
00149        uint32        bmPos, hdrSiz, palSiz;
00150        int           magic[2];            /* check magic number */
00151 
00152        if (cget == NULL)
00153               return NULL;
00154        magic[0] = (*cget)(c_data);
00155        if (magic[0] != 'B')
00156               return NULL;
00157        magic[1] = (*cget)(c_data);
00158        if (magic[1] != 'M' && magic[1] != 'A')
00159               return NULL;
00160        br = (BMPReader *)calloc(1, sizeof(BMPReader));
00161        if (br == NULL)
00162               return NULL;
00163        br->cget = cget;
00164        br->seek = seek;
00165        br->c_data = c_data;
00166        br->hdr = (BMPHeader *)malloc(sizeof(BMPHeader));
00167        if (br->hdr == NULL)
00168               goto err;
00169        br->fpos = 2;
00170                                    /* read & verify file header */
00171        (void)rdint32(br);                 /* file size */
00172        (void)rdint32(br);                 /* reserved word */
00173        bmPos = rdint32(br);               /* offset to bitmap */
00174        hdrSiz = 2 + 3*4 + rdint32(br);           /* header size */
00175        if (hdrSiz < 2 + 6*4 + 2*2 + 6*4)
00176               goto err;
00177        br->hdr->width = rdint32(br);             /* bitmap width */
00178        br->hdr->height = rdint32(br);            /* bitmap height */
00179        if (((br->hdr->width <= 0) | (br->hdr->height == 0)))
00180               goto err;
00181        if ((br->hdr->yIsDown = br->hdr->height < 0))
00182               br->hdr->height = -br->hdr->height;
00183        if (rduint16(br) != 1)                    /* number of planes */
00184               goto err;
00185        br->hdr->bpp = rduint16(br);              /* bits per pixel */
00186        br->hdr->compr = rdint32(br);             /* compression mode */
00187        (void)rdint32(br);                 /* bitmap size */
00188        br->hdr->hRes = rdint32(br);              /* horizontal resolution */
00189        br->hdr->vRes = rdint32(br);              /* vertical resolution */
00190        br->hdr->nColors = rdint32(br);           /* # colors used */
00191        if (!br->hdr->nColors && br->hdr->bpp <= 8)
00192               br->hdr->nColors = 1<<br->hdr->bpp;
00193        br->hdr->impColors = rdint32(br);       /* # important colors */
00194        if (br->hdr->impColors < 0)
00195               goto err;                   /* catch premature EOF */
00196        if (!BMPheaderOK(br->hdr))
00197               goto err;
00198        palSiz = sizeof(RGBquad)*br->hdr->nColors;
00199        if (br->hdr->impColors <= 0)
00200               br->hdr->impColors = br->hdr->nColors;
00201                                           /* extend header */
00202        if (bmPos < hdrSiz + palSiz)
00203               goto err;
00204        br->hdr->infoSiz = bmPos - (hdrSiz + palSiz);
00205        if (br->hdr->nColors > 0 || br->hdr->infoSiz > 0) {
00206               br->hdr = (BMPHeader *)realloc((void *)br->hdr,
00207                                    sizeof(BMPHeader) +
00208                                    palSiz + br->hdr->infoSiz);
00209               if (br->hdr == NULL)
00210                      goto err;
00211        }
00212                                           /* read colors or fields */
00213        if (br->hdr->compr == BI_BITFIELDS) {
00214               BMPbitField(br->hdr)[0] = (uint32)rdint32(br);
00215               BMPbitField(br->hdr)[1] = (uint32)rdint32(br);
00216               BMPbitField(br->hdr)[2] = (uint32)rdint32(br);
00217        } else if (rdbytes((char *)br->hdr->palette, palSiz, br) != BIR_OK)
00218               goto err;
00219                                           /* read add'l information */
00220        if (rdbytes(BMPinfo(br->hdr), br->hdr->infoSiz, br) != BIR_OK)
00221               goto err;
00222                                           /* read first scanline */
00223        br->scanline = (uint8 *)calloc(getScanSiz(br->hdr), sizeof(uint8));
00224        if (br->scanline == NULL)
00225               goto err;
00226        br->yscan = -1;
00227        if (seek != NULL && ((br->hdr->compr == BI_RLE8) |
00228                                    (br->hdr->compr == BI_RLE4))) {
00229               BMPReader       *newbr = (BMPReader *)realloc((void *)br,
00230                                           sizeof(BMPReader) +
00231                                           sizeof(br->scanpos[0]) *
00232                                                  br->hdr->height);
00233               if (newbr == NULL)
00234                      goto err;
00235               br = newbr;
00236               memset((void *)(br->scanpos + 1), 0,
00237                             sizeof(br->scanpos[0])*br->hdr->height);
00238        }
00239        br->scanpos[0] = br->fpos;
00240        if (BMPreadScanline(br) == BIR_OK)
00241               return br;
00242 err:
00243        if (br->hdr != NULL)
00244               free((void *)br->hdr);
00245        if (br->scanline != NULL)
00246               free((void *)br->scanline);
00247        free((void *)br);
00248        return NULL;
00249 }
00250 
00251 /* determine if image is grayscale */
00252 int
00253 BMPisGrayscale(const BMPHeader *hdr)
00254 {
00255        const RGBquad   *rgbp;
00256        int           n;
00257 
00258        if (hdr == NULL)
00259               return -1;
00260        if (hdr->bpp > 8)           /* assume they had a reason for it */
00261               return 0;
00262        for (rgbp = hdr->palette, n = hdr->impColors; n-- > 0; rgbp++)
00263               if ((rgbp->r != rgbp->g) | (rgbp->g != rgbp->b))
00264                      return 0;
00265        return 1;                   /* all colors neutral in map */
00266 }
00267 
00268 /* read and decode next BMP scanline */
00269 int
00270 BMPreadScanline(BMPReader *br)
00271 {
00272        int     n;
00273        int8    *sp;
00274 
00275        if (br->yscan + 1 >= br->hdr->height)
00276               return BIR_EOF;
00277        br->yscan++;                /* prepare to read */
00278        n = getScanSiz(br->hdr);    /* reading uncompressed data? */
00279        if (br->hdr->compr == BI_UNCOMPR || br->hdr->compr == BI_BITFIELDS)
00280               return rdbytes((char *)br->scanline, n, br);
00281        /*
00282         * RLE4/RLE8 Decoding
00283         *
00284         * Certain aspects of this scheme are completely insane, so
00285         * we don't support them.  Fortunately, they rarely appear.
00286         * One is the mid-file EOD (0x0001) and another is the ill-conceived
00287         * "delta" (0x0002), which is like a "goto" statement for bitmaps.
00288         * Whoever thought this up should be wrestled to the ground and told
00289         * why it's impossible to support such a scheme in any reasonable way.
00290         * Also, RLE4 mode allows runs to stop halfway through a byte,
00291         * which is likewise uncodeable, so we don't even try.
00292         * Finally, the scanline break is ambiguous -- we assume here that
00293         * it is required at the end of each scanline, though I haven't
00294         * found anywhere this is written.  Otherwise, we would read to
00295         * the end of the scanline, assuming the next bit of data belongs
00296         * the following scan.  If a break follows the last pixel, as it
00297         * seems to in the files I've tested out of Photoshop, you end up
00298         * painting every other line black.  Also, I assume any skipped
00299         * pixels are painted with color 0, which is often black.  Nowhere
00300         * is it specified what we should assume for missing pixels.  This
00301         * is undoubtedly the most brain-dead format I've ever encountered.
00302         */
00303        sp = (int8 *)br->scanline;
00304        n = br->hdr->width;
00305        if (br->hdr->compr == BI_RLE4)
00306               n = (n + 1) >> 1;
00307        while (n > 0) {
00308               int     skipOdd, len, val;
00309 
00310               if (rdbyte(len, br) == EOF)
00311                      return BIR_TRUNCATED;
00312               if (len > 0) {              /* got a run */
00313                      if (br->hdr->compr == BI_RLE4) {
00314                             if (len & 1)
00315                                    return BIR_UNSUPPORTED;
00316                             len >>= 1;
00317                      }
00318                      if (len > n)
00319                             return BIR_RLERROR;
00320                      if (rdbyte(val, br) == EOF)
00321                             return BIR_TRUNCATED;
00322                      n -= len;
00323                      while (len--)
00324                             *sp++ = val;
00325                      continue;
00326               }
00327                                    /* check for escape */
00328               switch (rdbyte(len, br)) {
00329               case EOF:
00330                      return BIR_TRUNCATED;
00331               case 0:                     /* end of line */
00332                      while (n--)
00333                             *sp++ = 0;
00334                      /* leaves n == -1 as flag for test after loop */
00335                      continue;
00336               case 1:                     /* end of bitmap */
00337               case 2:                     /* delta */
00338                      return BIR_UNSUPPORTED;
00339               }
00340                                    /* absolute mode */
00341               if (br->hdr->compr == BI_RLE4) {
00342                      if (len & 1)
00343                             return BIR_UNSUPPORTED;
00344                      len >>= 1;
00345               }
00346               skipOdd = len & 1;
00347               if (len > n)
00348                      return BIR_RLERROR;
00349               n -= len;
00350               while (len--) {
00351                      if (rdbyte(val, br) == EOF)
00352                             return BIR_TRUNCATED;
00353                      *sp++ = val;
00354               }
00355               if (skipOdd && rdbyte(val, br) == EOF)
00356                      return BIR_TRUNCATED;
00357        }
00358                                    /* verify break at end of line */
00359        if (!n && (rdbyte(n, br) != 0 || (rdbyte(n, br) != 0 &&
00360                             (n != 1 || br->yscan != br->hdr->height-1))))
00361               return BIR_RLERROR;
00362        if (br->seek != NULL)              /* record next scanline position */
00363               br->scanpos[br->yscan + 1] = br->fpos;
00364        return BIR_OK;
00365 }
00366 
00367 /* read a specific scanline */
00368 int
00369 BMPseekScanline(int y, BMPReader *br)
00370 {
00371        int     rv;
00372                                    /* check arguments */
00373        if (br == NULL)
00374               return BIR_EOF;
00375        if (y < 0)
00376               return BIR_SEEKERR;
00377        if (y >= br->hdr->height)
00378               return BIR_EOF;
00379                                    /* already read? */
00380        if (y == br->yscan)
00381               return BIR_OK;
00382                                    /* shall we seek? */
00383        if (y != br->yscan + 1 && br->seek != NULL) {
00384               int     yseek;
00385               uint32  seekp;
00386               if (br->hdr->compr == BI_UNCOMPR ||
00387                                    br->hdr->compr == BI_BITFIELDS) {
00388                      yseek = y;
00389                      seekp = br->scanpos[0] + y*getScanSiz(br->hdr);
00390               } else {
00391                      yseek = br->yscan + 1;
00392                      while (yseek < y && br->scanpos[yseek+1] != 0)
00393                             ++yseek;
00394                      if (y < yseek && br->scanpos[yseek=y] == 0)
00395                             return BIR_SEEKERR;
00396                      seekp = br->scanpos[yseek];
00397               }
00398               if ((rv = rdseek(seekp, br)) != BIR_OK)
00399                      return rv;
00400               br->yscan = yseek - 1;
00401        } else if (y < br->yscan)       /* else we can't back up */
00402               return BIR_SEEKERR;
00403                                    /* read until we get there */
00404        while (br->yscan < y)
00405               if ((rv = BMPreadScanline(br)) != BIR_OK)
00406                      return rv;
00407        return BIR_OK;
00408 }
00409 
00410 /* get ith pixel from last scanline */
00411 RGBquad
00412 BMPdecodePixel(int i, const BMPReader *br)
00413 {
00414        static const uint32     std16mask[3] = {0x7c00, 0x3e0, 0x1f};
00415        static const RGBquad    black = {0, 0, 0, 0};
00416        const uint32         *mask;
00417        const uint8          *pp;
00418        uint32               pval, v;
00419        RGBquad                     cval;
00420        
00421        if (((br == NULL) | (i < 0)) || i >= br->hdr->width)
00422               return black;
00423 
00424        cval.padding = 0;
00425 
00426        switch (br->hdr->bpp) {
00427        case 24:
00428               pp = br->scanline + 3*i;
00429               cval.b = *pp++;
00430               cval.g = *pp++;
00431               cval.r = *pp;
00432               return cval;
00433        case 32:
00434               if (br->hdr->compr == BI_UNCOMPR)
00435                      return ((RGBquad *)br->scanline)[i];
00436                                           /* convert bit fields */
00437               pp = br->scanline + 4*i;
00438               pval = *pp++;
00439               pval |= *pp++ << 8;
00440               pval |= *pp++ << 16;
00441               pval |= *pp << 24;
00442               mask = BMPbitField(br->hdr);
00443               v = pval & mask[0];
00444               while (v & ~0xff) v >>= 8;
00445               cval.r = v;
00446               v = pval & mask[1];
00447               while (v & ~0xff) v >>= 8;
00448               cval.g = v;
00449               v = pval & mask[2];
00450               while (v & ~0xff) v >>= 8;
00451               cval.b = v;
00452               return cval;
00453        case 8:
00454               return br->hdr->palette[br->scanline[i]];
00455        case 1:
00456               return br->hdr->palette[br->scanline[i>>3]>>((7-i)&7) & 1];
00457        case 4:
00458               return br->hdr->palette[br->scanline[i>>1]>>(i&1?4:0) & 0xf];
00459        case 16:
00460               pp = br->scanline + 2*i;
00461               pval = *pp++;
00462               pval |= *pp++ << 8;
00463               mask = std16mask;
00464               if (br->hdr->compr == BI_BITFIELDS)
00465                      mask = BMPbitField(br->hdr);
00466               cval.r = ((pval & mask[0]) << 8) / (mask[0] + 1);
00467               cval.g = ((pval & mask[1]) << 8) / (mask[1] + 1);
00468               cval.b = ((pval & mask[2]) << 8) / (mask[2] + 1);
00469               return cval;
00470        }
00471        return black;                      /* should never happen */
00472 }
00473 
00474 /* free BMP reader resources */
00475 void
00476 BMPfreeReader(BMPReader *br)
00477 {
00478        if (br == NULL)
00479               return;
00480        free((void *)br->hdr);
00481        free((void *)br->scanline);
00482        free((void *)br);
00483 }
00484 
00485 /* stdio getc() callback */
00486 int
00487 stdio_getc(void *p)
00488 {
00489        if (!p)
00490               return EOF;
00491        return getc((FILE *)p);
00492 }
00493 
00494 /* stdio putc() callback */
00495 void
00496 stdio_putc(int c, void *p)
00497 {
00498        if (p)
00499               putc(c, (FILE *)p);
00500 }
00501 
00502 /* stdio fseek() callback */
00503 int
00504 stdio_fseek(uint32 pos, void *p)
00505 {
00506        if (!p)
00507               return -1;
00508        return fseek((FILE *)p, (long)pos, 0);
00509 }
00510 
00511 /* allocate uncompressed (24-bit) RGB header */
00512 BMPHeader *
00513 BMPtruecolorHeader(int xr, int yr, int infolen)
00514 {
00515        BMPHeader       *hdr;
00516        
00517        if (xr <= 0 || yr <= 0 || infolen < 0)
00518               return NULL;
00519        hdr = (BMPHeader *)malloc(sizeof(BMPHeader) - sizeof(hdr->palette) +
00520                                    infolen);
00521        if (hdr == NULL)
00522               return NULL;
00523        hdr->width = xr;
00524        hdr->height = yr;
00525        hdr->yIsDown = 0;                  /* default to upwards order */
00526        hdr->bpp = 24;
00527        hdr->compr = BI_UNCOMPR;
00528        hdr->hRes = hdr->vRes = 2835;             /* default to 72 ppi */
00529        hdr->nColors = hdr->impColors = 0;
00530        hdr->infoSiz = infolen;
00531        return hdr;
00532 }
00533 
00534 /* allocate color-mapped header (defaults to minimal grayscale) */
00535 BMPHeader *
00536 BMPmappedHeader(int xr, int yr, int infolen, int ncolors)
00537 {
00538        int           n;
00539        BMPHeader       *hdr;
00540        
00541        if (xr <= 0 || yr <= 0 || infolen < 0 || ncolors < 2)
00542               return NULL;
00543        if (ncolors <= 2)
00544               n = 1;
00545        else if (ncolors <= 16)
00546               n = 4;
00547        else if (ncolors <= 256)
00548               n = 8;
00549        else
00550               return NULL;
00551        hdr = (BMPHeader *)malloc(sizeof(BMPHeader) +
00552                                    sizeof(RGBquad)*(1<<n) -
00553                                    sizeof(hdr->palette) +
00554                                    infolen);
00555        if (hdr == NULL)
00556               return NULL;
00557        hdr->width = xr;
00558        hdr->height = yr;
00559        hdr->yIsDown = 0;                  /* default to upwards order */
00560        hdr->bpp = n;
00561        hdr->compr = BI_UNCOMPR;           /* compression needs seek */
00562        hdr->hRes = hdr->vRes = 2835;             /* default to 72 ppi */
00563        hdr->nColors = ncolors;
00564        hdr->impColors = 0;                /* says all colors important */
00565        hdr->infoSiz = infolen;
00566        memset((void *)hdr->palette, 0, sizeof(RGBquad)*(1<<n) + infolen);
00567        for (n = ncolors; n--; )
00568               hdr->palette[n].r = hdr->palette[n].g =
00569                      hdr->palette[n].b = n*255/(ncolors-1);
00570        return hdr;
00571 }
00572 
00573                                    /* put byte to writer */
00574 #define wrbyte(c,bw)    ( (*(bw)->cput)(c,(bw)->c_data), \
00575                             ++(bw)->fpos > (bw)->flen ? \
00576                                    ((bw)->flen = (bw)->fpos) : \
00577                                    (bw)->fpos )
00578 
00579 /* write out a string of bytes */
00580 static void
00581 wrbytes(char *bp, uint32 n, BMPWriter *bw)
00582 {
00583        while (n--)
00584               wrbyte(*bp++, bw);
00585 }
00586 
00587 /* write 32-bit integer in littlendian order */
00588 static void
00589 wrint32(int32 i, BMPWriter *bw)
00590 {
00591        wrbyte(i& 0xff, bw);
00592        wrbyte(i>>8 & 0xff, bw);
00593        wrbyte(i>>16 & 0xff, bw);
00594        wrbyte(i>>24 & 0xff, bw);
00595 }
00596 
00597 /* write 16-bit unsigned integer in littlendian order */
00598 static void
00599 wruint16(uint16 ui, BMPWriter *bw)
00600 {
00601        wrbyte(ui & 0xff, bw);
00602        wrbyte(ui>>8 & 0xff, bw);
00603 }
00604 
00605 /* seek to the specified file position, returning 0 (BIR_OK) on success */
00606 static int
00607 wrseek(uint32 pos, BMPWriter *bw)
00608 {
00609        if (pos == bw->fpos)
00610               return BIR_OK;
00611        if (bw->seek == NULL)
00612               return BIR_SEEKERR;
00613        if ((*bw->seek)(pos, bw->c_data) != 0)
00614               return BIR_SEEKERR;
00615        bw->fpos = pos;
00616        if (pos > bw->flen)
00617               bw->flen = pos;
00618        return BIR_OK;
00619 }
00620 
00621 /* open BMP stream for writing */
00622 BMPWriter *
00623 BMPopenWriter(void (*cput)(int, void *), int (*seek)(uint32, void *),
00624                      void *c_data, BMPHeader *hdr)
00625 {
00626        BMPWriter       *bw;
00627        uint32        hdrSiz, palSiz, scanSiz, bmSiz;
00628                                           /* check arguments */
00629        if (cput == NULL)
00630               return NULL;
00631        if (!BMPheaderOK(hdr))
00632               return NULL;
00633        if ((hdr->bpp == 16) | (hdr->compr == BI_RLE4))
00634               return NULL;                /* unsupported */
00635 /* no seek means we may have the wrong file length, but most app's don't care
00636        if (seek == NULL && ((hdr->compr == BI_RLE8) | (hdr->compr == BI_RLE4)))
00637               return NULL;
00638 */
00639                                           /* compute sizes */
00640        hdrSiz = 2 + 6*4 + 2*2 + 6*4;
00641        if (hdr->compr == BI_BITFIELDS)
00642               hdrSiz += sizeof(uint32)*3;
00643        palSiz = sizeof(RGBquad)*hdr->nColors;
00644        scanSiz = getScanSiz(hdr);
00645        bmSiz = hdr->height*scanSiz;              /* wrong if compressed */
00646                                           /* initialize writer */
00647        bw = (BMPWriter *)malloc(sizeof(BMPWriter));
00648        if (bw == NULL)
00649               return NULL;
00650        bw->hdr = hdr;
00651        bw->yscan = 0;
00652        bw->scanline = (uint8 *)calloc(scanSiz, sizeof(uint8));
00653        if (bw->scanline == NULL) {
00654               free((void *)bw);
00655               return NULL;
00656        }
00657        bw->fbmp = hdrSiz + palSiz + hdr->infoSiz;
00658        bw->fpos = bw->flen = 0;
00659        bw->cput = cput;
00660        bw->seek = seek;
00661        bw->c_data = c_data;
00662                                           /* write out header */
00663        wrbyte('B', bw); wrbyte('M', bw);       /* magic number */
00664        wrint32(bw->fbmp + bmSiz, bw);            /* file size */
00665        wrint32(0, bw);                           /* reserved word */
00666        wrint32(bw->fbmp, bw);                    /* offset to bitmap */
00667        wrint32(hdrSiz - bw->fpos, bw);           /* info header size */
00668        wrint32(hdr->width, bw);           /* bitmap width */
00669        if (hdr->yIsDown)                  /* bitmap height */
00670               wrint32(-hdr->height, bw);
00671        else
00672               wrint32(hdr->height, bw);
00673        wruint16(1, bw);                   /* number of planes */
00674        wruint16(hdr->bpp, bw);                   /* bits per pixel */
00675        wrint32(hdr->compr, bw);           /* compression mode */
00676        wrint32(bmSiz, bw);                /* bitmap size */
00677        wrint32(hdr->hRes, bw);                   /* horizontal resolution */
00678        wrint32(hdr->vRes, bw);                   /* vertical resolution */
00679        wrint32(hdr->nColors, bw);         /* # colors used */
00680        wrint32(hdr->impColors, bw);              /* # important colors */
00681                                           /* write out color palette */
00682        wrbytes((char *)hdr->palette, palSiz, bw);
00683                                           /* write add'l information */
00684        wrbytes(BMPinfo(hdr), hdr->infoSiz, bw);
00685 #ifndef NDEBUG
00686        if (bw->fpos != bw->fbmp) {
00687               fputs("Coding error 1 in BMPopenWriter\n", stderr);
00688               exit(1);
00689        }
00690 #endif
00691        return bw;
00692 }
00693 
00694 /* find position of next run of 5 or more identical bytes, or 255 if none */
00695 static int
00696 findNextRun(const int8 *bp, int len)
00697 {
00698        int     pos, cnt;
00699                                           /* look for run */
00700        for (pos = 0; (len > 0) & (pos < 255); pos++, bp++, len--) {
00701               if (len < 5)                /* no hope left? */
00702                      continue;
00703               cnt = 1;                    /* else let's try it */
00704               while (bp[cnt] == bp[0])
00705                      if (++cnt >= 5)
00706                             return pos;     /* long enough */
00707        }
00708        return pos;                        /* didn't find any */
00709 }
00710                             
00711 /* write the current scanline */
00712 int
00713 BMPwriteScanline(BMPWriter *bw)
00714 {
00715        const int8      *sp;
00716        int           n;
00717 
00718        if (bw->yscan >= bw->hdr->height)
00719               return BIR_EOF;
00720                                           /* writing uncompressed? */
00721        if (bw->hdr->compr == BI_UNCOMPR || bw->hdr->compr == BI_BITFIELDS) {
00722               uint32  scanSiz = getScanSiz(bw->hdr);
00723               uint32  slpos = bw->fbmp + bw->yscan*scanSiz;
00724               if (wrseek(slpos, bw) != BIR_OK)
00725                      return BIR_SEEKERR;
00726               wrbytes((char *)bw->scanline, scanSiz, bw);
00727               bw->yscan++;
00728               return BIR_OK;
00729        }
00730        /*
00731         * RLE8 Encoding
00732         *
00733         * See the notes in BMPreadScanline() on this encoding.  Needless
00734         * to say, we avoid the nuttier aspects of this specification.
00735         * We also assume that every scanline ends in a line break
00736         * (0x0000) except for the last, which ends in a bitmap break
00737         * (0x0001).  We don't support RLE4 at all; it's too awkward.
00738         */
00739        sp = (const int8 *)bw->scanline;
00740        n = bw->hdr->width;
00741        while (n > 0) {
00742               int     cnt, val;
00743               cnt = findNextRun(sp, n);       /* 0-255 < n */
00744               if (cnt >= 3) {                    /* output absolute */
00745                      int     skipOdd = cnt & 1;
00746                      wrbyte(0, bw);
00747                      wrbyte(cnt, bw);
00748                      n -= cnt;
00749                      while (cnt--)
00750                             wrbyte(*sp++, bw);
00751                      if (skipOdd)
00752                             wrbyte(0, bw);
00753               }
00754               if (n <= 0)                 /* was that it? */
00755                      break;
00756               val = *sp;                  /* output run */
00757               for (cnt = 1; --n && cnt < 255; cnt++)
00758                      if (*++sp != val)
00759                             break;
00760               wrbyte(cnt, bw);
00761               wrbyte(val, bw);
00762        }
00763        bw->yscan++;                       /* write line break or EOD */
00764        if (bw->yscan == bw->hdr->height) {
00765               wrbyte(0, bw); wrbyte(1, bw);   /* end of bitmap marker */
00766               if (wrseek(2, bw) != BIR_OK)
00767                      return BIR_OK;              /* no one may care */
00768               wrint32(bw->flen, bw);             /* correct file length */
00769               if (wrseek(34, bw) != BIR_OK)
00770                      return BIR_OK;
00771               wrint32(bw->flen-bw->fbmp, bw); /* correct bitmap length */
00772        } else {
00773               wrbyte(0, bw); wrbyte(0, bw);   /* end of line marker */
00774        }
00775        return BIR_OK;
00776 }
00777 
00778 /* free BMP writer resources */
00779 void
00780 BMPfreeWriter(BMPWriter *bw)
00781 {
00782        if (bw == NULL)
00783               return;
00784        free((void *)bw->hdr);
00785        free((void *)bw->scanline);
00786        free((void *)bw);
00787 }