LoadPCXCpp

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 PCX files.

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

// PCX Loader - Codehead 06/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 PCXImg
 {
  public:
   PCXImg();
   ~PCXImg();
   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:
   short int iWidth,iHeight,iBPP,iPlanes,iBPL;
   long lImageSize;
   char bEnc;
   unsigned char *pImage, *pPalette, *pData;
   
   // Internal workers
   int ReadHeader();
   int LoadRLEData();
   int LoadPalette(unsigned long ulDataSize);
 };
 
PCXImg::PCXImg()
 { 
  pImage=pPalette=pData=NULL;
  iWidth=iHeight=iBPP=iPlanes=iBPL=bEnc=0;
 }
 
 
PCXImg::~PCXImg()
 {
  if(pImage)
   {
    delete [] pImage;
    pImage=NULL;
   }
 
  if(pPalette)
   {
    delete [] pPalette;
    pPalette=NULL;
   }
 
  if(pData)
   {
    delete [] pData;
    pData=NULL;
   }
 }
 
 
int PCXImg::Load(char* szFilename)
 {
  using namespace std;
  ifstream fIn;
  unsigned long ulSize;
  int iRet;
 
  // Clear out any existing image and palette
   if(pImage)
    {
     delete [] pImage;
     pImage=NULL;
    }
 
   if(pPalette)
    {
     delete [] pPalette;
     pPalette=NULL;
    }
 
  // 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=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();
 
  // Process the header
  iRet=ReadHeader();
 
   if(iRet!=IMG_OK)
    return iRet;
 
   if(iBPP!=8) // We'll only bother with 8 bit indexed and 24 bit RGB images
    return IMG_ERR_UNSUPPORTED;
 
   if(bEnc!=1) // We only know about RLE compressed images
    return IMG_ERR_UNSUPPORTED;
 
  // Get the image data
  iRet=LoadRLEData();
 
   if(iRet!=IMG_OK)
    return iRet;
 
  // Load palette if present
  iRet=LoadPalette(ulSize);
 
   if(iRet!=IMG_OK)
    return iRet;
 
  // Free the file data
  delete [] pData;
  pData=NULL;
 
  // Update the BPP value to reflect the image format
  iBPP*=iPlanes;
 
  return IMG_OK;
 }
 
 
int PCXImg::ReadHeader()
 {
  unsigned short x1,x2,y1,y2;
 
   if(pData==NULL)
    return IMG_ERR_NO_FILE;
 
   if(pData[0]!=0xA) // PCX ID Byte, should be 0xA
    return IMG_ERR_BAD_FORMAT;
 
   if(pData[1]>5)    // Version, we don't know about anything after v5
    return IMG_ERR_UNSUPPORTED;
 
  bEnc=pData[2];     // Encode flag 1 = RLE Compression
 
   if(pData[3]==1 || pData[3]==2 || pData[3]==4 || pData[3]==8)   // BPP value
    iBPP=pData[3];  
   else
    return IMG_ERR_BAD_FORMAT;
 
  // Get image window and produce width & height values
  memcpy(&x1,&pData[4],2);
  memcpy(&y1,&pData[6],2);
  memcpy(&x2,&pData[8],2);
  memcpy(&y2,&pData[10],2);
 
  iWidth=(x2-x1)+1;
  iHeight=(y2-y1)+1;
 
   if(iWidth<1 || iHeight<1)
    return IMG_ERR_BAD_FORMAT;
 
  // Planes byte.  1 = Indexed,  3 = RGB
  iPlanes=pData[65];
 
  // Bits per line for decoding purposes,
  memcpy(&iBPL,&pData[66],2);
 
  return IMG_OK;
 }
 
 
int PCXImg::LoadRLEData()
 {
  int iLineCount,iBufferLineLen,iImageLineLen;
  long lLinePos=0;
  unsigned char bRunLen;
  unsigned char *pCur,*pLine,*pInterLine;
 
  // Set our pointer to the beginning of the image data
  pCur=&pData[128];
 
  // Calculate line lengths for image and buffer, Allocate the buffer scan line
  iBufferLineLen=iBPL*iPlanes;
  iImageLineLen =iWidth*iPlanes;
  pLine=new unsigned char[iBufferLineLen];
   
   if(pLine==NULL)
    return IMG_ERR_MEM_FAIL;
 
  // Allocate space for the image data
   if(pImage!=NULL)
    delete [] pImage;
 
  pImage=new unsigned char[(iImageLineLen * iHeight)+1];
 
   if(pImage==NULL)
    return IMG_ERR_MEM_FAIL;
 
   // Decode each scanline
   for(iLineCount=0;iLineCount<iHeight;++iLineCount)
    {
     lLinePos=0;
      while(lLinePos<iBufferLineLen)
       {
         if(*pCur > 0xC0) // First 2 bits indicate run of next byte value
          {
           bRunLen=*pCur & 0x3F; // Remaining 6 bits indicate run length
           ++pCur;  // Repeated value 
            for( ;bRunLen!=0;bRunLen--,lLinePos++)
             pLine[lLinePos]=*pCur;
     
           ++pCur; 
          } 
         else
          {
           pLine[lLinePos]=*pCur; // Other bytes are directly copied
           ++lLinePos;
           ++pCur;
          }
       }
 
     // Once we've decoded a line, copy it to the image.
     // This disregards any end-of-line padding inserted during the compression
 
      if(iPlanes==1) // 8 bit images, straight copy
       {
        memcpy(&pImage[iLineCount*iImageLineLen],pLine,iImageLineLen);
       }
      else if(iPlanes==3) // for 24 bit, We have to interleave the RGB values
       {
        pInterLine=&pImage[iLineCount*iImageLineLen];
         for(lLinePos=0;lLinePos!=iWidth;++lLinePos,pInterLine+=3)
          { 
           pInterLine[0]=pLine[lLinePos];
           pInterLine[1]=pLine[lLinePos+iWidth];
           pInterLine[2]=pLine[lLinePos+(iWidth*2)];
          }
       }
 
    } 
 
  return IMG_OK;
 }
 
 
int PCXImg::LoadPalette(unsigned long ulDataSize)
 {
  // Load a 256 color palette
  
   if(pPalette)
    {
     delete [] pPalette;
     pPalette=NULL;
    }
 
   if(iPlanes==3) // 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;
 
  // Start of palette entries should be 769 bytes back from the end of the file
  // First byte is 0x0C
   if(pData[ulDataSize-769]!=0x0C)
    return IMG_ERR_BAD_FORMAT;
 
  memcpy(pPalette,&pData[ulDataSize-768],768);
 
  return IMG_OK;
 }
 
 
int PCXImg::GetBPP()
 {
  return iBPP;
 }
 
 
int PCXImg::GetWidth()
 {
  return iWidth;
 }
 
 
int PCXImg::GetHeight()
 {
  return iHeight;
 }
 
 
unsigned char* PCXImg::GetImg()
 {
  return pImage;
 }
 
 
unsigned char* PCXImg::GetPalette()
 {
  return pPalette;
 }