LoadBMPCpp

From GPWiki

Files:GUITutorial_warn.gif The Game Programming Wiki has moved! Files:GUITutorial_warn.gif

The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server.

However, the GPWiki forums are still active! Come say hello.

Sample code to load 8 bit indexed and 24 bit RGB BMP files.

The Load() method gets the file into memory, use the other public functions to access the image properties and data after loading.

// BMP Loader - Codehead 08/11/04
// 
#include <iostream>
#include <fstream>
#include <memory.h>
 
#define IMG_OK              0x1
#define IMG_ERR_NO_FILE     0x2
#define IMG_ERR_MEM_FAIL    0x4
#define IMG_ERR_BAD_FORMAT  0x8
#define IMG_ERR_UNSUPPORTED 0x40
 
class BMPImg
 {
  public:
   BMPImg();
   ~BMPImg();
   int Load(char* szFilename);
   int GetBPP();
   int GetWidth();
   int GetHeight();
   unsigned char* GetImg();       // Return a pointer to image data
   unsigned char* GetPalette();   // Return a pointer to VGA palette
 
  private:
   unsigned int iWidth,iHeight,iEnc;
   short int iBPP,iPlanes;
   int iImgOffset,iDataSize;
   unsigned char *pImage, *pPalette, *pData;
   
   // Internal workers
   int GetFile(char* szFilename);
   int ReadBmpHeader();
   int LoadBmpRaw();
   int LoadBmpRLE8();
   int LoadBmpPalette();
   void FlipImg(); // Inverts image data, BMP is stored in reverse scanline order
 };
 
 
BMPImg::BMPImg()
 { 
  pImage=pPalette=pData=NULL;
  iWidth=iHeight=iBPP=iPlanes=iEnc=0;
 }
 
 
BMPImg::~BMPImg()
 {
  if(pImage)
   {
    delete [] pImage;
    pImage=NULL;
   }
 
  if(pPalette)
   {
    delete [] pPalette;
    pPalette=NULL;
   }
 
  if(pData)
   {
    delete [] pData;
    pData=NULL;
   }
 }
 
 
int BMPImg::Load(char* szFilename)
 {
  int iRet;
 
  // Clear out any existing image and palette
   if(pImage)
    {
     delete [] pImage;
     pImage=NULL;
    }
 
   if(pPalette)
    {
     delete [] pPalette;
     pPalette=NULL;
    }
 
  // Get the file into memory
  iRet=GetFile(szFilename);
 
   if(iRet!=IMG_OK)
    return iRet;
 
  // Process the header
  iRet=ReadBmpHeader();
 
   if(iRet!=IMG_OK)
    return iRet;
 
   if(iBPP<8) // We'll only bother with 8 bit and above
    return IMG_ERR_UNSUPPORTED;
 
  // Get the image data
   switch(iEnc)
    {
     case 0: // Uncompressed
       iRet=LoadBmpRaw(); // 8 / 24 Bit. (24 bit is in BGR order)
      break;
 
     case 1: // RLE 8 (Indexed 256 colour only)
       iRet=LoadBmpRLE8();
      break;
 
     case 2: // RLE 4 (16 Colour indexed, Outdated, not covered here)
      return IMG_ERR_UNSUPPORTED;
 
     case 3: // Bitfields (16/32 bit only, Rare, not covered here)
      return IMG_ERR_UNSUPPORTED;
 
     default:
      return IMG_ERR_UNSUPPORTED;
    }
 
   if(iRet!=IMG_OK)
    return iRet;
 
  // Flip image to correct scanline reversal
  FlipImg();
 
  // Load palette if present
  iRet=LoadBmpPalette();
 
   if(iRet!=IMG_OK)
    return iRet;
 
  // Free the file data
  delete [] pData;
  pData=NULL;
 
  return IMG_OK;
 }
 
 
int BMPImg::GetFile(char* szFilename)
 {
  using namespace std;
  ifstream fIn;
  unsigned long ulSize;
 
  // Open the specified file
  fIn.open(szFilename,ios::binary);
    
   if(fIn==NULL)
    return IMG_ERR_NO_FILE;
 
  // Get file size
  fIn.seekg(0,ios_base::end);
  ulSize=fIn.tellg();
  fIn.seekg(0,ios_base::beg);
 
  // Allocate some space
  // Check and clear pDat, just in case
   if(pData)
    {
     delete [] pData; 
     pData=NULL;
    }
 
  pData=new unsigned char[ulSize];
 
   if(pData==NULL)
    {
     fIn.close();
     return IMG_ERR_MEM_FAIL;
    }
 
  // Read the file into memory
  fIn.read((char*)pData,ulSize);
 
  fIn.close();
 
  return IMG_OK;
 }
 
 
