// Expanded bitmap interpretation
typedef struct
{
long HeaderSize; // The size of the infoheader
bool Masked; // As icon format with supplemental 1bit mask data
bool Compressed; // The image is an RLE compressed image
bool ColIndirect; // The bitmap references a colour table
bool MaskIndirect; // The bitmap references bitmasks in the colour table
long ColTabEntries; // The number of entries in the colour table
short BitCount; // The number of bits per pixel
long PixelMultiplier; // The number of bytes/pixel (pixels/byte ~ divide)
bool Divide; // Indicates the reciprocity of the pixel multiplier
long ColTabEntrySize; // The size (in bytes) of a colour table entry
long ColBlockSize; // The size (in bytes) of the whole colour table
bool BottomUp; // The bitmap is bottom up
long XDim; // The x dimension of the bitmap
long YDim; // The y dimension of the bitmap
char LinePackSize; // The size (in bytes) of the line packing
long LineSize; // The size (in bytes) of a line of pixel data
long PixBlockSize; // The size (in bytes) of the whole pixel data
char MaskLinePackSize; // The size (in bytes) of the mask line packing
long MaskLineSize; // The size (in bytes) of a line of mask data
long MaskBlockSize; // The size (in bytes) of the whole mask data
long ColTabOffs; // The offset (in bytes) of the colour table
long PixelOffs; // The offset (in bytes) of the pixel data
long MaskOffs; // The offset (in bytes) of the mask data
long TotalSize;
}BitmapMetrics;
bool CalcBitmapMetrics(void* pMap,BitmapMetrics& rBMM,bool Masked = false);
bool IsBitmapInfoHeader(void* pBuf,unsigned long BufSz);
bool IsBitmapFileHeader(void* pBuf,unsigned long BufSz);
void StripBitmapFileHeader(void*& pBuf,unsigned long& BufSz);
void AddBitmapFileHeader(void*& pBuf,unsigned long& BufSz);
void ConvertBitmapInfoHeaderType(void*& pBuf,unsigned long& BufSz,unsigned long Type,bool Masked = false);
CalcBitmapMetrics() takes a pointer to any version of BITMAPINFO and returns the BitmapMetrics above. The basic assumption is that the actual bitmap data follows directly on from the passed void* to BITMAPINFO. Clearly all of the returned offsets are byte offsets from the beginning of the BITMAPINFO, rather than any kind of pointer type. The pixel data, and optional colour table, do not have to be present when calling this function. Only the BITMAPINFO need be present. In this condition the byte offsets would potentially reference invalid memory locations. It is the users responsibility to reallocate the memory such that the byte offsets are valid. This lack of need for the actual bitmap data is useful for both creating and initialising new DIB's. The function is also useful for manuipulating existing DIB's.
The function returns false for any bitmap that would require special attention. The five critical cases are;
This type can be directly passed to wndows, but it has the limitation that the palette is mapped to system colors on entry to Windows, and then when retrieved from Windows, it comes with the standard palette. For this reason, the LibRLE functions are provided to allow external compression and decompression of RLE bitmaps, which give you full control over the palette before you pass the bitmap to Windows. In this case the BitmapMetrics structure is filled out. The dimensions of the image data and the linesize are set for the uncompressed image. The offsets, block sizes and overall size are set to that of the compressed image.
These types are not converted by Windows, and are only intended to provide a passthrough mechanism. This mechanism allows use of these image types with some types of printer which support PNG and JPEG inputs indirectly through a Windows printer device context. In this case the BitmapMetrics structure is not filled out.
The function's Masked argument:
This argument is designed to aid with the manipulation of standard Windows icons. These are Windows bitmaps, but with a double pixel data field. The first pixel field after the palette describes the colour data and works in the normal way. The second field contains one bit per pixel transparency data. Both are contiguous, and it is mandated that the images they represent are identical in size. This is essential because the BITMAPINFO for the transparency data is inferred from that of the colour data. In this case the extended members of the BitmapMetrics structure that define the mask pixel data are filled out in addition to the normal ones.
The function returns true for any bitmap, in the conventional sense. i.e. HEADER optional colour palette, and pixel data. As far as we know, this BitmapMetrics metadata satisfies the need to handle any Windows bitmap. If an application handles each of the boolean flag cases then the application can handle any bitmap presented to it. The additional parameters allow an application to find specific palette entries and pixel data by casting and simple addition.
The remaining functions are fairly self explanatory;
IsBitmapXXX() functions:
These simply look at the buffer and report validity status. These functions don't perform any deep semantic checking. By probability, they will allow an application to filter out anything that is not obviously a bitmap.
Strip/AddBitmapFileHeader() functions:
These take an open reference to a memory buffer, and will reallocate that memory. They simply add and remove the BITMAPFILEHEADER structure at the beginning of a DIB. The file header is necassary to make a universal bitmap disk file, but prohibited on a memory DIB that one might pass into a device context. Check that the buffer is not null on return. This is the indication of failure.
ConvertBitmapInfoHeaderType() function:
This allows conversion and resizing of a DIB, for any given header type. This is not "image sizing", but metadata sizing. In general it is expected that a compatible disk file bitmap has a version 1 BITMAPINFOHEADER associated with it. It is assumed that most programs would expect all current types to be readable, and version 1 to be the sole output format, unless explicitly requested otherwise. Convert type is useful for pragmatic "downcasting" of a bitmap for disk file purposes, and perhaps "upcasting" internally for supporting the more advanced features that Windows offers in the latest versions of their BITMAPINFOHEADER. It is notable that one may use a version 1 header even for alpha transparent images with Windows, however, it is unreasonable to expect this to be a universal file format.
All of the image import filters require some image metadata (in an ImgInfo struct) when converting from bitmap to their native type. Typically this data includes such things as BitCount, image dimensions, and particularly the linesize. All of this information can be generated by CalcBitmapMetrics(). Some of the information like bit depth and dimension is easy enough to get from the BITMAPINFOHEADER directly. It is notable, however, that the distinction between bottom up and top down dibs can easily get lost, and CalcBitmapMetrics() isolates this problem. The same is true for linesize. In order to calculate the linesize one must combine the x dimension, with the size of the pixel data, and then round. CalcBitmapMetrics() isolates this problem too. In general the parameters in the ImgInfo struct can be obtained by their similarly named peers in BitmapMetrics.
To pass a standard DIB into an image import (i.e. export) filter one would typically do the following;
/*
* BMP2PNG - Convert a bitmap to a PNG
* pBuf - A Pointer to the image in memory
* Size - The size of the memory block
*
* On entry the memory must contain a valid 8bpp Windows bitmap
* On exit the memory contains a valid PNG
*/
void DIB2PNG(void*& pBuf,unsigned long& Size)
{
bool Fail = true;
// Can anything be done?
if(pBuf != NULL)
{
// Is it actually a bitmap?
if(IsBitmapFileHeader(pBuf))
{
// What sort of bitmap is it then?
BitmapMetrics aBMM;
CalcBitmapMetrics(pBuf,aBMM);
// Is it the right sort of bitmap?
if(
(!aBMM.Masked) &&
(!aBMM.Compressed) &&
(!aBMM.MaskIndirect) &&
(aBMM.ColIndirect) &&
(aBMM.Bitcount == 8)
)
{
// Remove the file header
StripBitmapFileHeader(pBuf,Size);
// Remove the info header, but retain the colour and pixel blocks
unsigned long NewSize = aBMM.ColBlockSize + aBMM.PixBlockSize;
void* pColorTable = &(((unsigned char*)pBuf)[aBMM.ColTabOffs]);
memcpy(pBuf,pColorTable,NewSize);
pBuf = realloc(pBuf,NewSize);
Size = NewSize;
// Make the PNGInfo
ImgPNGInfo Info;
Info.BitCount = aBMM.Bitcount;
Info.XDim = aBMM.XDim;
Info.YDim = aBMM.YDim;
Info.LineSize = aBMM.LineSize;
Info.PNGColorType = PNG_TYPE_PALETTE;
Info.Trns = PNG_TRNS_NONE;
// Convert the bitmap to PNG
png_ToPNG(pBuf,BufSz,Info);
Fail = false;
}
}
}
if(Fail)
{
// Indicate failure
if(pBuf != NULL)
free(pBuf);
pBuf = NULL;
Size = 0;
}
}