graphicstools/gdi_tools/bmconv/PBMCOMP.CPP
author jakl.martin@cell-telecom.com
Mon, 06 Dec 2010 18:07:30 +0100
branchNewGraphicsArchitecture
changeset 218 99b3451c560e
parent 0 5d03bc08d59c
permissions -rw-r--r--
Fix for Bug 3890

// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include "BMCONV.H"
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>

extern TRgb* color256Palette;
extern char* color256InversePalette;

static inline bool CanCopy16bppData(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (256 + 1) + //(2 * 128) bytes data + 1 leading byte
		                            (aDataSize % 128 ? (aDataSize % 128) * 2 + 1 : 0);
	return aDestNew <= aDestEnd;
	}

static inline bool CanWrite16bppValue(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (2 + 1) + //2 bytes data + 1 leading byte
		                            (aDataSize % 128 ? 2 + 1 : 0);
	return aDestNew <= aDestEnd;
	}

static inline bool CanCopy8bppData(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (128 + 1) + //128 bytes data + 1 leading byte
		                            (aDataSize % 128 ? (aDataSize % 128) + 1 : 0);
	return aDestNew <= aDestEnd;
	}

static inline bool CanWrite8bppValue(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (1 + 1) + //1 byte data + 1 leading byte
		                            (aDataSize % 128 ? 1 + 1 : 0);
	return aDestNew <= aDestEnd;
	}

static inline bool CanCopy24bppData(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (384 + 1) + //(128 * 3) bytes data + 1 leading byte
		                            (aDataSize % 128 ? (aDataSize % 128) * 3 + 1 : 0);
	return aDestNew <= aDestEnd;
	}

static inline bool CanWrite24bppValue(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (3 + 1) + //3 bytes data + 1 leading byte
		                            (aDataSize % 128 ? 3 + 1 : 0);
	return aDestNew <= aDestEnd;
	}

static inline bool CanCopy32bppData(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (512 + 1) + //(128 * 4) bytes data + 1 leading byte
		                            (aDataSize % 128 ? (aDataSize % 128) * 4 + 1 : 0);
	return aDestNew <= aDestEnd;
	}

static inline bool CanWrite32bppValue(const char* aDest, const char* aDestEnd, int aDataSize)
	{
	const char* aDestNew = 	aDest + (aDataSize / 128) * (4 + 1) + //4 bytes data + 1 leading byte
		                            (aDataSize % 128 ? 4 + 1 : 0);
	return aDestNew <= aDestEnd;
	}

BitmapCompiler::BitmapCompiler(char* aSourcefilenames[],int aNumSources):
	iSourcefilenames(aSourcefilenames),
	iPbmSources(NULL),
	iNumSources(aNumSources)
	{}

BitmapCompiler::~BitmapCompiler()
	{
	iDestFile.close();
	if(iPbmSources)
		for(int count=0;count<iNumSources;count++)
			delete iPbmSources[count];
	delete [] iPbmSources;
	iPbmSources = NULL;
	}

int BitmapCompiler::Compile(TStoreType aSt,int aCompress,char* aDestfilename,char* aHeaderFilename,char* aPaletteFilename)
	{
	int ret = LoadPalette(aPaletteFilename);
	if (ret)
		return ret;

	iDestFile.open(aDestfilename, ios::out | ios::binary);
	if(iDestFile.is_open()==0)
		return DestFile;
	if (iNumSources < 1)
		return SourceFile;

	if (iNumSources==1 && ((iSourcefilenames[0][0]==OPTCHAR || iSourcefilenames[0][0]==ALTERNATE_OPTCHAR)) && iSourcefilenames[0][1]=='m')
		{
		// Source is an existing MBM file, presumably because we want to convert
		// a filestore into a ROM image.
		if (aHeaderFilename)
			{
			cout << "Header file generation is not permitted with /m or -m\n";
			aHeaderFilename = NULL;
			}
		iSourcefilenames[0]+=2;
		EpocLoader pl;
		int romFormat;
		ret = pl.EpocBitmapCount(iSourcefilenames[0], iNumSources, romFormat);
		if (!ret)
			ret = LoadPbmFile(iSourcefilenames[0]);
		}
	else
		{
		// Read the usual list of [OPT]bmp_n source bitmaps.
		ret = LoadSourcefiles();
		}
	if (ret)
		return ret;

	ret = CreateHeader(aHeaderFilename);
	if (ret)
		return ret;

	if (aSt == ERomStore || aSt == ECompressedRomStore)
		ret = RomImage(aSt == ECompressedRomStore);
	else
		ret = FileImage(aCompress);
	return ret;
	};

int BitmapCompiler::AllocatePbmSourcesArray()
	{
	iPbmSources = new SEpocBitmapHeader*[iNumSources];
	if(iPbmSources == NULL)
		return NoMemory;

	memset(iPbmSources,0xff,sizeof(SEpocBitmapHeader*)*iNumSources);
	int count;
	for(count=0;count<iNumSources;count++)
		iPbmSources[count]=NULL;
	return NoError;
	}

