windows-nt/Source/XPSP1/NT/shell/osshell/accesory/mspaint/pcximage.cpp
2020-09-26 16:20:57 +08:00

696 lines
19 KiB
C++

//---------------------------------------------------------------
// 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;
}
/****************************************************************************/