Viewing PCX files
Posted
by Pierre Fournier
on March 20th, 1999
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 KBDownload demo project - 10 KB

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