int BitmapCompiler::LoadSourcefiles()
	{
	int ret = AllocatePbmSourcesArray();
	if (ret)
		return ret;
	for(int count=0;count<iNumSources;count++)
		{
		bool useAlpha = false;
		int bpp = 2;
		TBitmapColor color = EMonochromeBitmap;
		BitmapLoader bmpload;
		if(iSourcefilenames[count][0]==OPTCHAR || iSourcefilenames[count][0]==ALTERNATE_OPTCHAR)
			{
			iSourcefilenames[count]++;
			if(iSourcefilenames[count][0]=='c')
				{
				color = EColorBitmap;
				iSourcefilenames[count]++;
				}
			bpp=iSourcefilenames[count][0]-'0';
			iSourcefilenames[count]++;
			int next=iSourcefilenames[count][0]-'0';
			if(next==2 || next==6 || next==4)
				{
				bpp=bpp*10+next;
				iSourcefilenames[count]++;
				}
			if(iSourcefilenames[count][0]=='a')
				{
				useAlpha = true;
				iSourcefilenames[count]++;
				if (color!=EColorBitmap || bpp!=32)
					return Bpp;
				}
			if (color == EColorBitmap && bpp!=4 && bpp!=8 && bpp!=12 && bpp!=16 && bpp!=24 && bpp!=32)
				return Bpp;
			if (color == EMonochromeBitmap && bpp!=1 && bpp!=2 && bpp!=4 && bpp!=8)
				return Bpp;
			}
		if (color == EColorBitmap && useAlpha)
			color = EColorBitmapAlpha;
		ret = bmpload.LoadBitmap(iSourcefilenames[count],bpp,color,iPbmSources[count]);
		if (ret)
			return ret;
		}
	return NoError;
	}

int BitmapCompiler::LoadPbmFile(char* aPbmFilename)
	{
	int ret = AllocatePbmSourcesArray();
	if (ret)
		return ret;
	for(int count=0;count<iNumSources;count++)
		{
		EpocLoader pl;
		ret = pl.LoadEpocBitmap(aPbmFilename,count);
		if (!ret)
			ret = pl.DupBitmap(iPbmSources[count]);
		if (ret)
			return ret;
		}
	return NoError;
	}

int BitmapCompiler::RomImage(bool aCompress)
	{
	int count=0;

	if (aCompress)
		{
		for(; count<iNumSources; count++)
			{
			int ret = CompressBitmap(iPbmSources[count]);
			if (ret)
				return ret;
			}
		}

	int ret = WriteRomheader();
	if (ret)
		return ret;

	for(count=0;count<iNumSources;count++)
		{
		ret = WriteRombitmap(iPbmSources[count]);
		if (ret)
			return ret;
		}
	return NoError;
	}

int BitmapCompiler::FileImage(int aCompress)
	{
	int count = 0;
	if(aCompress)
		{
		for(;count<iNumSources;count++)
			{
			int ret = CompressBitmap(iPbmSources[count]);
			if(ret)
				return ret;
			}
		}
	int ret = WriteFileheader();
	if (ret)
		return ret;
	for (count=0;count<iNumSources;count++)
		{
		ret = WriteFilebitmap(iPbmSources[count]);
		if (ret)
			return ret;
		}
	ret = WriteHeadStream();
	return ret;
	}

int BitmapCompiler::WriteRomheader()
	{
	iDestFile.write((char*)&KMultiBitmapRomImageUid,4);
	iDestFile.write((char*)&iNumSources,4);
	int byteswritten=8+(iNumSources*4);
	for (int count=0;count<iNumSources;count++)
		{
		iDestFile.write((char*)&byteswritten,4);
		byteswritten+=(((iPbmSources[count]->iBitmapSize)+3)/4*4)-sizeof(SEpocBitmapHeader)+sizeof(Bitmap);
		}
	return NoError;
	}

int BitmapCompiler::WriteRombitmap(SEpocBitmapHeader* aPbm)
	{
	if (aPbm->iPaletteEntries != 0)
	    return PaletteSupportNotImplemented;

	int bitmapsize = aPbm->iBitmapSize-sizeof(SEpocBitmapHeader)+sizeof(Bitmap);
	bitmapsize = ((bitmapsize+3)/4)*4;

	char* buffer = new char[bitmapsize];
	if (buffer == NULL)
		return NoMemory;
	memset(buffer,0xff,bitmapsize);

	Bitmap* bmp = (Bitmap*)buffer;
	bmp->iUid=KCBitwiseBitmapUid;
	TBitmapColor color = aPbm->iColor;

	switch(aPbm->iBitsPerPixel)
		{ // for corresponding enums, see TDisplayMode in <gdi.h>
	case 1:
		bmp->iDispMode=1;
		break;
	case 2:
		bmp->iDispMode=2;
		break;
	case 4:
		if (color == EMonochromeBitmap)
			bmp->iDispMode=3;
		else
			bmp->iDispMode=5;
		break;
	case 8:
		if (color == EMonochromeBitmap)
			bmp->iDispMode=4;
		else
			bmp->iDispMode=6;
		break;
	case 12:
		bmp->iDispMode=10;
		break;
	case 16:
		bmp->iDispMode=7;
		break;
	case 24:
		bmp->iDispMode=8;
		break;
	case 32:
		if (color == EColorBitmapAlpha)
			bmp->iDispMode=12; // Color16MA
		else
			bmp->iDispMode=11; // Color16MU
		break;
	default:
		delete [] buffer;
		return SourceFile;
		}

	bmp->iHeap = NULL;
	bmp->iPile = NULL;
	bmp->iHeader = *aPbm;
	bmp->iByteWidth = BitmapUtils::ByteWidth(bmp->iHeader.iWidthInPixels,bmp->iHeader.iBitsPerPixel);
	bmp->iDataOffset = sizeof(Bitmap);

	CopyTail(buffer + sizeof(Bitmap), aPbm, aPbm->iBitmapSize, sizeof(SEpocBitmapHeader));
	iDestFile.write(buffer,bitmapsize);

	delete [] buffer;

	return NoError;
	}

