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