int BMPImg::ReadBmpHeader()
 {
  int iInfo;
 
   if(pData==NULL)
    return IMG_ERR_NO_FILE;
 
   if(pData[0x0]!='B' || pData[0x1]!='M') // BMP ID Bytes, should be 'BM'
    return IMG_ERR_BAD_FORMAT;
 
  memcpy(&iImgOffset,&pData[0xA],4);   // Offset to image data
   
  memcpy(&iInfo,&pData[0xE],4);        // Info header size, should be 0x28
   if(iInfo!=0x28)
    return IMG_ERR_BAD_FORMAT;
 
  memcpy(&iWidth,&pData[0x12],4);   // Image width
  memcpy(&iHeight,&pData[0x16],4);  // Image height
  memcpy(&iPlanes,&pData[0x1A],2);  // Colour planes
  memcpy(&iBPP,&pData[0x1C],2);     // BPP
  memcpy(&iEnc,&pData[0x1E],4);     // Encoding
 
  iDataSize=(iWidth*iHeight*(iBPP/8)); // Calculate Image Data size
 
  return IMG_OK;
 }
 
 
int BMPImg::LoadBmpRaw()
 {
   if(pImage)
    {
     delete [] pImage;
     pImage=NULL;
    }
 
  // Allocate space for the image data
  pImage=new unsigned char[iDataSize];
 
   if(pImage==NULL)
    return IMG_ERR_MEM_FAIL;
 
  memcpy(pImage,&pData[iImgOffset],iDataSize);
 
  return IMG_OK;
 }
 
 
int BMPImg::LoadBmpRLE8()
 {
  unsigned char bOpCode,bVal;
  unsigned char *pSrc;
  int iDcode=1,iCount,iPos,iIndex;
 
  // Allocate space for the image  
   if(pImage)
    delete [] pImage;
 
  pImage=new unsigned char[iDataSize];
 
   if(pImage==NULL)
    return IMG_ERR_MEM_FAIL;
 
  // Get the start of the RLE data
  pSrc=&pData[iImgOffset];
 
  iPos=0;
  iIndex=0;
 
   while(iDcode)
    {
      // Stay on even bytes
      while(iPos%2)
       {
        iPos++;
       }
 
     bOpCode=pSrc[iPos];
     bVal=pSrc[iPos+1];
     iPos+=2;
 
      if(bOpCode>0) // Run mode, Repeat 'bVal' 'OpCode' times
       {
         for(iCount=0;iCount!=bOpCode;iCount++)
          {
           pImage[iIndex]=bVal;
           ++iIndex;
          }
       }
      else // Absolute Mode (Opcode=0), various options
       {
         switch(bVal)
          {
           case 0:  // EOL, no action
            break;
 
           case 1:  // EOF, STOP!
             iDcode=0;
            break;
 
           case 2:  // Reposition, Never used
            break;
 
           default: // Copy the next 'bVal' bytes directly to the image
             for(iCount=bVal;iCount!=0;iCount--)
              {
               pImage[iIndex]=pSrc[iPos];
               ++iIndex;
               ++iPos;
              }
            break;
          }
 
	}
      if(iIndex>iDataSize) // Stop if image size exceeded.
       iDcode=0;
    }
  
  return IMG_OK;
 }
 
 
int BMPImg::LoadBmpPalette()
 {
  int iIndex;
  unsigned char *pPalPos, *pDatPos;
  
   if(pPalette)
    {
     delete [] pPalette;
     pPalette=NULL;
    }
 
   if(iBPP>8) // NULL Palette for RGB images
    return IMG_OK;
 
  // Create space for palette
  pPalette=new unsigned char[768];
 
   if(pPalette==NULL)
    return IMG_ERR_MEM_FAIL;
 
  // Set starting position for pointers
  pPalPos=pPalette;
  pDatPos=&pData[0x36];
 
  // Get colour values, skip redundant 4th value
   for(iIndex=0;iIndex!=256;++iIndex)
    {
     pPalPos[0]=pDatPos[2]; // Red
     pPalPos[1]=pDatPos[1]; // Green
     pPalPos[2]=pDatPos[0]; // Blue
 
     pPalPos+=3;
     pDatPos+=4;
    }
 
  return IMG_OK;
 }
 
 
void BMPImg::FlipImg(void)
 {
  unsigned char bTemp;
  unsigned char *pLine1, *pLine2;
  int iLineLen,iIndex;
 
  iLineLen=iWidth*(iBPP/8);
  pLine1=pImage;
  pLine2=&pImage[iLineLen * (iHeight - 1)];
 
   for( ;pLine1<pLine2;pLine2-=(iLineLen*2))
    {
     for(iIndex=0;iIndex!=iLineLen;pLine1++,pLine2++,iIndex++)
      {
       bTemp=*pLine1;
       *pLine1=*pLine2;
       *pLine2=bTemp;       
      }
    } 
 
 }
 
 
int BMPImg::GetBPP()
 {
  return iBPP;
 }
 
 
int BMPImg::GetWidth()
 {
  return iWidth;
 }
 
 
int BMPImg::GetHeight()
 {
  return iHeight;
 }
 
 
unsigned char* BMPImg::GetImg()
 {
  return pImage;
 }
 
 
unsigned char* BMPImg::GetPalette()
 {
  return pPalette;
 }