int BitmapCompiler::WriteFileheader()
	{
	int zero=0;
	iDestFile.write((char*)&KWriteOnceFileStoreUid,4);
	iDestFile.write((char*)&KMultiBitmapFileImageUid,4);
	iDestFile.write((char*)&zero,4);
	iDestFile.write((char*)&KMultiBitmapFileImageChecksum,4);
	int byteswritten=16;
	for(int count=0;count<iNumSources;count++)
		{
		byteswritten+=iPbmSources[count]->iBitmapSize;
		}
	byteswritten+=4;
	iDestFile.write((char*)&byteswritten,4);
	return NoError;
	}

int BitmapCompiler::WriteHeadStream()
	{
	iDestFile.write((char*)&iNumSources,4);
	int byteswritten=20;
	for(int count=0;count<iNumSources;count++)
		{
		iDestFile.write((char*)&byteswritten,4);
		byteswritten+=iPbmSources[count]->iBitmapSize;
		}
	return NoError;
	}

int BitmapCompiler::WriteFilebitmap(SEpocBitmapHeader* aPbm)
	{
	if (aPbm->iPaletteEntries != 0)
		return PaletteSupportNotImplemented;

	int dataSize = aPbm->iBitmapSize - sizeof(SEpocBitmapHeader);

	iDestFile.write((char*)(aPbm),sizeof(SEpocBitmapHeader));
	// WritePalette()
	iDestFile.write(((char*)(aPbm)) + sizeof(SEpocBitmapHeader),dataSize);

	return NoError;
	}

void BitmapCompiler::WritePalette()
	{
	for (int index = 0; index < 256; index++)
		{
		iDestFile.write((char*)(&iPalette[index]),3);
		}
	}

int BitmapCompiler::LoadPalette(char* aPaletteFilename)
	{
	if (!aPaletteFilename)
		{
		iDefaultPalette = 1;
		return NoError;
		}

	iDefaultPalette = 0;
	color256Palette = iPalette;
	color256InversePalette = iInversePalette;

	struct stat fileinfo;
	if (stat(aPaletteFilename,&fileinfo)==-1)
		return CommandFile;

	int filesize = fileinfo.st_size;
	if (filesize == 0)
		return PaletteFile;

#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
	fstream paletteFile(aPaletteFilename, ios::in | ios::binary);
#else
	fstream paletteFile(aPaletteFilename, ios::in | ios::binary | ios::nocreate);
#endif

	if(!paletteFile.is_open())
		return PaletteFile;

	char* paletteData = new char[filesize];
	if (!paletteData)
		return NoMemory;

	memset(paletteData,0,filesize);
	paletteFile.read(paletteData,filesize);
	paletteFile.close();

	char* dataPtr = (char*)paletteData;
	char* dataPtrLimit = dataPtr + filesize;

	for (int index = 0; index < 256; index++)
		{
		char hexBuf[10];
		int ret = ReadHexString(hexBuf,dataPtr,dataPtrLimit);
		if (ret)
			{
			delete paletteData;
			return ret;
			}

		int red = HexToInt(hexBuf[8],hexBuf[9]);
		int green = HexToInt(hexBuf[6],hexBuf[7]);
		int blue = HexToInt(hexBuf[4],hexBuf[5]);

		iPalette[index] = TRgb(red,green,blue);
		}

	delete paletteData;
	CalculateInversePalette();
	return NoError;
	}

void BitmapCompiler::CalculateInversePalette()
	{
	for (int index = 0; index < 4096; index++)
		{
		TRgb color = TRgb((index & 0x00f) * 17,((index & 0x0f0) >> 4) * 17,((index & 0xf00) >> 8) * 17);

		int nearest = 0;
		int distance = iPalette[0].Difference(color);

		for (int paletteIndex = 0; paletteIndex < 256; paletteIndex++)
			{
			TRgb paletteColor = iPalette[paletteIndex];

			if (paletteColor == color)
				{
				nearest = paletteIndex;
				break;
				}

			int paletteDistance = paletteColor.Difference(color);
			if (paletteDistance < distance)
				{
				nearest = paletteIndex;
				distance = paletteDistance;
				}
			}

		iInversePalette[index] = (char)nearest;
		TRgb palColor = iPalette[nearest];
		color = palColor;
		}
	}

