Skip navigation.

Ambassador

The Royal C++ Embassy

The (infamous) BMP loader

, , ,

The code I will paste below is old, from my university times, but the important point is that "it just works". Do not try to copy and paste completely, because you need to add/remove/modify the code before using it. This, however, may shed a light to those who want to implement an image loader for various image formats. Definetely, it'll help those who want to load a BMP file as well.

// 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 :smile:


Cheers!

Discovery is on the way, despite the crack?Post-launch videos

Write a comment

You must be logged in to write a comment. If you're not a registered member, please sign up.

Download Opera, the fastest and most secure browser
December 2009
S M T W T F S
November 2009January 2010
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31