|
|
//---------------------------------------------------------------
// File: pcximage.cpp
//
// Image manipulation functions for PCX format images.
//---------------------------------------------------------------
#include "stdafx.h"
#include "global.h"
#include "pbrush.h"
#include "pbrusfrm.h"
#include "pbrusvw.h"
#include "minifwnd.h"
#include "bmobject.h"
#include "imgsuprt.h"
#include "imgwnd.h"
#include "imgbrush.h"
#include "imgwell.h"
#include "imgtools.h"
#include "toolbox.h"
#include "imgfile.h"
#include "imgcolor.h"
#include "undo.h"
#include "props.h"
#include "ferr.h"
#include "ctype.h"
#include "cmpmsg.h"
#define COLORMAPLENGTH 48
#define FILLERLENGTH 58
#ifdef PCX_SUPPORT
struct PCXHeader { unsigned char manufacturer; unsigned char version; unsigned char encoding; unsigned char bits_per_pixel_per_plane; short xmin; short ymin; short xmax; short ymax; unsigned short hresolution; unsigned short vresolution; unsigned char colormap[COLORMAPLENGTH]; unsigned char reserved; unsigned char nplanes; unsigned short bytes_per_line; short palette_info; unsigned char filler[FILLERLENGTH]; // Header is 128 bytes
};
#endif
class CFileBuffer : public CObject { DECLARE_DYNCREATE( CFileBuffer )
public:
enum Type { READ, WRITE };
CFileBuffer(); ~CFileBuffer();
BOOL Create( CFile* pfile, Type IO ); short Get ( void ); BOOL Put ( BYTE cByte ); long Seek ( long lOff, UINT nFrom ); BOOL Flush ( void );
private:
void Fill ( void );
enum { MAX_BUFFER = 2048 };
CFile* m_pFile; int m_iBuffPos; int m_iBuffSize; BYTE* m_pBuffer; };
IMPLEMENT_DYNCREATE( CFileBuffer, CObject )
#include "memtrace.h"
/****************************************************************************/
CFileBuffer::CFileBuffer() : CObject() { m_pFile = 0; m_iBuffPos = 0; m_iBuffSize = 0; m_pBuffer = 0; }
/****************************************************************************/
CFileBuffer::~CFileBuffer() { if (m_pBuffer) delete [] m_pBuffer; }
/****************************************************************************/
BOOL CFileBuffer::Create( CFile* pfile, Type IO ) { ASSERT( pfile != NULL );
if (pfile == NULL) return FALSE;
m_pFile = pfile; m_pBuffer = new BYTE[MAX_BUFFER];
if (! m_pBuffer) { theApp.SetMemoryEmergency(); return FALSE; }
if (IO == READ) { Fill();
if (! m_iBuffSize) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrIllformedFile ); return FALSE; } } return TRUE; }
/****************************************************************************/
short CFileBuffer::Get( void ) { if (! m_iBuffSize) return EOF;
short sByte = (short)(unsigned short)m_pBuffer[m_iBuffPos++];
if (m_iBuffPos == m_iBuffSize) Fill();
return sByte; }
/****************************************************************************/
BOOL CFileBuffer::Put( BYTE cByte ) { m_pBuffer[m_iBuffSize++] = cByte;
if (m_iBuffSize == MAX_BUFFER) return Flush();
return TRUE; }
/****************************************************************************/
long CFileBuffer::Seek( long lOff, UINT nFrom ) { long lPos = m_pFile->Seek( lOff, nFrom );
Fill();
return lPos; }
/****************************************************************************/
void CFileBuffer::Fill() { m_iBuffSize = m_pFile->Read( m_pBuffer, MAX_BUFFER ); m_iBuffPos = 0; }
/****************************************************************************/
BOOL CFileBuffer::Flush( void ) { TRY { m_pFile->Write( m_pBuffer, m_iBuffSize ); } CATCH( CFileException, ex ) { m_pFile->Abort(); theApp.SetFileError( IDS_ERROR_SAVE, ex->m_cause );
return FALSE; } END_CATCH
m_iBuffSize = 0;
return TRUE; }
/****************************************************************************/ /****************************************************************************/ #ifdef PCX_SUPPORT
BOOL CBitmapObj::ReadPCX( CFile* pfile ) { if (! pfile->GetLength()) { if (m_hThing) Free();
m_bDirty = TRUE;
return TRUE; }
// if a PCX extension try to load this as a PCX image.
PCXHeader hdr; PBITMAP p_dib; // Device independent bitmap
short bytes_per_line;
pfile->Read( (unsigned char*)&hdr, sizeof( PCXHeader ) );
// Check if image file format is acceptable
if (hdr.manufacturer != 0x0a) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrCantDetermineType ); return FALSE; }
// We only handle 1, 4, 8, or 24-bit images
short bits_per_pixel = hdr.nplanes * hdr.bits_per_pixel_per_plane;
if (bits_per_pixel != 1 && bits_per_pixel != 4 && bits_per_pixel != 8 && bits_per_pixel != 24) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrCantDetermineType ); return FALSE; }
short image_width = hdr.xmax - hdr.xmin + 1; short image_height = hdr.ymax - hdr.ymin + 1;
// Allocate space where the PCX image will be unpacked.
long pcx_image_size = (long) hdr.nplanes * (long) image_height * (long) hdr.bytes_per_line;
BYTE* image = (BYTE*) new BYTE[pcx_image_size];
if (image == NULL) { theApp.SetMemoryEmergency(); return FALSE; }
// Read in PCX image into this area.
CFileBuffer FileBuffer;
if (! FileBuffer.Create( pfile, CFileBuffer::READ )) { delete [] image;
return FALSE; }
// Decode run-length encoded image data
short i; short byte; short count; long pos = 0L;
while ((byte = FileBuffer.Get()) != EOF) { if ((byte & 0xc0) == 0xc0) { count = byte & 0x3f;
if ((byte = FileBuffer.Get()) != EOF) { for (i = 0; i < count; i++) { if (pos >= pcx_image_size) break;
image[pos] = (CHAR)byte; pos++; } } } else { if (pos >= pcx_image_size) break;
image[pos] = (CHAR)byte; pos++; } }
// Allocate memory for the device independent bitmap (DIB)
// Note that the number of bytes in each line of a DIB image
// must be a multiple of 4.
short bytes_per_line_per_plane = (image_width * hdr.bits_per_pixel_per_plane + 7) / 8;
short actual_bytes_per_line = (image_width * hdr.nplanes * hdr.bits_per_pixel_per_plane + 7) / 8; bytes_per_line = actual_bytes_per_line;
if ( bytes_per_line % 4) bytes_per_line = 4 * ( bytes_per_line / 4 + 1);
// Make room for a palette
short palettesize = 16;
if (bits_per_pixel == 1) palettesize = 2;
if (hdr.version >= 5 && bits_per_pixel > 4) { // Go back 769 bytes from the end of the file
FileBuffer.Seek( -769, CFile::end );
if (FileBuffer.Get() == 12) { // There is a 256-color palette following this byte
palettesize = 256; } } // If image has more than 256 colors then there is no palette
if (bits_per_pixel > 8) palettesize = 0;
// Allocate space for the bitmap
if (m_hThing) Free();
m_lMemSize = sizeof( BITMAPINFOHEADER ) + palettesize * sizeof( RGBQUAD ) + (long)bytes_per_line * (long)image_height; if (! Alloc()) return FALSE;
p_dib = (PBITMAP) GlobalLock(m_hThing);
// Set up bitmap info header
LPBITMAPINFOHEADER p_bminfo = (LPBITMAPINFOHEADER)p_dib;
p_bminfo->biSize = sizeof(BITMAPINFOHEADER); p_bminfo->biWidth = image_width; p_bminfo->biHeight = image_height; p_bminfo->biPlanes = 1; p_bminfo->biBitCount = hdr.bits_per_pixel_per_plane * hdr.nplanes; p_bminfo->biCompression = BI_RGB; p_bminfo->biSizeImage = (long)image_height * (long) bytes_per_line; p_bminfo->biXPelsPerMeter = (long)hdr.hresolution; p_bminfo->biYPelsPerMeter = (long)hdr.vresolution; p_bminfo->biClrUsed = 0; p_bminfo->biClrImportant = 0;
// Set up the color palette
if (palettesize > 0) { //***** RGBQUAD *palette = (RGBQUAD*) ((LPSTR)imdata->p_dib
LPRGBQUAD palette = LPRGBQUAD((LPSTR)p_dib + sizeof(BITMAPINFOHEADER));
short palindex;
for (palindex = 0; palindex < palettesize; palindex++) { if (palettesize == 256) { // Read palette from file
palette[palindex].rgbRed = (BYTE)FileBuffer.Get(); palette[palindex].rgbGreen = (BYTE)FileBuffer.Get(); palette[palindex].rgbBlue = (BYTE)FileBuffer.Get(); palette[palindex].rgbReserved = 0; } if (palettesize == 16) { // 16-color palette from PCX header
palette[palindex].rgbRed = (BYTE)hdr.colormap[3*palindex]; palette[palindex].rgbGreen = (BYTE)hdr.colormap[3*palindex+1]; palette[palindex].rgbBlue = (BYTE)hdr.colormap[3*palindex+2]; palette[palindex].rgbReserved = 0; } if (palettesize == 2) { // Set up palette for black and white images
palette[palindex].rgbRed = palindex * 255; palette[palindex].rgbGreen = palindex * 255; palette[palindex].rgbBlue = palindex * 255; palette[palindex].rgbReserved = 0; } } }
// Load image data into the DIB. Note the DIB image must be
// stored "bottom to top" line order. That's why we position
// data at the end of the array so that the image can be
// stored backwards--from the last line to the first.
BYTE* data = (BYTE*)p_dib + ((long)sizeof( BITMAPINFOHEADER ) + palettesize * sizeof( RGBQUAD ) + (image_height - 1) * bytes_per_line);
// Define a macro to access bytes in the PCX image according
// to specified line and plane index.
short lineindex, byteindex, planeindex;
#define bytepos(lineindex, planeindex, byteindex) \
((long)(lineindex)*(long)hdr.bytes_per_line* \ (long)hdr.nplanes + \ (long)(planeindex)*(long)hdr.bytes_per_line + \ (long)(byteindex))
// Construct packed pixels out of decoded PCX image.
short loc; unsigned short onebyte; unsigned short bits_copied; unsigned short few_bits; unsigned short k; unsigned short bbpb = 8/hdr.bits_per_pixel_per_plane;
// Build a mask to pick out bits from each byte of the PCX image
unsigned short himask = 0x80, mask;
if (hdr.bits_per_pixel_per_plane > 1) for (i = 0; i < hdr.bits_per_pixel_per_plane - 1; i++) himask = 0x80 | (himask >> 1);
for (lineindex = 0; lineindex < image_height; lineindex++, data -= bytes_per_line) { if (actual_bytes_per_line < bytes_per_line) for (loc = actual_bytes_per_line; loc < bytes_per_line; loc++) data[loc] = 0;
loc = 0; onebyte = 0; bits_copied = 0;
for (byteindex = 0; byteindex < bytes_per_line_per_plane; byteindex++) { for (k = 0, mask = himask; k < bbpb; k++, mask >>= hdr.bits_per_pixel_per_plane) { // Go through all scan line for all planes and copy bits into
// the data array
for (planeindex = 0; planeindex < hdr.nplanes; planeindex++) { few_bits = image[bytepos(lineindex, planeindex, byteindex)] & mask;
// Shift the selected bits to the most significant position
if (k > 0) few_bits <<= (k*hdr.bits_per_pixel_per_plane);
// OR the bits with current pixel after shifting them right
if (bits_copied > 0) few_bits >>= bits_copied;
onebyte |= few_bits; bits_copied += hdr.bits_per_pixel_per_plane;
if (bits_copied >= 8) { data[loc] = (UCHAR)onebyte; loc++; bits_copied = 0; onebyte = 0; } } } } }
// Success!
delete [] (BYTE*)image;
GlobalUnlock(m_hThing);
return TRUE; }
/****************************************************************************/ #define WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4)
BOOL CBitmapObj::WritePCX( CFile* pfile ) { if (m_pImg == NULL) { // The image has not been loaded, so we'll just copy the
// original out to the file...
ASSERT( m_hThing );
if (! m_hThing) return FALSE; } else { // The image has been loaded and may have been edited, so
// we'll convert it back to a dib to save...
if (! m_hThing) SaveResource( FALSE );
if (! m_hThing) return FALSE; }
// build pcx file from the DIB
PBITMAP p_dib = (PBITMAP)GlobalLock(m_hThing); // Device independent bitmap
PCXHeader hdr; // PCX bitmap header
LPBITMAPINFOHEADER p_bminfo = (LPBITMAPINFOHEADER)p_dib; // Set up bitmap info header
short palettesize = DIBNumColors( (LPSTR)p_dib); // Get palette size
hdr.manufacturer = 10; // hdr.version = (char)((hPalette || (GetDeviceCaps(fileDC, RASTERCAPS) & RC_PALETTE)) ? 5 : 3);
hdr.version = (CHAR)( palettesize ? 5 : 3); hdr.encoding = 1; hdr.xmin = hdr.ymin = 0; hdr.xmax = p_bminfo->biWidth - 1; hdr.ymax = p_bminfo->biHeight- 1; // hdr.hresolution = theApp.ScreenDeviceInfo.iWidthinPels;
// hdr.vresolution = theApp.ScreenDeviceInfo.iHeightinPels;
hdr.hresolution = (WORD)p_bminfo->biXPelsPerMeter; hdr.vresolution = (WORD)p_bminfo->biYPelsPerMeter; hdr.reserved = 0; hdr.nplanes = (BYTE)p_bminfo->biPlanes; //biPlanes should always be 1
hdr.palette_info = (BYTE)p_dib->bmWidthBytes; hdr.bits_per_pixel_per_plane = (CHAR) p_bminfo->biBitCount;
hdr.bytes_per_line = WIDTHBYTES( (LONG) (p_bminfo->biBitCount * p_bminfo->biWidth) );
// Clean up filler
for (int index = FILLERLENGTH; index--; ) hdr.filler[index] ='\0';
// If there are at most 16 colors place them in header
LPRGBQUAD palette = LPRGBQUAD((LPSTR)p_dib + sizeof(BITMAPINFOHEADER)); LPSTR lpDst = (LPSTR)hdr.colormap;
// Clean up colormap
for (index = COLORMAPLENGTH; index--; ) lpDst[index] ='\0';
if (palettesize <= 16) for (index = palettesize; index--; ) { *lpDst++ = palette->rgbRed; /* swap RED and BLUE components */ *lpDst++ = palette->rgbGreen; *lpDst++ = palette->rgbBlue; palette++; }
pfile->Write( (unsigned char*)&hdr, sizeof( PCXHeader ) );
// Now pack the image
// Load image data from the DIB. Note the DIB image is
// stored "bottom to top" line order. That's why we position
// data at the end of the array so that the image can be
// stored backwards--from the last line to the first.
CFileBuffer FileBuffer;
if (! FileBuffer.Create( pfile, CFileBuffer::WRITE )) { GlobalUnlock(m_hThing); return FALSE; }
// find the start of the bitmap data then go to the end of the data
// the PCX is stored in reverse order of the DIB
int TopofData = sizeof( BITMAPINFOHEADER ) + palettesize * sizeof( RGBQUAD ); BYTE* data = (BYTE*)p_dib + TopofData + hdr.bytes_per_line * (p_bminfo->biHeight );
for (index = p_bminfo->biHeight; index--; ) { data -= hdr.bytes_per_line;
if (! PackBuff( &FileBuffer, data, hdr.bytes_per_line )) //convert to run length encoding.
{ GlobalUnlock(m_hThing); return FALSE; } }
if (palettesize == 256) // Write palette to file
{ if (! FileBuffer.Put( 12 )) // Tag number for palette information
{ GlobalUnlock(m_hThing); return FALSE; }
for (index = 0; index < palettesize; index++) { if (! FileBuffer.Put( palette[index].rgbRed ) || ! FileBuffer.Put( palette[index].rgbGreen ) || ! FileBuffer.Put( palette[index].rgbBlue )) { GlobalUnlock(m_hThing); return FALSE; } } }
GlobalUnlock(m_hThing); return FileBuffer.Flush(); }
#endif //PCX_SUPPORT
/****************************************************************************/
/* run length encoding equates */ #define MINcount 2
#define MAXcount 63
#define ESCbits 0xC0
#define BUFFER_SIZE 1024
/* bitmaps are ordered <b, g, r, i> but PCX is ordered <r, g, b, i> ... */
BOOL CBitmapObj::PackBuff(CFileBuffer *FileBuffer, BYTE *PtrDib, int byteWidth ) { BYTE runChar; BYTE runCount; BYTE* endPtr = PtrDib + byteWidth;
for (runCount = 1, runChar = *PtrDib++; PtrDib <= endPtr; ++PtrDib) { if (PtrDib != endPtr && *PtrDib == runChar && runCount < MAXcount) ++runCount; else if (*PtrDib != runChar && runCount < MINcount && (runChar & ESCbits) != ESCbits) { while (runCount--) if (! FileBuffer->Put( runChar )) return FALSE;
runCount = 1; runChar = *PtrDib; } else { runCount |= ESCbits;
if (! FileBuffer->Put( runCount ) || ! FileBuffer->Put( runChar )) return FALSE;
runCount = 1; runChar = *PtrDib; } }
return TRUE; }
/****************************************************************************/
|