/* ** - BMP read/write file ** by Michal Stencl Copyright (c) 1998 ** - read BMP 2, 4, 8 bpp ** - write BMP 8, 24 bpp ** - [stenclpmd@ba.telecom.sk] ** */ #include #include #include #if defined(_MSC_VER) && defined(_WIN32) #include #else #include #endif #include #include #include #ifdef __MSDOS__ # include #endif #include "libgrx.h" #include "clipping.h" #if defined(__MSDOS__) || defined(MSDOS) #define BIN_WR (O_WRONLY | O_BINARY) #define BIN_RD (O_RDONLY | O_BINARY) #else #define BIN_WR O_WRONLY #define BIN_RD O_RDONLY #endif #define BIN_CREAT (BIN_WR|O_CREAT) #ifndef S_IREAD #define S_IREAD S_IRUSR #endif #ifndef S_IWRITE #define S_IWRITE S_IWUSR #endif #define CREAT_PERM S_IREAD|S_IWRITE #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define BMPFILEHEADERSIZE 14 #define BMPINFOHEADERSIZE 40 #define BI_RGB 0L #define BI_RLE8 1L #define BI_RLE4 2L #define _GrBitmapPointerTypes_DEFINED_ typedef struct _GR_bitmapfileheader GrBitmapFileHeader; typedef struct _GR_bitmapinfoheader GrBitmapInfoHeader; typedef struct _GR_bmpimagecolors GrBmpImageColors; typedef struct _GR_bmpimage GrBmpImage; /* ************************************************************************ */ /* _GR_bitmapfileheader */ /* ************************************************************************ */ struct _GR_bitmapfileheader { GR_int16u bf_type; GR_int32u bf_size; GR_int16u bf_reserved1; GR_int16u bf_reserved2; GR_int32u bf_offbits; }; /* ************************************************************************ */ /* _GR_bitmapinfoheader */ /* ************************************************************************ */ struct _GR_bitmapinfoheader { GR_int32u bn_size; GR_int32u bn_width; GR_int32u bn_height; GR_int16u bn_planes; GR_int16u bn_bitcount; GR_int32u bn_compression; GR_int32u bn_sizeimage; GR_int32u bn_xpelspermeter; GR_int32u bn_ypelspermeter; GR_int32u bn_clrused; GR_int32u bn_clrimportant; }; /* ************************************************************************ */ /* _GR_bmpimagecolors */ /* ************************************************************************ */ struct _GR_bmpimagecolors { GR_int8u *bp_palette; /* (R, G, B, Reserved) * | 2 | 16 | 256 */ GrColor *bp_colormap; int bp_numcolors; }; /* ************************************************************************ */ /* _GR_bmpimage */ /* ************************************************************************ */ struct _GR_bmpimage { GrBitmapFileHeader *bi_bmpfileheader; GrBitmapInfoHeader *bi_bmpinfoheader; GrBmpImageColors *bi_bmpimagecolors; GR_int16s bi_erasepalette; char *bi_map; }; #define bi_width bi_bmpinfoheader->bn_width #define bi_height bi_bmpinfoheader->bn_height #define bi_palette bi_bmpimagecolors->bp_palette #define bi_colormap bi_bmpimagecolors->bp_colormap #define bi_numcolors bi_bmpimagecolors->bp_numcolors int GrLoadBmpFileHeader ( int _handle, GrBitmapFileHeader* _fileheader ); int GrLoadBmpInfoHeader ( int _handle, GrBitmapInfoHeader* _infoheader ); static unsigned char *__GrLoadPaletteBmp ( int _handle, unsigned long _paloffset, int _colors ); static unsigned char *GrLoadPaletteBmp ( int _handle, int *_col, GrBitmapInfoHeader *_iheader ); static char *GrLoadImageFromBmpBiRgb ( int _handle, unsigned long _offset, long _maxbufsize, int _colors, GrBitmapInfoHeader *_infoheader ); static char *GrLoadImageFromBmpBiRle8 ( int _handle, unsigned long _offset, unsigned long _maxbufsize, int _colors, GrBitmapInfoHeader *_infoheader ); static char *GrLoadImageFromBmpBiRle4 ( int _handle, unsigned long _offset, unsigned long _maxbufsize, int _colors, GrBitmapInfoHeader *_infoheader ); static char *GrLoadImageFromBmp ( int _handle, unsigned long _offset, int _colors, GrBitmapInfoHeader *_infoheader ); /* exported functions */ int GrFreeBmpImageColors ( GrBmpImageColors *_pal ); int GrAllocBmpImageColors ( GrBmpImage *_bmp, GrBmpImageColors *_pal ); GrBmpImage *GrLoadBmpImage ( char *_filename ); GrPattern *GrConvertBmpImageToPattern ( GrBmpImage *_bmp ); GrPattern *GrConvertBmpImageToStaticPattern ( GrBmpImage *_bmp ); void GrUnloadBmpImage ( GrBmpImage *_bmp ); int GrSaveBmpImage ( char *_filename, GrContext *_c, int _x1, int _y1, int _x2, int _y2 ); unsigned long GrBmpImageWidth ( GrBmpImage* _bmp ); unsigned long GrBmpImageHeight ( GrBmpImage* _bmp ); char *GrBmpImagePalette ( GrBmpImage* _bmp ); GrColor *GrBmpImageColorMap ( GrBmpImage* _bmp ); GrColor GrBmpImageNumColors ( GrBmpImage* _bmp ); /* end of exported functions */ /* ************************************************************************ */ int GrLoadBmpFileHeader ( int _handle, GrBitmapFileHeader *_fileheader ) /* ************************************************************************ */ { if (( !_fileheader ) || ( _handle == -1 )) return FALSE; memset(_fileheader, 0, BMPFILEHEADERSIZE); lseek(_handle, SEEK_SET, 0); read(_handle, &_fileheader->bf_type, 2); read(_handle, &_fileheader->bf_size, 4); read(_handle, &_fileheader->bf_reserved1, 2); read(_handle, &_fileheader->bf_reserved2, 2); read(_handle, &_fileheader->bf_offbits, 4); return TRUE; } /* ************************************************************************ */ int GrLoadBmpInfoHeader ( int _handle, GrBitmapInfoHeader *_infoheader ) /* ************************************************************************ */ { if (( !_infoheader ) || ( _handle == -1 )) return FALSE; lseek(_handle, SEEK_SET, BMPFILEHEADERSIZE); _infoheader->bn_size = 0; read(_handle, &_infoheader->bn_size, 4); memset(_infoheader, 0, _infoheader->bn_size); read(_handle, &_infoheader->bn_width, 4); if ( _infoheader->bn_width % 4 ) _infoheader->bn_width += 4 - (_infoheader->bn_width % 4); read(_handle, &_infoheader->bn_height, 4); read(_handle, &_infoheader->bn_planes, 2); read(_handle, &_infoheader->bn_bitcount, 2); read(_handle, &_infoheader->bn_compression, 4); read(_handle, &_infoheader->bn_sizeimage, 4); read(_handle, &_infoheader->bn_xpelspermeter, 4); read(_handle, &_infoheader->bn_ypelspermeter, 4); read(_handle, &_infoheader->bn_clrused, 4); read(_handle, &_infoheader->bn_clrimportant, 4); return TRUE; } /* ************************************************************************ */ static unsigned char *__GrLoadPaletteBmp ( int _handle, unsigned long _paloffset, int _colors ) /* ************************************************************************ */ { unsigned char *palette; if ( _handle == -1 ) return NULL; palette = (unsigned char*)malloc(_colors * 4); if ( !palette ) return NULL; lseek(_handle, SEEK_SET, _paloffset); read(_handle, palette, _colors * 4); return palette; } /* ************************************************************************ */ static unsigned char *GrLoadPaletteBmp ( int _handle, int *_col, GrBitmapInfoHeader* _iheader ) /* ************************************************************************ */ { unsigned char *palette; unsigned long paloffset; *_col = -1; if (( _handle == -1 ) || ( !_iheader )) return NULL; palette = NULL; paloffset = BMPFILEHEADERSIZE + _iheader->bn_size; switch ( _iheader->bn_bitcount ) { case 1 : { *_col = 2; palette = __GrLoadPaletteBmp(_handle, paloffset, 2); } break; case 4 : { *_col = 16; palette = __GrLoadPaletteBmp(_handle, paloffset, 16); } break; case 8 : { *_col = 256; palette = __GrLoadPaletteBmp(_handle, paloffset, 256); } break; case 24 : { *_col = 0; palette = NULL; } break; default : *_col = -1; } if (( !palette ) && ( *_col != 0 )) *_col = -1; return palette; } /* ************************************************************************ */ static char *GrLoadImageFromBmpBiRgb ( int _handle, unsigned long _offset, long _maxbufsize, int _colors, GrBitmapInfoHeader *_infoheader ) /* ************************************************************************ */ { char *map = NULL; char *buffer = NULL; unsigned long width; unsigned long size; unsigned long i; if (( _handle == -1 ) || ( !_infoheader ) || ( _infoheader->bn_bitcount < 1 )) return NULL; width = _infoheader->bn_width; size = width; i = size; lseek(_handle, SEEK_SET, _offset); if ( _infoheader->bn_bitcount == 1 ) { unsigned char bits[8], n; unsigned long w, bufsize; int j, k; char *runmap; _maxbufsize = _maxbufsize * 8; map = (char *)malloc(_maxbufsize); bufsize = (unsigned long)ceil((float)width / 8); buffer = (char *)malloc(bufsize); runmap = NULL; if ( !map || !buffer ) { if (map) free(map); if (buffer) free(buffer); return NULL; } while ( i <= _maxbufsize ) { read(_handle, buffer, bufsize); runmap = &map[_maxbufsize - i]; for ( w = 0; w < width; w++) { j = w % 8; if ( !j ) { n = buffer[w / 8]; for ( k = 0; k < 8; k++ ) { bits[7 - k] = n & 1; n = n >> 1; } } runmap[w] = bits[j]; } i += size; } } if ( _infoheader->bn_bitcount == 4 ) { unsigned long bufsize; char *runmap; unsigned char bits[2], n; unsigned long w; int j, q; _maxbufsize = _maxbufsize * 2; map = (char *)malloc(_maxbufsize); bufsize = (unsigned long)ceil((float)width / 2); buffer = (char*)malloc(bufsize); runmap = NULL; if ( !map || !buffer ) { if (map) free(map); if (buffer) free(buffer); return NULL; } while ( i <= _maxbufsize ) { read(_handle, buffer, bufsize); runmap = &map[_maxbufsize - i]; for ( w = 0; w < width; w++) { j = w % 2; if ( !j ) { n = buffer[w / 2]; q = n & 255; bits[1] = q & 15; q = q >> 4; bits[0] = q & 15; n = n >> 8; } runmap[w] = bits[j]; } i += size; } } if ( _infoheader->bn_bitcount == 8 ) { unsigned long bufsize; map = (char*)malloc(_maxbufsize); bufsize = size; if ( !map ) return NULL; while ( i <= _maxbufsize ) { read(_handle, &map[_maxbufsize - i], bufsize); i += bufsize; } } if ( _infoheader->bn_bitcount == 24 ) { } if (buffer) free(buffer); return map; } /* ************************************************************************ */ static char *GrLoadImageFromBmpBiRle8 ( int _handle, unsigned long _offset, unsigned long _maxbufsize, int _colors, GrBitmapInfoHeader *_infoheader ) /* ************************************************************************ */ { return NULL; /* this version not contains Rle8 yet */ } /* ************************************************************************ */ static char *GrLoadImageFromBmpBiRle4 ( int _handle, unsigned long _offset, unsigned long _maxbufsize, int _colors, GrBitmapInfoHeader *_infoheader ) /* ************************************************************************ */ { return NULL; /* this version not contains Rle4 yet */ } /* ************************************************************************ */ static char *GrLoadImageFromBmp ( int _handle, unsigned long _offset, int _colors, GrBitmapInfoHeader *_infoheader ) /* ************************************************************************ */ { char* map; int maxbufsize; if (( _handle == -1 ) || ( !_infoheader )) return NULL; map = NULL; maxbufsize = _infoheader->bn_sizeimage; switch ( _infoheader->bn_compression ) { case BI_RGB : { if ( !maxbufsize ) maxbufsize = _infoheader->bn_width * _infoheader->bn_height; map = GrLoadImageFromBmpBiRgb(_handle, _offset, maxbufsize, _colors, _infoheader); break; } case BI_RLE8 : map = GrLoadImageFromBmpBiRle8(_handle, _offset, maxbufsize, _colors, _infoheader); break; case BI_RLE4 : map = GrLoadImageFromBmpBiRle4(_handle, _offset, maxbufsize, _colors, _infoheader); break; } return map; } /*====++====================================================================*/ /* EXPORTED FUNCTIONS */ /*==++======================================================================*/ /* ************************************************************************ */ int GrFreeBmpImageColors ( GrBmpImageColors *_pal ) /* ************************************************************************ */ { if (( !_pal ) || ( !_pal->bp_colormap )) return FALSE; if ( _pal->bp_palette ) { int i; GrColor *colors = _pal->bp_colormap; colors[0] = _pal->bp_numcolors; for ( i = 0; i < _pal->bp_numcolors; i++ ) GrFreeColor(colors[i+1]); free(_pal->bp_palette); _pal->bp_palette = NULL; _pal->bp_numcolors = 0; return TRUE; } return FALSE; } /* ************************************************************************ */ int GrAllocBmpImageColors ( GrBmpImage *_bmp, GrBmpImageColors *_pal ) /* ************************************************************************ */ { if (( !_bmp ) || ( _bmp->bi_colormap != NULL ) || (_bmp->bi_numcolors < 2 )) return FALSE; _bmp->bi_erasepalette = TRUE; if ( _bmp->bi_palette ) { int i; GrColor *colors = malloc(sizeof(GrColor)*(_bmp->bi_numcolors+1)); if ( !colors ) return FALSE; colors[0] = _bmp->bi_numcolors; for ( i = 0; i < _bmp->bi_numcolors; i++ ) colors[i+1] = GrAllocColor(_bmp->bi_palette[i*4+2], _bmp->bi_palette[i*4+1], _bmp->bi_palette[i*4+0]); _bmp->bi_colormap = colors; if ( _pal ) { _bmp->bi_erasepalette = FALSE; memcpy(_pal,_bmp->bi_bmpimagecolors,sizeof(GrBmpImageColors)); _bmp->bi_palette = NULL; _bmp->bi_numcolors = 0; } return TRUE; } return FALSE; } /* ************************************************************************ */ GrBmpImage *GrLoadBmpImage ( char *_filename ) /* ************************************************************************ */ { #define defClose { \ close(handle); \ if (bmpimage) free(bmpimage); \ return NULL; \ } #define ADD2PTR(p,o) ((void *) ((char *)(p)+(o)) ) int handle; GrBmpImage *bmpimage = NULL; if ( (handle = open(_filename, BIN_RD)) != -1 ) { bmpimage = malloc( sizeof(GrBmpImage) +sizeof(GrBitmapFileHeader) +sizeof(GrBitmapInfoHeader) +sizeof(GrBmpImageColors)); if ( !bmpimage ) defClose; memset(bmpimage, 0, sizeof(GrBmpImage) +sizeof(GrBitmapFileHeader) +sizeof(GrBitmapInfoHeader) +sizeof(GrBmpImageColors)); bmpimage->bi_bmpfileheader = ADD2PTR(bmpimage, sizeof(GrBmpImage)); bmpimage->bi_bmpinfoheader = ADD2PTR(bmpimage, sizeof(GrBmpImage) +sizeof(GrBitmapFileHeader)); bmpimage->bi_bmpimagecolors = ADD2PTR(bmpimage, sizeof(GrBmpImage) +sizeof(GrBitmapFileHeader) +sizeof(GrBitmapInfoHeader)); bmpimage->bi_erasepalette = TRUE; if ( !GrLoadBmpFileHeader(handle, bmpimage->bi_bmpfileheader) ) defClose; if ( bmpimage->bi_bmpfileheader->bf_type != 19778 ) /* MAGIC NUMBER */ defClose; if ( !GrLoadBmpInfoHeader(handle, bmpimage->bi_bmpinfoheader) ) defClose; bmpimage->bi_palette = GrLoadPaletteBmp(handle, &(bmpimage->bi_numcolors), bmpimage->bi_bmpinfoheader); if ( bmpimage->bi_numcolors == -1 ) defClose; bmpimage->bi_map = GrLoadImageFromBmp(handle, bmpimage->bi_bmpfileheader->bf_offbits - BMPFILEHEADERSIZE, bmpimage->bi_numcolors, bmpimage->bi_bmpinfoheader); if ( !bmpimage->bi_map ) defClose; } #undef defClose return bmpimage; } /* ************************************************************************ */ GrPattern *GrConvertBmpImageToPattern ( GrBmpImage *_bmp ) /* ************************************************************************ */ { if (( !_bmp ) || ( !_bmp->bi_map )) return NULL; return GrBuildPixmap(_bmp->bi_map, _bmp->bi_width, _bmp->bi_height, _bmp->bi_colormap); } /* ************************************************************************ */ GrPattern *GrConvertBmpImageToStaticPattern ( GrBmpImage *_bmp ) /* ************************************************************************ */ { GrPattern *p = NULL; if ( _bmp && _bmp->bi_map ) { p = GrBuildPixmap(_bmp->bi_map, _bmp->bi_width, _bmp->bi_height, _bmp->bi_colormap); if ( p ) GrUnloadBmpImage(_bmp); } return p; } /* ************************************************************************ */ void GrUnloadBmpImage ( GrBmpImage *_bmp ) /* ************************************************************************ */ { if ( !_bmp ) return; if ( _bmp->bi_erasepalette ) GrFreeBmpImageColors(_bmp->bi_bmpimagecolors); _bmp->bi_palette = NULL; _bmp->bi_numcolors = 0; if ( _bmp->bi_map ) free(_bmp->bi_map); free(_bmp); _bmp = NULL; } /* ************************************************************************ */ int GrSaveBmpImage ( char *_filename, GrContext *_c, int _x1, int _y1, int _x2, int _y2 ) /* ************************************************************************ */ { int handle; unsigned long width, height; unsigned char palette[256*4]; int r, g, b; char* line; unsigned long yy, xx; GrColor pixcol; GrBitmapFileHeader fileheader; GrBitmapInfoHeader infoheader; GrColor colors, i; GrContext safe; if ( !_c ) _c = (GrContext *)GrCurrentContext(); /* handle = creat(_filename, S_IWRITE); if ( handle < 0 ) { close(handle); return FALSE; } */ handle = open(_filename, BIN_CREAT, CREAT_PERM); if ( handle < 0 ) return FALSE; clip_box_(_c, _x1, _y1, _x2, _y2, CLIP_EMPTY_MACRO_ARG, CLIP_EMPTY_MACRO_ARG); width = _x2 - _x1; height = _y2 - _y1; safe = *GrCurrentContext(); GrSetContext(_c); colors = GrNumColors(); GrSetContext(&safe); if ( width % 4 ) width += 4 - (width % 4); /*========= FILEHEADER =========*/ fileheader.bf_type = 19778; if ( colors == 256 ) fileheader.bf_size = BMPINFOHEADERSIZE + BMPFILEHEADERSIZE + 256*4 + width*height; else fileheader.bf_size = BMPINFOHEADERSIZE + BMPFILEHEADERSIZE + (width*height*3); fileheader.bf_reserved1 = 0; fileheader.bf_reserved2 = 0; if ( colors == 256 ) fileheader.bf_offbits = BMPINFOHEADERSIZE + BMPFILEHEADERSIZE + 256*4; else fileheader.bf_offbits = BMPINFOHEADERSIZE + BMPFILEHEADERSIZE; /*========= INFOHEADER =========*/ infoheader.bn_size = BMPINFOHEADERSIZE; infoheader.bn_width = width; infoheader.bn_height = height; infoheader.bn_planes = 1; infoheader.bn_bitcount = ( colors == 256 ) ? 8 : 24; infoheader.bn_compression = BI_RGB; infoheader.bn_sizeimage = width*height*(infoheader.bn_bitcount / 8); infoheader.bn_xpelspermeter = 0L; infoheader.bn_ypelspermeter = 0L; infoheader.bn_clrused = 0L; infoheader.bn_clrimportant = 0L; /*========= PALETTE =========*/ if ( colors == 256 ) { for ( i = 0; i < colors; i++ ) { GrQueryColor(i, &r, &g, &b); palette[(i*4)] = (unsigned char)b; palette[(i*4)+1] = (unsigned char)g; palette[(i*4)+2] = (unsigned char)r; palette[(i*4)+3] = 0; } } line = (char *)malloc(width*(infoheader.bn_bitcount / 8)); if ( !line ) { close(handle); return FALSE; } /*========= WRITE FILEHEADER =========*/ write(handle, &fileheader.bf_type, 2); write(handle, &fileheader.bf_size, 4); write(handle, &fileheader.bf_reserved1, 2); write(handle, &fileheader.bf_reserved2, 2); write(handle, &fileheader.bf_offbits, 4); /*========= WRITE INFOHEADER =========*/ write(handle, &infoheader.bn_size, 4); write(handle, &infoheader.bn_width, 4); write(handle, &infoheader.bn_height, 4); write(handle, &infoheader.bn_planes, 2); write(handle, &infoheader.bn_bitcount, 2); write(handle, &infoheader.bn_compression, 4); write(handle, &infoheader.bn_sizeimage, 4); write(handle, &infoheader.bn_xpelspermeter, 4); write(handle, &infoheader.bn_ypelspermeter, 4); write(handle, &infoheader.bn_clrused, 4); write(handle, &infoheader.bn_clrimportant, 4); /*========= WRITE PALETTE =========*/ if ( colors == 256 ) write(handle, palette, 256*4); /*========= WRITE MAP =========*/ yy = height; do { xx = 0; do { pixcol = GrPixelC(_c,_x1+xx,_y1+yy); if ( colors == 256 ) line[xx] = pixcol; else { line[(xx*3)+0] = GrRGBcolorBlue(pixcol); line[(xx*3)+1] = GrRGBcolorGreen(pixcol);; line[(xx*3)+2] = GrRGBcolorRed(pixcol);; } } while(++xx < width); write(handle, line, width*(infoheader.bn_bitcount / 8)); } while(--yy > 0); free((void *)line); close(handle); return TRUE; } /* ************************************************************************ */ unsigned long GrBmpImageWidth ( GrBmpImage* _bmp ) /* ************************************************************************ */ { return ( _bmp && _bmp->bi_bmpinfoheader ) ? _bmp->bi_width : 0L; } /* ************************************************************************ */ unsigned long GrBmpImageHeight ( GrBmpImage* _bmp ) /* ************************************************************************ */ { return ( _bmp && _bmp->bi_bmpinfoheader ) ? _bmp->bi_height : 0L; } /* ************************************************************************ */ char *GrBmpImagePalette ( GrBmpImage* _bmp ) /* ************************************************************************ */ { return (char *)(( _bmp && _bmp->bi_bmpimagecolors ) ? _bmp->bi_palette : NULL); } /* ************************************************************************ */ GrColor *GrBmpImageColorMap ( GrBmpImage* _bmp ) /* ************************************************************************ */ { return ( _bmp && _bmp->bi_bmpimagecolors ) ? _bmp->bi_colormap : NULL; } /* ************************************************************************ */ GrColor GrGetBmpImageNumColors ( GrBmpImage* _bmp ) /* ************************************************************************ */ { return ( _bmp && _bmp->bi_bmpimagecolors ) ? _bmp->bi_numcolors : 0; }