int BitmapCompiler::CreateHeader(char* aHeaderFilename)
	{
	if (!aHeaderFilename)
		return NoError;

	fstream hf(aHeaderFilename,ios::out);
	if(!hf.is_open())
		return DestFile;

	char* basename = strrchr(aHeaderFilename,'\\');
	if (basename==0)
		basename = aHeaderFilename;
	else
		basename++;	// skip directory separator

	char unadornedFile[256];
	strcpy(unadornedFile, UnadornedName(aHeaderFilename));

	hf << "// " << basename << "\n";
	hf << "// Generated by BitmapCompiler\n";
	hf << "// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).\n";
	hf << "//\n\n";
	hf << "enum TMbm" << unadornedFile << "\n";
	hf << "\t{\n";

	for (int count=0;count<iNumSources;count++)
		{
		hf << "\tEMbm" << unadornedFile << UnadornedName(iSourcefilenames[count]);
		if(count<iNumSources-1)
			hf << ',';
		hf << '\n';
		}
	hf << "\t};\n";
	hf.close();
	return NoError;
	}

char* BitmapCompiler::UnadornedName(char* aName)
	{
	int len = strlen(aName);

	char* foundDir = strrchr(aName,'\\');
	char* foundUrl = strrchr(aName,'/');
	char* foundExt = strrchr(aName,'.');

	char* foundPath = (foundDir > foundUrl) ? foundDir : foundUrl;
	char* firstchar = (foundPath > 0) ? foundPath + 1 : aName;
	char* lastchar = (foundExt > firstchar) ? foundExt : aName+len;

	static char result[256];
	if (lastchar-firstchar > 255)
		{
		strcpy(result,"NameTooLong");
		return result;
		}
	int i=0;
	result[0] = (char)toupper(*firstchar);
	firstchar+=1;
	for (i=1; firstchar<lastchar; i++,firstchar++)
		{
		result[i] = (char)tolower(*firstchar);
		}
	result[i] = '\0';
	return result;
	}

int BitmapCompiler::CompressBitmap(SEpocBitmapHeader*& aPbm)
	{
	int originalsize = aPbm->iBitmapSize-sizeof(SEpocBitmapHeader);
	char* newbits=new char[originalsize+sizeof(SEpocBitmapHeader)];
	if(!newbits)
		return NoMemory;

	memset(newbits,0xff,sizeof(SEpocBitmapHeader)+originalsize);
	memcpy(newbits,aPbm,sizeof(SEpocBitmapHeader));
	char* newbitsptr=newbits+sizeof(SEpocBitmapHeader);
	char* oldbits=((char*)(aPbm))+sizeof(SEpocBitmapHeader);

	TBitmapfileCompression compression = ENoBitmapCompression;
	int ret = NoCompression;

	if (aPbm->iBitsPerPixel <= 8)
		{
		compression = EByteRLECompression;
		ret = CompressByteData(newbitsptr,oldbits,originalsize);
		}
	else if (aPbm->iBitsPerPixel == 12)
		{
		compression = ETwelveBitRLECompression;
		ret = CompressTwelveBitData(newbitsptr,oldbits,originalsize);
		}
	else if (aPbm->iBitsPerPixel == 16)
		{
		compression = ESixteenBitRLECompression;
		ret = CompressSixteenBitData(newbitsptr,oldbits,originalsize);
		}
	else if (aPbm->iBitsPerPixel == 24)
		{
		compression = ETwentyFourBitRLECompression;
		ret = CompressTwentyFourBitData(newbitsptr,oldbits,originalsize);
		}
	else if (aPbm->iBitsPerPixel == 32 && (aPbm->iColor == EColorBitmap))
		{
		compression = EThirtyTwoUBitRLECompression;
		ret = CompressThirtyTwoUBitData(newbitsptr,oldbits,originalsize);
		}
	else if (aPbm->iBitsPerPixel == 32 && (aPbm->iColor == EColorBitmapAlpha))
		{
		compression = EThirtyTwoABitRLECompression;
		ret = CompressThirtyTwoABitData(newbitsptr,oldbits,originalsize);
		}

	if(ret)
		{
		delete [] newbits;
		if(ret>0)
			return ret;
		return NoError;
		}

	delete aPbm;
	aPbm = (SEpocBitmapHeader*)newbits;
	aPbm->iBitmapSize = newbitsptr-(newbits+sizeof(SEpocBitmapHeader))+sizeof(SEpocBitmapHeader);
	aPbm->iCompression = compression;
	return NoError;
	}

