The (infamous) BMP loader
Tuesday, 4. July 2006, 23:17:54
// BMPLoader class
// Loads a BMP into ImageData
class BMPLoader: public ImageLoaderImpl<BMPLoader, BMPData>,
public ImageHandler<BMPLoader>,
public IImageLoader
{
public:
// ImageHandler class determines which function
// to use when saving a specific type of BMP
BEGIN_IMAGE_HANDLERS(BMPLoader)
IMAGE_HANDLER(BMP_24, LoadBMP24) // for 24bit, use LoadBMP24
IMAGE_HANDLER(BMP_8, LoadBMP8) // for 8bit, use LoadBMP8
END_IMAGE_HANDLERS()
BMPData *pID; // Pointer to interface
// Declared in IImageLoader interface as pure virtual function
// Here, we redirect it to call ImageHandler function LoadImage
int Load(const char *pszFileName, ImageData *lpData)
{
return LoadImage(pszFileName, lpData);
}
// Determine BMP type
int GetImageType(const char *pszFileName)
{
FILE *fp=NULL;
BITMAPFILEHEADER fhead;
BITMAPINFOHEADER ihead;
int nType= 0;
fp=fopen(pszFileName, "rb");
if(fp == NULL)
return -1;
// read filehead
fread(&fhead,sizeof(fhead),1,fp);
// read infohead
fread(&ihead,sizeof(ihead),1,fp);
fclose(fp);
if (fhead.bfType != 0x4d42) // BM signature
return -1;
if (ihead.biPlanes != 1)
return -2;
if (ihead.biCompression != 0)
return -2;
if (ihead.biBitCount==24) // 24bit TrueColor
{
pID->SetTagValue(ENTRY_TAG_TYPE, (long)ITYPE_TRUECOLOR);
pID->SetTagValue(ENTRY_TAG_COLORCOUNT, 0);
nType=BMP_24;
}
else
if (ihead.biBitCount==8) // 8bit indexed color
{
pID->SetTagValue(ENTRY_TAG_TYPE, (long)ITYPE_8BIT);
pID->SetTagValue(ENTRY_TAG_COLORCOUNT, (long)256);
nType=BMP_8;
}
else
if (ihead.biBitCount==4) // 4bit - cannot load
{
pID->SetTagValue(ENTRY_TAG_TYPE, (long)ITYPE_INDEXED_COLOR);
nType=BMP_4;
}
else
if (ihead.biBitCount==2) // 2bit - cannot load
{
pID->SetTagValue(ENTRY_TAG_TYPE, (long)ITYPE_GRAY);
nType=BMP_2;
}
pID->SetTagValue(ENTRY_TAG_WIDTH, ihead.biWidth); // set width
pID->SetTagValue(ENTRY_TAG_HEIGHT, ihead.biHeight); // set height
return nType;
}
BMPLoader()
{
pID = new BMPData; // create datastructure interface
pID->InsertEntry((long*)&m_imageType, ENTRY_TAG_TYPE); // set image type
}
~BMPLoader()
{
delete pID; // delete interface, we are done
}
// Calculates buffer length required for a line
inline int CalculateBufferLength(int nWidth)
{
register int nRet = nWidth*3; // maximum buffer length is width pixels *3 colors for a line
return nRet;
}
// 24bit Bitmap Load function
bool LoadBMP24(const char *pszFileName, ImageData *pImageData)
{
long *lpData = (long*)pImageData; // cast ImageData* to long* to store in interface
BYTE *cr; // red array
BYTE *cg; // green array
BYTE *cb; // blue array
int offset; // offset for arrays
BYTE *buf; // buffer
int nBufferLen; // buffer length
bool bReverse=false; // BMP in reverse order?
FILE *fp = NULL;
// Insert ImageData* to image datastructure
pID->InsertEntry(lpData, ENTRY_TAG_DATAPTR);
// Add datastructure interface into loader interface
AddInterface(&IID_IImageDataStruct, (void**)&pID);
if (!AllocateImage()) // try to allocate image
return false;
long *lpHeight, *lpWidth; // width, and height of bitmap
// get width from datastructure interface
pID->QueryEntry(ENTRY_TAG_HEIGHT,(void**)&lpHeight);
// get height from datastructure interface
pID->QueryEntry(ENTRY_TAG_WIDTH,(void**)&lpWidth);
if(*lpHeight<0) // reverse check
{
*lpHeight = -(*lpHeight); // rotate 180degrees
bReverse = true; // this is reverse
}
cr = pImageData->GetColorPtrRed(); // get allocated Red array in allocated block
cg = pImageData->GetColorPtrGreen(); // get allocated Green array in allocated block
cb = pImageData->GetColorPtrBlue(); // get allocated Blue array in allocated block
// get required buffer length for a line
nBufferLen= CalculateBufferLength(pImageData->GetSize(GS_X));
if((buf = (BYTE*)malloc(nBufferLen)) == NULL) // allocate buffer
{
FreeImage(pImageData); // free allocated image block
return false; // could not allocate buffer
}
if ((fp = fopen(pszFileName, "rb"))==NULL)
return false;
// BMP file format has BITMAPFILEHEADER
// and BITMAPINFOHEADER sturtures at first blocks
// We skip them to reach the bitmap itself
fseek(fp, sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER), SEEK_SET);
if(bReverse) // are we loading in reverse
{
offset = 0; // offset for color arrays
for(int nRow=0;nRow<(*lpHeight);nRow++) // start from bottom of image through top
{
fread(buf, nBufferLen, 1, fp); // read line
int bufCurX=0; // current x position in buffer
for(int curX=0;curX<(*lpWidth);curX++) // loop until end of line, read 1 pixel
{
*(cb+offset+curX) = buf[bufCurX]; // blue array position=offset + current X
*(cg+offset+curX) = buf[++bufCurX]; // same for green, but increase buffer's x position
*(cr+offset+curX) = buf[++bufCurX]; // increase buffer position once again
}
offset +=(*lpWidth); // now, offset is set to next row
}
}
else
{ // not reverse
offset = ((*lpHeight)-1)*(*lpWidth); // offset for color arrays
for(int nRow=0;nRow<*lpHeight;nRow++) // loop from top line to bottom line
{
fread(buf, nBufferLen, 1, fp); // read line
int bufCurX=-1; // current x in buffer
for(int curX=0;curX<*lpWidth;curX++) // read pixel
{
*(cb+offset+curX) = buf[++bufCurX]; // blue array posision
*(cg+offset+curX) = buf[++bufCurX]; // green array position
*(cr+offset+curX) = buf[++bufCurX]; // red array position
}
offset -=(*lpWidth); // decrease offset for one line
}
}
fclose(fp); // close file
free(buf); // free buffer
pImageData->SetState(IS_LOAD, true); // set imagedata's loaded flag*/
return true; // loaded
}
// 8bit Indexed color bitmap load function
bool LoadBMP8(const char *pszFileName, ImageData *pImageData)
{
long *lpData = (long*)pImageData;
BYTE *cr; // red array
BYTE *cg; // green array
BYTE *cb; // blue array
int offset; // offset for arrays
BYTE *buf; // buffer
int nBufferLen; // buffer length
bool bReverse=false; // BMP in reverse order?
FILE *fp = NULL;
// same as above
pID->InsertEntry(lpData, ENTRY_TAG_DATAPTR);
AddInterface(&IID_IImageDataStruct, (void**)&pID);
if (!AllocateImage())
return false;
if((buf = (BYTE*)malloc(1024)) == NULL)
{
FreeImage(pImageData);
return false;
}
long *lpHeight, *lpWidth, *lpClrUsed;
pID->QueryEntry(ENTRY_TAG_HEIGHT,(void**)&lpHeight);
pID->QueryEntry(ENTRY_TAG_WIDTH,(void**)&lpWidth);
pID->QueryEntry(ENTRY_TAG_COLORCOUNT, (void**)&lpClrUsed);
if ((fp = fopen(pszFileName, "rb"))==NULL)
return false;
fseek(fp, sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER), SEEK_CUR);
// We read 4 times of colors used
// Becuase, RBGQUAD structure has a reserved byte member
fread(buf, (*lpClrUsed)*4, 1, fp);
// get a pointer to the palette
RGBQUAD *pPal = pImageData->GetPalettePtr();
// read used colors from palette
for(int nPalColor=0; nPalColor<(*lpClrUsed); nPalColor++)
{
pPal[nPalColor].rgbRed = buf[nPalColor*4+2];
pPal[nPalColor].rgbGreen = buf[nPalColor*4+1];
pPal[nPalColor].rgbBlue = buf[nPalColor*4+0];
}
// fill rest of palette with 0s (black)
for (;nPalColor<256;nPalColor++)
{
pPal[nPalColor].rgbRed=0;
pPal[nPalColor].rgbGreen=0;
pPal[nPalColor].rgbBlue=0;
}
// we will use only red pointer to save
// indexes. 8bit bitmap has only indexes
// to colors in palette.
cr = pImageData->GetColorPtrRed();
// buffer length for a line
nBufferLen = ((*lpWidth)+3)&0xfffc;
// Tail means there is more than 1 line to read
// This is not a good thing, but we will keep reading file
int nTail = nBufferLen-(*lpWidth);
if ((*lpHeight)<0)
bReverse=true;
if (bReverse)
{
offset = 0;
for (int nRow=0;nRow<(*lpHeight);nRow++)
{
if (fread(cr+offset, (*lpWidth), 1, fp)==0)
break; // if we get here, there is a problem (eof?)
if (nTail)
fread(buf, nTail, 1, fp);
offset += (*lpWidth);
}
}
else
{
offset = ((*lpHeight)-1)*(*lpWidth);
for (int nRow=0;nRow<(*lpHeight);nRow++)
{
if (fread(cr+offset, (*lpWidth), 1, fp)==0)
break; // if we get here, there is a problem (eof?)
if (nTail)
fread(buf, nTail, 1, fp); // read the 'tail'.
offset -=(*lpWidth);
}
}
fclose(fp);
free(buf);
pImageData->SetState(IS_LOAD, true);
return true;
}
};
There are a few interesting functions here and the rest belong to architectural details, such as ImageHandler<T>.
GetImageType
This is BMPLoader class' type discovery function. It collects basic information about the bitmap and returns a value which matches (or does not match) to the types declared in between BEGIN_IMAGE_HANDLERS-END_IMAGE_HANDLERS macros. So, for example, when you provide file name of a 24-bit bitmap file, this function returns BMP_24 (as you can see from the code, too). ImageHandler then calls LoadBMP24.
LoadBMPxx
This set of functions load BMP images and nothing else. The idea was placing the bitmap (i.e. the "image") into a class, which has the basic information, such as type, width, height and color depth. This ImageData class stores bitmap in three distinct byte arrays for red, green and blue values for each pixel. If image is monochrome, only red is used. For example, if a pixel's value is, in RGB, 0x33, 0x66, 0xFF, each array's nth elements are set to the byte value of the color. In this case, current pixel (element) in the byte array for red color is set to 0x33, green is set to 0x66 and blue is set to 0xFF. If image type were monochrome, then only red's value were used.
All you need, I think, is looking at GetImageType and LoadBMPxx functions to read the BMP. Again:
- This is an old code
- You need to add/remove/modify it to use
- Would appreciate, if you refer me in your code

Cheers!









