dcsimg

Viewing PCX files

WEBINAR:
On-Demand

Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame


This article was contributed by Pierre Fournier .

For my project, I needed to load PCX files and couldn't find the source code to do it.   I took a documentation about the file format of PCX files and decided to give it a shot.  The effort was worth it!  If you would like to know how to load and display single plane PCX files (like I did a couple of weeks ago), then this one's for you.

In my demo, the actual function that reads and decompresses the PCX file is located in my derived CMDIChildWnd class.  1 function and 3 data members have been added:

public:
	void LoadPCX( LPCSTR lpcszFilename );

protected:
	CSize BitmapSize;
	BITMAPINFO *psBmpInfo;
	BYTE *pabRawBitmap;

The function that reads the PCX file goes like this:

void CChildFrame::LoadPCX( LPCSTR lpcszFilename )
{
   // Standard PCX header
   struct PCXHead {
     char   ID;
     char   Version;
     char   Encoding;
     char   BitPerPixel;
     short  X1;
     short  Y1;
     short  X2;
     short  Y2;
     short  HRes;
     short  VRes;
     char   ClrMap[16*3];
     char   Reserved1;
     char   NumPlanes;
     short  BPL;
     short  Pal_t;
     char   Filler[58];
   } sHeader;

   // Open the file and put its entire content in memory
   FILE *pFile = fopen( lpcszFilename, "rb" );
   if ( !pFile )
   {
      MessageBox("Unable to open the PCX file");
      return;
   }
   const long clFileSize = _filelength(_fileno(pFile));
   BYTE *pabFileData = (BYTE *)new BYTE[ clFileSize ];
   fread( pabFileData, clFileSize, 1, pFile );
   fclose( pFile );

   // Get the header
   memcpy( &sHeader, pabFileData, sizeof(sHeader) );

   // Each scan line MUST have a size that can be divided by a 'long' data type
   int iScanLineSize = sHeader.NumPlanes * sHeader.BPL;
   ldiv_t sDivResult = ldiv( iScanLineSize, sizeof(long) );
   if ( sDivResult.rem > 0 )
      iScanLineSize = (iScanLineSize/sizeof(long)+1) * sizeof(long);

   // Set the bitmap size data member
   BitmapSize = CSize( sHeader.X2-sHeader.X1+1, sHeader.Y2-sHeader.Y1+1 );
   const long clImageSize = iScanLineSize * BitmapSize.cy;

   // Set the bitmap information
   psBmpInfo = (BITMAPINFO *)new BYTE[ sizeof(BITMAPINFOHEADER) +
                                             (sizeof(RGBQUAD)*256) ];
   psBmpInfo->bmiHeader.biSize           = sizeof(BITMAPINFOHEADER);
   psBmpInfo->bmiHeader.biWidth          = BitmapSize.cx;
   psBmpInfo->bmiHeader.biHeight         = -BitmapSize.cy;
   psBmpInfo->bmiHeader.biPlanes         = sHeader.NumPlanes;
   psBmpInfo->bmiHeader.biBitCount       = sHeader.BitPerPixel;
   psBmpInfo->bmiHeader.biCompression    = BI_RGB;
   psBmpInfo->bmiHeader.biSizeImage      = 0;
   psBmpInfo->bmiHeader.biXPelsPerMeter  = 0;
   psBmpInfo->bmiHeader.biYPelsPerMeter  = 0;
   psBmpInfo->bmiHeader.biClrUsed        = 0;
   psBmpInfo->bmiHeader.biClrImportant   = 0;

   // Prepare a buffer large enough to hold the image
   pabRawBitmap = (BYTE *)new BYTE[ clImageSize ];
   if ( !pabRawBitmap )
   {
      MessageBox( "Can't allocate memory for the image" );
      delete [] pabFileData;
      return;
   }

   // Get the compressed image
   long lDataPos = 0;
   long lPos = 128;     // That's where the data begins

   for ( int iY=0; iY < BitmapSize.cy; iY++ )
   {
      // Decompress the scan line
      for ( int iX=0; iX < sHeader.BPL; )
      {
         UINT uiValue = pabFileData[lPos++];
         if ( uiValue > 192 ) {  // Two high bits are set = Repeat
            uiValue -= 192;                  // Repeat how many times?
            BYTE Color = pabFileData[lPos++];  // What color?

            if ( iX <= BitmapSize.cx )
            {  // Image data.  Place in the raw bitmap.
               for ( BYTE bRepeat=0; bRepeat < uiValue; bRepeat++ )
               {
                  pabRawBitmap[lDataPos++] = Color;
                  iX++;
               }
            }
            else
               iX += uiValue; // Outside the image.  Skip.
         }
         else
         {
            if ( iX <= BitmapSize.cx )
               pabRawBitmap[lDataPos++] = uiValue;
            iX++;
         }
      }

      // Pad the rest with zeros
      if ( iX < iScanLineSize )
      {
         for ( ;iX < iScanLineSize; iX++ )
            pabRawBitmap[lDataPos++] = 0;
      }
   }

   if ( pabFileData[lPos++] == 12 )          // Simple validation
      // Get the palette
      for ( short Entry=0; Entry < 256; Entry++ )
      {
         psBmpInfo->bmiColors[Entry].rgbRed       = pabFileData[lPos++];
         psBmpInfo->bmiColors[Entry].rgbGreen     = pabFileData[lPos++];
         psBmpInfo->bmiColors[Entry].rgbBlue      = pabFileData[lPos++];
         psBmpInfo->bmiColors[Entry].rgbReserved  = 0;
      }

   delete [] pabFileData;

   // Resize/Repaint the window
   const CSize BorderSize( GetSystemMetrics( SM_CXEDGE ) +
                           GetSystemMetrics( SM_CXFRAME ) - 1,
                           GetSystemMetrics( SM_CYEDGE ) +
                           GetSystemMetrics( SM_CYFRAME ) - 1 );
   SetWindowPos( NULL, 0, 0, BitmapSize.cx + (BorderSize.cx*2),
                 BitmapSize.cy + (BorderSize.cy*2)+GetSystemMetrics(SM_CYCAPTION),
                 SWP_NOMOVE | SWP_NOZORDER );
}

Now that we have the PCX loaded in memory, it is ready to be displayed.

BOOL CChildFrame::OnEraseBkgnd(CDC* pDC) 
{
   if ( pabRawBitmap && psBmpInfo )
      // Display the image
      SetDIBitsToDevice( *pDC, 0, 0,
                         BitmapSize.cx, BitmapSize.cy, 0, 0,
                         0, BitmapSize.cy,
                         pabRawBitmap, psBmpInfo, DIB_RGB_COLORS);

   return CMDIChildWnd::OnEraseBkgnd(pDC);;
}

Downloads

Download source (MSVC++ 6.0) - 33 KB
Download demo project - 10 KB


Comments

  • There are no comments yet. Be the first to comment!

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date