int BitmapCompiler::CompressByteData(char*& aDest,char* aSrce,int aSize)
	{
	const char* destEnd = aDest + aSize;
	char* bytepointer=aSrce;
	char* limitpointer=bytepointer+aSize;
	int margin = (aSize>>6);
	char* limitmargin = limitpointer - ((margin > 4) ? margin : 4);
	char* cutoffpointer=aDest+(aSize>>1)+(aSize>>2);
	int ret=NoError;
	char* oldbytepointer=NULL;
	while(bytepointer<limitmargin)
		{
		char value=*bytepointer;
		if(*(bytepointer+1)==value && *(bytepointer+2)==value)
			{
			oldbytepointer=bytepointer;
			bytepointer+=3;
			while ( ( bytepointer < limitmargin ) && ( *bytepointer == value ) )
				bytepointer++;
			ret = WriteCompressedByteValues(aDest,value,bytepointer-oldbytepointer, destEnd);
			if(ret) return ret;
			}
		else
			{
			oldbytepointer=bytepointer;
			while((bytepointer<limitmargin) && (*(bytepointer+1)!=value || *(bytepointer+2)!=value))
				{
				bytepointer++;
				value=*bytepointer;
				}
			ret = WriteCompressedByteData(aDest,oldbytepointer,bytepointer-oldbytepointer, destEnd);
			if(ret) return ret;
			}
		if(aDest>cutoffpointer)
			return NoCompression;
		}

	int remaining = limitpointer-bytepointer;
	if(remaining > 0)
		{
		if (aDest + remaining > cutoffpointer)
			return NoCompression;
		ret=WriteCompressedByteData(aDest,bytepointer,remaining, destEnd);
		if(ret) return ret;
		}
	return NoError;
	}

int BitmapCompiler::WriteCompressedByteData(char*& aDest,char* aData,int aLength, const char* aDestEnd)
	{
	if(!CanCopy8bppData(aDest, aDestEnd, aLength))
		return NoCompression;
	while(aLength>128)
		{
		*aDest++=-128;
		for(int count=0;count<128;count++)
			*aDest++=*aData++;
		aLength-=128;
		}
	if(aLength>128 || aLength<1) return CompressionError;
	*aDest++=char(-aLength);
	for(int count=0;count<aLength;count++)
		*aDest++=*aData++;
	return NoError;
	}

int BitmapCompiler::WriteCompressedByteValues(char*& aDest,char aValue,int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanWrite8bppValue(aDest, aDestEnd, aLength))
		return NoCompression;

	while (aLength > 128)
		{
		*aDest++ = 127;
		*aDest++ = aValue;
		aLength -= 128;
		}

	*aDest++ = char(aLength-1);
	*aDest++ = aValue;

	return NoError;
	}

int BitmapCompiler::CompressTwelveBitData(char*& aDest,char* aSrce,int aSizeInBytes)
	{
	unsigned short* srcePtr = (unsigned short*)aSrce;
	unsigned short* srceLimitPtr = srcePtr + (aSizeInBytes / 2);
	unsigned short* dest = (unsigned short*)aDest;

	while (srcePtr < srceLimitPtr)
		{
		unsigned short* runStartPtr = srcePtr;
		unsigned short value = *srcePtr;
		do
			srcePtr++;
		while (srcePtr < srceLimitPtr && *srcePtr == value);
		WriteCompressedTwelveBitData(dest,*runStartPtr,srcePtr - runStartPtr); // Ignore error return as 12bpp compression never fails
		}

	aDest = (char*)dest;
	return NoError;
	}

int BitmapCompiler::WriteCompressedTwelveBitData(unsigned short*& aDest,unsigned short aData,int aLength)
	{
	// The run length l is stored as l-1 in the top 4 bits of each 16-bit word (between 1 and 16)
	aData &= 0x0fff;
	unsigned short maxLengthData = (unsigned short)(aData | 0xf000);

	while(aLength>16)
		{
		*aDest++ = maxLengthData;
		aLength -= 16;
		}

	if (aLength > 0)
		*aDest++ = (unsigned short)(aData | ((aLength - 1) << 12));

	return NoError;
	}

int BitmapCompiler::CompressSixteenBitData(char*& aDest,char* aSrce,int aSizeInBytes)
	{
	char* destEnd = aDest + aSizeInBytes;
	unsigned short* srcePtr = (unsigned short*)aSrce;
	unsigned short* srceLimitPtr = srcePtr + (aSizeInBytes / 2);
	unsigned short* srceLimitPtrMinusOne = srceLimitPtr - 1;
	char* destCompressedLimitPtr = aDest + (aSizeInBytes * 7 / 8);
	int ret = NoError;

	while (srcePtr < srceLimitPtrMinusOne)
		{
		unsigned short value = *srcePtr;
		unsigned short* runStartPtr = srcePtr++;

		if(*srcePtr == value)
			{
			do
				srcePtr++;
			while ( ( srcePtr < srceLimitPtr ) && ( *srcePtr == value ) );

			ret = WriteCompressedSixteenBitValues(aDest,value,srcePtr-runStartPtr, destEnd);
			if (ret)
				return ret;
			}
		else
			{
			value = *srcePtr;
			while (srcePtr < srceLimitPtrMinusOne && *(srcePtr + 1) != value)
				{
				srcePtr++;
				value = *srcePtr;
				}

			ret = WriteCompressedSixteenBitData(aDest,runStartPtr,srcePtr - runStartPtr, destEnd);
			if (ret)
				return ret;
			}
		if (aDest > destCompressedLimitPtr)
			return NoCompression;
		}

	if (srcePtr < srceLimitPtr)
		{
		ret = WriteCompressedSixteenBitData(aDest,srcePtr,srceLimitPtr - srcePtr, destEnd);
		if (ret)
			return ret;
		}

	if (aDest > destCompressedLimitPtr)
		return NoCompression;
	return NoError;
	}

