www.pudn.com > ilib > IBMP.c
/* * IBMP.c * * Image library * * Description: * Read BMP files. * * History: * 01-Apr-00 Jim Winstead jimw@trainedmonkey.com * Created * ****************************************************************************/ #include#include #include #include #include #include "Ilib.h" #include "IlibP.h" #define BMP_HEADER ('B' + ('M' << 8)) /* Compression formats */ #define BI_RGB 0 #define BI_RLE8 1 #define BI_RLE4 2 #define BI_BITFIELDS 3 #define SET_PIXEL(buffer, byte) \ do { \ *(buffer+0) = (colortable[byte&0xff]&(0xff<<16))>>16; \ *(buffer+1) = (colortable[byte&0xff]&(0xff<<8))>>8; \ *(buffer+2) = (colortable[byte&0xff]&(0xff<<0))>>0; \ } while (0); static int ReadShort (fp, ret) FILE *fp; int *ret; { int c = fgetc(fp); if (c == EOF) return 0; *ret = (c&0xff); c = fgetc(fp); if (c == EOF) return 0; *ret |= (c&0xff) << 8; return 1; } static int ReadLong (fp, ret) FILE *fp; int *ret; { int c = fgetc(fp); if (c == EOF) return 0; *ret = (c&0xff); c = fgetc(fp); if (c == EOF) return 0; *ret |= (c&0xff) << 8; c = fgetc(fp); if (c == EOF) return 0; *ret |= (c&0xff) << 16; c = fgetc(fp); if (c == EOF) return 0; *ret |= (c&0xff) << 24; return 1; } IError _IReadBMP ( fp, options, image_return ) FILE *fp; IOptions options; IImageP **image_return; { IImageP *image = NULL; int scratch, fileSize, offset, w, h, depth, compression, imagesize, xpelspermeter, ypelspermeter, colorsused, colorsimportant; int redmask, greenmask, bluemask, redshift, greenshift, blueshift, redbits, greenbits, bluebits; int *colortable = NULL; /* read the header and make sure this is a BMP file */ if (!ReadShort(fp, &scratch) || scratch != BMP_HEADER) return IInvalidFormat; /* read the file size (not sure this is useful) */ if (!ReadLong(fp, &fileSize)) return (IInvalidFormat); /* read the two reserved fields (must be zero, according to spec) */ if (!ReadShort(fp, &scratch) || scratch != 0) return (IInvalidFormat); if (!ReadShort(fp, &scratch) || scratch != 0) return (IInvalidFormat); /* read the offset of the actual graphic bits */ if (!ReadLong(fp, &offset)) return (IInvalidFormat); /*fprintf(stderr, "offset = %d\n", offset);*/ /* verify that the header is 40 bytes */ /* XXX: really should save this and just skip bytes we don't understand. */ /* and we could be using this to detect things in OS/2 format */ if (!ReadLong(fp, &scratch) || scratch != 40) return (IInvalidFormat); /* get image size and depth */ if (!ReadLong(fp, &w)) return (IInvalidFormat); if (!ReadLong(fp, &h)) return (IInvalidFormat); if (!ReadShort(fp, &scratch) || scratch != 1) return (IInvalidFormat); if (!ReadShort(fp, &depth)) return (IInvalidFormat); /*fprintf(stderr, "image size: %dx%dx%d\n", w, h, depth);*/ if (!ReadLong(fp, &compression)) return (IInvalidFormat); if (!ReadLong(fp, &imagesize)) return (IInvalidFormat); if (!ReadLong(fp, &xpelspermeter)) return (IInvalidFormat); if (!ReadLong(fp, &ypelspermeter)) return (IInvalidFormat); if (!ReadLong(fp, &colorsused)) return (IInvalidFormat); if (!ReadLong(fp, &colorsimportant)) return (IInvalidFormat); /* we have read 40 bytes of header */ /* we have read 54 bytes of data from the file */ /* fprintf(stderr, "compression=%d, imagesize=%d, colorsused=%d\n", compression, imagesize, colorsused); */ /* get the masks, shifts, and bits for bitfields (16 and 32 bits) */ if (compression == BI_BITFIELDS) { if (!ReadLong(fp,&redmask)) return IInvalidImage; for (redshift = 0; !(redmask&(1< = 0; y--) { for (x = 0; x < w; x++) { int r,g,b; r = fgetc(fp); g = fgetc(fp); b = fgetc(fp); /* this leaks the image object if we hit a premature EOF */ if (r == EOF || g == EOF || b == EOF) return (IInvalidFormat); *(image->data + (y*3*w) + (x*3)) = b; *(image->data + (y*3*w) + (x*3) + 1) = g; *(image->data + (y*3*w) + (x*3) + 2) = r; } } } else if (depth == 16 && compression == BI_BITFIELDS) { int x, y; /* slurp in the data */ for (y = h - 1; y >= 0; y--) { for (x = 0; x < w; x++) { char *pixel = image->data + (y*3*w) + (x*3); int color; /* this leaks the image object if we hit a premature EOF */ if (!ReadShort(fp, &color)) return (IInvalidFormat); /* scale 5/6 bit values to 8 bits */ *(pixel + 0) = (((color&redmask)>>redshift)<<8)>>redbits; *(pixel + 1) = (((color&greenmask)>>greenshift)<<8)>>greenbits; *(pixel + 2) = (((color&bluemask)>>blueshift)<<8)>>bluebits; } } } else if (depth == 8 && compression == BI_RGB) { int x, y; /*fprintf(stderr, "8 bit, no compression\n");*/ for (y = h - 1; y >= 0; y--) { for (x = 0; x < w; x++) { char *pixel = image->data+(y*w*3)+(x*3); int byte = fgetc(fp); if (byte == EOF) return IInvalidFormat; SET_PIXEL(pixel, byte); } } } else if (depth <= 8 && compression) { int byte, count, x, y; char *buffer = image->data+w*(h-1)*3; for (y = 0; y < h; ) { count = fgetc(fp); if (count == EOF) return (IInvalidFormat); if (count != 0) { /*fprintf(stderr, "stretch of %d bytes\n", count);*/ byte = fgetc(fp); if (byte == EOF) return (IInvalidFormat); for (scratch = 0; scratch < count; scratch++) { if (compression == 1) { SET_PIXEL(buffer, byte); } else { int thisbyte = (scratch & 0x01) ? (byte & 0x0f) : ((byte >> 4) & 0x0f); SET_PIXEL(buffer, thisbyte); } buffer += 3; } } else { count = fgetc(fp); if (count == EOF) return (IInvalidFormat); if (count == 0x01) break; /* end of bitmap */ switch (count) { case 0x00: /* end of the line */ /*fprintf(stderr, "hit end of line %d\n", y);*/ y++; buffer = image->data+w*(h-y-1)*3; break; case 0x02: /* goto specific position */ x = fgetc(fp); y = fgetc(fp); /*fprintf(stderr, "going to %d,%d\n", x, y);*/ if (x == EOF || y == EOF) return (IInvalidFormat); buffer = image->data+w*(h-y-1)*3+(x*3); break; default: /* a bunch of literal bytes */ /*fprintf(stderr, "handling %d literal bytes\n", count);*/ for (scratch = 0; scratch < count; scratch++) { byte = fgetc(fp); if (byte == EOF) return (IInvalidFormat); if (compression == 1) { SET_PIXEL(buffer, byte); } else { int thisbyte = (scratch & 0x01) ? (byte & 0x0f) : ((byte >> 4) & 0x0f); SET_PIXEL(buffer, thisbyte); } buffer += 3; } /* handle padding */ if (compression == 1) { if (count & 0x01) { /*fprintf(stderr, "eating padding\n");*/ (void)fgetc(fp); } } else if ((count & 0x03) == 1 || ((count & 0x03) == 2)) { (void)fgetc(fp); } break; } } } } else { _IFreeImage(image); return (IInvalidFormat); } if (colortable) free(colortable); *image_return = image; return ( INoError ); }