roo_e/thirdparty/grx249/addons/bmp/bmp.c
2022-05-16 17:59:47 -05:00

669 lines
22 KiB
C

/*
** <bmp.c> - BMP read/write file
** by Michal Stencl Copyright (c) 1998
** - read BMP 2, 4, 8 bpp
** - write BMP 8, 24 bpp
** <e-mail> - [stenclpmd@ba.telecom.sk]
**
*/
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#if defined(_MSC_VER) && defined(_WIN32)
#include <io.h>
#else
#include <unistd.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#ifdef __MSDOS__
# include <io.h>
#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;
}