int BitmapCompiler::WriteCompressedSixteenBitData(char*& aDest,unsigned short* aData,
												  int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanCopy16bppData(aDest, aDestEnd, aLength))
		return NoCompression;

	char* srcePtr = (char*)aData;

	while (aLength > 128)
		{
		*aDest++ = -128;
		memcpy(aDest,srcePtr,256);
		aDest += 256;
		srcePtr += 256;
		aLength -= 128;
		}

	*aDest++ = char(-aLength);

	int remainingBytes = aLength * 2;
	memcpy(aDest,srcePtr,remainingBytes);
	aDest += remainingBytes;

	return NoError;
	}

int BitmapCompiler::WriteCompressedSixteenBitValues(char*& aDest,unsigned short aValue,
													int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanWrite16bppValue(aDest, aDestEnd, aLength))
		return NoCompression;

	char lowByte = char(aValue);
	char highByte = char(aValue >> 8);

	while (aLength > 128)
		{
		*aDest++ = 127;
		*aDest++ = lowByte;
		*aDest++ = highByte;
		aLength -= 128;
		}

	*aDest++ = char(aLength-1);
	*aDest++ = lowByte;
	*aDest++ = highByte;

	return NoError;
	}

int BitmapCompiler::CompressTwentyFourBitData(char*& aDest,char* aSrce,int aSizeInBytes)
	{
	const char* destEnd = aDest + aSizeInBytes; 
	char* srceLimitPtr = aSrce + aSizeInBytes;
	char* srceLimitPtrMinusThree = srceLimitPtr - 3; // three bytes == one pixel
	char* destCompressedLimitPtr = aDest + (aSizeInBytes * 7 / 8);
	int ret = NoError;

	while (aSrce < srceLimitPtrMinusThree)
		{
		char* runStartPtr = aSrce;
		char component1 = *aSrce++;
		char component2 = *aSrce++;
		char component3 = *aSrce++;

		if (TrueColorPointerCompare(aSrce,component1,component2,component3))
			{
			do
				aSrce += 3;
			while (aSrce < srceLimitPtr && TrueColorPointerCompare(aSrce,component1,component2,component3));

			ret = WriteCompressedTwentyFourBitValues(aDest,component1,component2,component3,(aSrce - runStartPtr) / 3, destEnd);
			if (ret)
				return ret;
			}
		else
			{
			bool more  = true;
			bool eqRun = false;
			do
				{
				component1 = *aSrce++;
				component2 = *aSrce++;
				component3 = *aSrce++;
				more = (aSrce < srceLimitPtr);
				eqRun = more && TrueColorPointerCompare(aSrce,component1,component2,component3);
				}
			while (more && !eqRun);
			if (eqRun)
				aSrce -= 3;
			int pixelLength = (aSrce - runStartPtr) / 3;
			ret = WriteCompressedTwentyFourBitData(aDest,runStartPtr,pixelLength,destEnd);
			if (ret)
				return ret;
			}
		if (aDest > destCompressedLimitPtr)
			return NoCompression;
		}

	if (aSrce < srceLimitPtr)
		{
		ret = WriteCompressedTwentyFourBitData(aDest,aSrce,(srceLimitPtr - aSrce) / 3, destEnd);
		if (ret)
			return ret;
		}

	if (aDest > destCompressedLimitPtr)
		return NoCompression;
	return NoError;
	}

int BitmapCompiler::WriteCompressedTwentyFourBitData(char*& aDest,char* aData,
													 int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanCopy24bppData(aDest, aDestEnd, aLength))
		return NoCompression;

	while (aLength > 128)
		{
		*aDest++ = -128;
		memcpy(aDest,aData,384);
		aDest += 384;
		aData += 384;
		aLength -= 128;
		}

	*aDest++ = char(-aLength);

	int remainingBytes = aLength * 3;
	memcpy(aDest,aData,remainingBytes);
	aDest += remainingBytes;

	return NoError;
	}

int BitmapCompiler::WriteCompressedTwentyFourBitValues(char*& aDest,char aComponent1,
													   char aComponent2,char aComponent3,
													   int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanWrite24bppValue(aDest, aDestEnd, aLength))
		return NoCompression;

	while (aLength > 128)
		{
		*aDest++ = 127;
		*aDest++ = aComponent1;
		*aDest++ = aComponent2;
		*aDest++ = aComponent3;
		aLength -= 128;
		}

	*aDest++ = char(aLength-1);
	*aDest++ = aComponent1;
	*aDest++ = aComponent2;
	*aDest++ = aComponent3;

	return NoError;
	}

/** The function encodes 32-bit buffer into 24-bit stream by using RLE compression algorithm*/
int BitmapCompiler::CompressThirtyTwoUBitData(char*& aDest,char* aSrce,int aSizeInBytes)
	{
	const char* destEnd = aDest + aSizeInBytes; 
	char* srceLimitPtr = aSrce + aSizeInBytes;
	char* srceLimitPtrMinusFour = srceLimitPtr - 4; // four bytes == one pixel
	char* destCompressedLimitPtr = aDest + (aSizeInBytes * 7 / 8);
	int ret = NoError;

	while (aSrce < srceLimitPtrMinusFour)
		{
		char* runStartPtr = aSrce;
		char component1 = *aSrce++;
		char component2 = *aSrce++;
		char component3 = *aSrce++;
		aSrce++;

		if (TrueColorPointerCompare(aSrce,component1,component2,component3))
			{
			do
				aSrce += 4;
			while (aSrce < srceLimitPtr && TrueColorPointerCompare(aSrce,component1,component2,component3));

			ret = WriteCompressedThirtyTwoUBitValues(aDest,component1,component2,component3,(aSrce - runStartPtr) / 4, destEnd);
			if (ret)
				return ret;
			}
		else //non repeating pixel buffer
			{
			bool more  = true;
			bool eqRun = false;
			do {
				component1 = *aSrce++;
				component2 = *aSrce++;
				component3 = *aSrce++;
				aSrce++; //last byte is unused (no alpha component)
				more = (aSrce < srceLimitPtr);
				eqRun = more && TrueColorPointerCompare(aSrce,component1,component2,component3);
				} while (more && !eqRun);
			if (eqRun)
				aSrce -= 4;
			int pixelLength = (aSrce - runStartPtr) / 4;
			ret = WriteCompressedThirtyTwoUBitData(aDest,runStartPtr,pixelLength,destEnd);
 			if (ret)
				return ret;
			}
		if (aDest > destCompressedLimitPtr)
			return NoCompression;
		}

	if (aSrce < srceLimitPtr)
		{
		ret = WriteCompressedThirtyTwoUBitData(aDest,aSrce,(srceLimitPtr - aSrce) / 4, destEnd);
		if (ret)
			return ret;
		}

	if (aDest > destCompressedLimitPtr)
		return NoCompression;
	return NoError;
	}

/** The function copies non repeating 32-bit buffer to 24-bit compressed buffer*/
int BitmapCompiler::WriteCompressedThirtyTwoUBitData(char*& aDest,char* aData,
													 int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanCopy24bppData(aDest, aDestEnd, aLength))
		return NoCompression;

	while (aLength > 128)
		{
		*aDest++ = -128;
		for(int ii = 0; ii < 128; ii++)
			{
			memcpy(aDest,aData, 3);
			aDest += 3;
			aData += 4;
			}
		aLength -= 128;
		}

	*aDest++ = char(-aLength);

	while(aLength--)
		{
		memcpy(aDest,aData, 3);
		aDest += 3;
		aData += 4;
		}

	return NoError;
	}

/** The function copies repeating colour to the 24-bit compressed buffer */
int BitmapCompiler::WriteCompressedThirtyTwoUBitValues(char*& aDest,char aComponent1,
													   char aComponent2,char aComponent3,
													   int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanWrite24bppValue(aDest, aDestEnd, aLength))
		return NoCompression;
	while (aLength > 128)
		{
		*aDest++ = 127;
		*aDest++ = aComponent1;
		*aDest++ = aComponent2;
		*aDest++ = aComponent3;
		aLength -= 128;
		}

	*aDest++ = char(aLength-1);
	*aDest++ = aComponent1;
	*aDest++ = aComponent2;
	*aDest++ = aComponent3;

	return NoError;
	}

int BitmapCompiler::TrueColorPointerCompare(char* aColorPointer,char aComponent1,char aComponent2,char aComponent3)
	{
	return (*aColorPointer == aComponent1 && *(aColorPointer + 1) == aComponent2 && *(aColorPointer + 2) == aComponent3);
	}

/** The function encodes 32-bit buffer into 32-bit stream by using RLE compression algorithm*/
int BitmapCompiler::CompressThirtyTwoABitData(char*& aDest,char* aSrce,int aSizeInBytes)
	{
	const char* destEnd = aDest + aSizeInBytes; 
	char* srceLimitPtr = aSrce + aSizeInBytes;
	char* srceLimitPtrMinusFour = srceLimitPtr - 4; // four bytes == one pixel
	char* destCompressedLimitPtr = aDest + (aSizeInBytes * 7 / 8);
	int ret = NoError;

	while (aSrce < srceLimitPtrMinusFour)
		{
		char* runStartPtr = aSrce;
		char component1 = *aSrce++;
		char component2 = *aSrce++;
		char component3 = *aSrce++;
		char component4 = *aSrce++;

		if (ColorAlphaPointerCompare(aSrce,component1,component2,component3,component4))
			{
			do
				aSrce += 4;
			while (aSrce < srceLimitPtr && ColorAlphaPointerCompare(aSrce,component1,component2,component3,component4));
			int eqPixelLength = (aSrce - runStartPtr) / 4;
			ret = WriteCompressedThirtyTwoABitValues(aDest,component1,component2,component3,component4,eqPixelLength,destEnd);
			if (ret)
				return ret;
			}
		else //non repeating pixel buffer
			{
			bool more  = true;
			bool eqRun = false;
			do {
				component1 = *aSrce++;
				component2 = *aSrce++;
				component3 = *aSrce++;
				component4 = *aSrce++;
				more = (aSrce < srceLimitPtr);
				eqRun = more && ColorAlphaPointerCompare(aSrce,component1,component2,component3,component4);
				} while (more && !eqRun);
			if (eqRun)
				aSrce -= 4;
			int nePixelLength = (aSrce - runStartPtr) / 4;
			ret = WriteCompressedThirtyTwoABitData(aDest,runStartPtr,nePixelLength,destEnd);
			if (ret)
				return ret;
			}
		if (aDest > destCompressedLimitPtr)
			return NoCompression;
		}

	if (aSrce < srceLimitPtr)
		{
		ret = WriteCompressedThirtyTwoABitData(aDest,aSrce,(srceLimitPtr - aSrce) / 4, destEnd);
		if (ret)
			return ret;
		}

	if (aDest > destCompressedLimitPtr)
		return NoCompression;
	return NoError;
	}

/** The function copies non repeating 32-bit buffer to 32-bit compressed buffer*/
int BitmapCompiler::WriteCompressedThirtyTwoABitData(char*& aDest,char* aData,
													 int aLength, const char* aDestEnd)
	{
	if (aLength < 1)
		return CompressionError;

	if(!CanCopy32bppData(aDest, aDestEnd, aLength))
		return NoCompression;

	while (aLength > 128)
		{
		*aDest++ = -128;
		memcpy(aDest,aData,512);
		aDest += 512;
		aData += 512;
		aLength -= 128;
		}

	*aDest++ = char(-aLength);
	memcpy(aDest,aData, aLength*4);
	aDest += aLength*4;
	return NoError;
	}

/** The function copies repeating pixels including alpha to a 32-bit compressed buffer */
int BitmapCompiler::WriteCompressedThirtyTwoABitValues(char*& aDest,char aComponent1,
													   char aComponent2,char aComponent3,
													   char aComponent4,int aLength,
													   const char* aDestEnd)

	{
	if (aLength < 1)
		return CompressionError;

	if(!CanWrite32bppValue(aDest, aDestEnd, aLength))
		return NoCompression;
	while (aLength > 128)
		{
		*aDest++ = 127;
		*aDest++ = aComponent1;
		*aDest++ = aComponent2;
		*aDest++ = aComponent3;
		*aDest++ = aComponent4;
		aLength -= 128;
		}

	*aDest++ = char(aLength-1);
	*aDest++ = aComponent1;
	*aDest++ = aComponent2;
	*aDest++ = aComponent3;
	*aDest++ = aComponent4;

	return NoError;
	}

int BitmapCompiler::ColorAlphaPointerCompare(char* aColorPointer,char aComponent1,char aComponent2,char aComponent3,char aComponent4)
	{
	return (*aColorPointer == aComponent1 && *(aColorPointer + 1) == aComponent2 && *(aColorPointer + 2) == aComponent3 && *(aColorPointer + 3) == aComponent4);
	}

int BitmapCompiler::ReadHexString(char aHexBuf[10],char*& aDataPtr,char* aDataPtrLimit)
	{
	while (aDataPtr < aDataPtrLimit)
		{
		if (aDataPtr[0] == '0')
			{
			if (aDataPtr[1] == 'x' || aDataPtr[1] == 'X')
				{
				memcpy(aHexBuf,aDataPtr,10);
				aDataPtr += 10;
				return NoError;
				}
			}
		aDataPtr++;
		}

	return PaletteFile;
	}

int BitmapCompiler::HexToInt(char aHighNibble,char aLowNibble)
	{
	return (HexToInt(aHighNibble) << 4) + HexToInt(aLowNibble);
	}

int BitmapCompiler::HexToInt(char aNibble)
	{
	int value = 0;

	if (aNibble >= '0' && aNibble <= '9')
		value = aNibble - '0';
	else if (aNibble >= 'a' && aNibble <= 'f')
		value = aNibble - 'a' + 10;
	else if (aNibble >= 'A' && aNibble <= 'F')
		value = aNibble - 'A' + 10;

	return value;
	}

void BitmapCompiler::CopyTail(void* aDst, void* aSrc, int aFullSize, int aSkipped)
	{
	memcpy(aDst, ((char*)aSrc)+aSkipped, aFullSize-aSkipped);
	}