gfxconversion/bmconv_s60/src/PBMTOBM.CPP
changeset 0 f453ebb75370
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gfxconversion/bmconv_s60/src/PBMTOBM.CPP	Tue Feb 02 01:11:11 2010 +0200
@@ -0,0 +1,730 @@
+/*
+* Copyright (c) 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 <assert.h>
+#include "BMCONV.H"
+
+EpocLoader::EpocLoader():
+	iPbmBits(NULL)
+	{}
+
+EpocLoader::~EpocLoader()
+	{
+	delete iPbmBits;
+	}
+
+int EpocLoader::EpocBitmapCount(char* aFileName, int& aCount, int& isRomFormat)
+    {
+#if defined(__MSVCDOTNET__) || defined(__TOOLS2__) || defined(__CW32__)
+	fstream file(aFileName, ios::in | ios::binary);
+#else //!__MSVCDOTNET__
+	fstream file(aFileName, ios::in | ios::binary | ios::nocreate);
+#endif //__MSVCDOTNET__
+
+	if (file.is_open()==0)
+		return Files;
+
+	long int wordbuffer;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	if (wordbuffer==KMultiBitmapRomImageUid)
+		{
+		// ROM format file - next 4 bytes are the number of bitmaps
+		isRomFormat=1;
+		}
+	else
+		{
+		if (wordbuffer!=KWriteOnceFileStoreUid)
+			return SourceFile;
+		isRomFormat=0;
+		file.read((char *)&wordbuffer,4);
+		if (file.gcount()!=4 || wordbuffer!=KMultiBitmapFileImageUid)
+			return SourceFile;
+		file.read((char *)&wordbuffer,4);
+		if (file.gcount()!=4 || wordbuffer!=0)
+			return SourceFile;
+		file.read((char *)&wordbuffer,4);
+		if (file.gcount()!=4 || wordbuffer!=KMultiBitmapFileImageChecksum)
+			return SourceFile;
+		file.read((char *)&wordbuffer,4);
+		if (file.gcount()!=4)
+			return SourceFile;
+		file.seekg(wordbuffer,ios::beg);
+		}
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	aCount=wordbuffer;
+	return NoError;
+    }
+
+int EpocLoader::LoadEpocBitmap(char* aFileName,int aIndex)
+	{
+#if defined(__MSVCDOTNET__) || defined(__TOOLS2__) || defined(__CW32__)
+	fstream file(aFileName, ios::in | ios::binary);
+#else //!__MSVCDOTNET__
+	fstream file(aFileName, ios::in | ios::binary | ios::nocreate);
+#endif //__MSVCDOTNET__
+
+	if (file.is_open()==0)
+		return Files;
+
+	long int wordbuffer;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	file.close();
+
+	if (wordbuffer==KMultiBitmapRomImageUid)
+		return LoadRom(aFileName,aIndex);
+
+	return LoadFile(aFileName,aIndex);
+	}
+
+int EpocLoader::LoadFile(char* aFileName,int aIndex)
+	{
+#if defined(__MSVCDOTNET__) || defined(__TOOLS2__) || defined(__CW32__)
+	fstream file(aFileName, ios::in | ios::binary);
+#else //!__MSVCDOTNET__
+	fstream file(aFileName, ios::in | ios::binary | ios::nocreate);
+#endif //__MSVCDOTNET__
+
+	if (file.is_open()==0)
+		return Files;
+
+	long int wordbuffer;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4 || wordbuffer!=KWriteOnceFileStoreUid)
+		return SourceFile;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4 || wordbuffer!=KMultiBitmapFileImageUid)
+		return SourceFile;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4 || wordbuffer!=0)
+		return SourceFile;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4 || wordbuffer!=KMultiBitmapFileImageChecksum)
+		return SourceFile;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	file.seekg(wordbuffer,ios::beg);
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+
+	int numsources=wordbuffer;
+	if (aIndex >= numsources)
+		return OutOfRange;
+	file.seekg(aIndex*4,ios::cur);
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	file.seekg(wordbuffer,ios::beg);
+	int ret = DoLoadFile(file);
+	file.close();
+	return ret;
+	}
+
+int EpocLoader::LoadRom(char* aFileName,int aIndex)
+	{
+#if defined(__MSVCDOTNET__) || defined(__TOOLS2__) || defined(__CW32__)
+	fstream file(aFileName, ios::in | ios::binary);
+#else //!__MSVCDOTNET__
+	fstream file(aFileName, ios::in | ios::binary | ios::nocreate);
+#endif //__MSVCDOTNET__
+
+	if (file.is_open()==0)
+		return Files;
+
+	long int wordbuffer;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	if (wordbuffer!=KMultiBitmapRomImageUid)
+		return SourceFile;
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	if (aIndex>=wordbuffer)
+		return OutOfRange;
+	file.seekg(aIndex*4,ios::cur);
+	file.read((char *)&wordbuffer,4);
+	if (file.gcount()!=4)
+		return SourceFile;
+	file.seekg(wordbuffer,ios::beg);
+	int ret=DoLoadRom(file);
+	file.close();
+	return ret;
+	}
+
+int EpocLoader::DoLoadFile(fstream& aFile)
+	{
+	long size = sizeof(SEpocBitmapHeader);
+	aFile.read((char *)&iPbmHeader,size);
+	if (aFile.gcount()!=size)
+		return SourceFile;
+	iOriginalPbmHeader = iPbmHeader;
+	size=iPbmHeader.iBitmapSize-iPbmHeader.iStructSize;
+
+	iPbmBits=new char[size];
+	if (iPbmBits==NULL)
+		return NoMemory;
+	memset(iPbmBits,0xff,size);
+	aFile.read(iPbmBits,size);
+	aFile.close();
+	if (aFile.gcount() != size)
+		return SourceFile;
+
+	if (iPbmHeader.iCompression != ENoBitmapCompression)
+		{
+		return Decompress(size);
+		}
+
+	return NoError;
+	}
+
+int EpocLoader::DoLoadRom(fstream& aFile)
+	{
+	Bitmap bmp;
+	long size=sizeof(Bitmap);
+	aFile.read((char*)&bmp,size);
+	if (aFile.gcount() != size)
+		return SourceFile;
+
+	size = bmp.iHeader.iBitmapSize - sizeof(SEpocBitmapHeader);
+	iPbmBits = new char[size];
+	if (iPbmBits == NULL)
+		return NoMemory;
+	memset(iPbmBits,0xff,size);
+
+	aFile.read(iPbmBits,size);
+	if (aFile.gcount() != size)
+		return SourceFile;
+	iPbmHeader = bmp.iHeader;
+	iOriginalPbmHeader = iPbmHeader;
+
+	if (iPbmHeader.iCompression != ENoBitmapCompression)
+		{
+		return Decompress(size);
+		}
+
+	return NoError;
+	}
+
+int EpocLoader::Decompress(int aSize)
+	{
+	int byteWidth = BitmapUtils::ByteWidth(iPbmHeader.iWidthInPixels,iPbmHeader.iBitsPerPixel);
+	int expandedsize = byteWidth * iPbmHeader.iHeightInPixels;
+	char* newbits = new char[expandedsize];
+	if (newbits == NULL)
+		return NoMemory;
+	memset(newbits,0xff,expandedsize);
+	int ret = NoError;
+	switch (iPbmHeader.iCompression)
+		{
+	case EByteRLECompression:
+		ret = ExpandByteRLEData(newbits,expandedsize,iPbmBits,aSize);
+		break;
+	case ETwelveBitRLECompression:
+		ret = ExpandTwelveBitRLEData(newbits,expandedsize,iPbmBits,aSize);
+		break;
+	case ESixteenBitRLECompression:
+		ret = ExpandSixteenBitRLEData(newbits,expandedsize,iPbmBits,aSize);
+		break;
+	case ETwentyFourBitRLECompression:
+		ret = ExpandTwentyFourBitRLEData(newbits,expandedsize,iPbmBits,aSize);
+		break;
+	case EThirtyTwoUBitRLECompression:
+		ret = ExpandThirtyTwoUBitRLEData(newbits,expandedsize,iPbmBits,aSize);
+		break;
+	case EThirtyTwoABitRLECompression:
+		ret = ExpandThirtyTwoABitRLEData(newbits,expandedsize,iPbmBits,aSize);
+		break;
+	default:
+		ret = UnknownCompression;
+		break;
+		}
+	delete iPbmBits;
+	iPbmBits = newbits;
+	iPbmHeader.iCompression = ENoBitmapCompression;
+	iPbmHeader.iBitmapSize += expandedsize-aSize;
+	return ret;
+	}
+
+int EpocLoader::ExpandByteRLEData(char* aDest,int aDestSize,char* aSrce,int aSrceSize)
+	{
+	char* srcelimit=aSrce+aSrceSize;
+	char* destlimit=aDest+aDestSize;
+	while(aSrce<srcelimit && aDest<destlimit)
+		{
+		char count=*aSrce++;
+		if (count<0)
+			{
+			int runLength=-count;
+			memcpy(aDest,aSrce,runLength);
+			aSrce+=runLength;
+			aDest+=runLength;
+			}
+		else
+			{
+			char value=*aSrce++;
+			while(count>=0)
+				{
+				*aDest++=value;
+				count--;
+				}
+			}
+		}
+	if (aSrce!=srcelimit || aDest!=destlimit)
+		return DecompressionError;
+	return NoError;
+	}
+
+int EpocLoader::ExpandTwelveBitRLEData(char* aDest,int aDestSizeInBytes,char* aSrce,int aSrceSizeInBytes)
+	{
+	unsigned short* srcePtr = (unsigned short*)aSrce;
+	unsigned short* destPtr = (unsigned short*)aDest;
+	unsigned short* srcePtrLimit = srcePtr + (aSrceSizeInBytes / 2);
+	unsigned short* destPtrLimit = destPtr + (aDestSizeInBytes / 2);
+
+	while (srcePtr < srcePtrLimit && destPtr < destPtrLimit)
+		{
+		unsigned short value = *srcePtr++;
+		int runLength = (value >> 12) + 1;
+		value &= 0x0fff;
+
+		for (;runLength > 0;runLength--)
+			*destPtr++ = value;
+		}
+
+	if (srcePtr != srcePtrLimit || destPtr != destPtrLimit)
+		return DecompressionError;
+
+	return NoError;
+	}
+
+int EpocLoader::ExpandSixteenBitRLEData(char* aDest,int aDestSizeInBytes,char* aSrce,int aSrceSizeInBytes)
+	{
+	char* srcePtrLimit = aSrce + aSrceSizeInBytes;
+	unsigned short* destPtr = (unsigned short*)aDest;
+	unsigned short* destPtrLimit = destPtr + (aDestSizeInBytes / 2);
+
+	while (aSrce < srcePtrLimit && destPtr < destPtrLimit)
+		{
+		int runLength = *aSrce++;
+
+		if (runLength >= 0)
+			{
+			unsigned short value = *((unsigned short*)(aSrce));
+			aSrce += 2;
+			for (runLength++; runLength > 0; runLength--)
+				*destPtr++ = value;
+			}
+		else
+			{
+			runLength = -runLength;
+			int byteLength = runLength * 2;
+			memcpy(destPtr,aSrce,byteLength);
+			aSrce += byteLength;
+			destPtr += runLength;
+			}
+		}
+
+	if (aSrce != srcePtrLimit || destPtr != destPtrLimit)
+		return DecompressionError;
+
+	return NoError;
+	}
+
+int EpocLoader::ExpandTwentyFourBitRLEData(char* aDest,int aDestSizeInBytes,char* aSrce,int aSrceSizeInBytes)
+	{
+	char* srcePtrLimit = aSrce + aSrceSizeInBytes;
+	char* destPtrLimit = aDest + aDestSizeInBytes;
+
+	while (aSrce < srcePtrLimit && aDest < destPtrLimit)
+		{
+		int runLength = *aSrce++;
+
+		if (runLength >= 0)
+			{
+			char component1 = *aSrce++;
+			char component2 = *aSrce++;
+			char component3 = *aSrce++;
+			for (runLength++; runLength > 0; runLength--)
+				{
+				*aDest++ = component1;
+				*aDest++ = component2;
+				*aDest++ = component3;
+				}
+			}
+		else
+			{
+			runLength = -runLength;
+			int byteLength = runLength * 3;
+			memcpy(aDest,aSrce,byteLength);
+			aSrce += byteLength;
+			aDest += byteLength;
+			}
+		}
+
+	if (aSrce != srcePtrLimit || aDest != destPtrLimit)
+		return DecompressionError;
+
+	return NoError;
+	}
+
+/** The function decodes 24-bit compressed pixel buffer into the 32-bit buffer, where top bytes are unused*/
+int EpocLoader::ExpandThirtyTwoUBitRLEData(char* aDest,int aDestSizeInBytes,char* aSrce,int aSrceSizeInBytes)
+	{
+	char* srcePtrLimit = aSrce + aSrceSizeInBytes;
+	char* destPtrLimit = aDest + aDestSizeInBytes;
+
+	while (aSrce < srcePtrLimit && aDest < destPtrLimit)
+		{
+		int runLength = *aSrce++;
+
+		if (runLength >= 0)
+			{
+			char component1 = *aSrce++;
+			char component2 = *aSrce++;
+			char component3 = *aSrce++;
+			for (runLength++; runLength > 0; runLength--)
+				{
+				*aDest++ = component1;
+				*aDest++ = component2;
+				*aDest++ = component3;
+				aDest++;
+				}
+			}
+		else 
+			{
+			runLength = -runLength;
+			for(int ii = 0; ii < runLength; ii++)
+				{
+				memcpy(aDest,aSrce,3);
+				aSrce += 3;
+				aDest += 4;
+				}
+			}
+		}
+
+	if (aSrce != srcePtrLimit || aDest != destPtrLimit)
+		return DecompressionError;
+
+	return NoError;
+	}
+
+/** The function decodes 32-bit compressed pixel buffer into the 32-bit buffer, where top bytes are alpha channel*/
+int EpocLoader::ExpandThirtyTwoABitRLEData(char* aDest,int aDestSizeInBytes,char* aSrce,int aSrceSizeInBytes)
+	{
+	char* srcePtrLimit = aSrce + aSrceSizeInBytes;
+	char* destPtrLimit = aDest + aDestSizeInBytes;
+
+	while (aSrce < srcePtrLimit && aDest < destPtrLimit)
+		{
+		int runLength = *aSrce++;
+
+		if (runLength >= 0)
+			{
+			char component1 = *aSrce++;
+			char component2 = *aSrce++;
+			char component3 = *aSrce++;
+			char component4 = *aSrce++;
+			for (runLength++; runLength > 0; runLength--)
+				{
+				*aDest++ = component1;
+				*aDest++ = component2;
+				*aDest++ = component3;
+				*aDest++ = component4;
+				}
+			}
+		else 
+			{
+			runLength = -runLength;
+			memcpy(aDest,aSrce,4*runLength);
+			aSrce += 4*runLength;
+			aDest += 4*runLength;
+			}
+		}
+
+	if (aSrce != srcePtrLimit || aDest != destPtrLimit)
+		return DecompressionError;
+
+	return NoError;
+	}
+
+TRgb EpocLoader::GetPixel(int aXCoord,int aYCoord)
+	{
+	unsigned char col;
+	aXCoord%=iPbmHeader.iWidthInPixels;
+	aYCoord%=iPbmHeader.iHeightInPixels;
+	int byteWidth = BitmapUtils::ByteWidth(iPbmHeader.iWidthInPixels,iPbmHeader.iBitsPerPixel);
+	int yOffset = aYCoord * byteWidth;
+
+	switch(iPbmHeader.iBitsPerPixel)
+		{
+		case 1:
+			col = iPbmBits[yOffset + (aXCoord / 8)];
+			col >>= (aXCoord&7);
+			return TRgb::Gray2(col & 1);
+		case 2:
+			col = iPbmBits[yOffset + (aXCoord / 4)];
+			col = (unsigned char)(col>>(2*(aXCoord%4)));
+			return TRgb::Gray4(col & 3);
+		case 4:
+			col = iPbmBits[yOffset + (aXCoord / 2)];
+			if (aXCoord & 1)
+				col >>= 4;
+			col &= 0xf;
+			if (iPbmHeader.iColor==EColorBitmap)
+				return TRgb::Color16(col);
+			else
+				return TRgb::Gray16(col);
+		case 8:
+			col=iPbmBits[yOffset + aXCoord];
+			if (iPbmHeader.iColor==EColorBitmap)
+				return TRgb::Color256(col);
+			else
+				return TRgb::Gray256(col);
+		case 12:
+		case 16:
+			{
+			unsigned short* shortPtr = (unsigned short*)&iPbmBits[yOffset + (aXCoord * 2)];
+			if (iPbmHeader.iBitsPerPixel == 12)
+				return TRgb::Color4K(*shortPtr);
+			else
+				return TRgb::Color64K(*shortPtr);
+			}
+		case 24:
+			{
+			char* pixelPtr = iPbmBits + yOffset + (aXCoord * 3);
+			TRgb pixelColor;
+			pixelColor.iBlue = *pixelPtr++;
+			pixelColor.iGreen = *pixelPtr++;
+			pixelColor.iRed = *pixelPtr;
+			return pixelColor;
+			}
+		case 32:
+			{
+			char* pixelPtr = iPbmBits + yOffset + (aXCoord * 4);
+			TRgb pixelColor;
+			pixelColor.iBlue = *pixelPtr++;
+			pixelColor.iGreen = *pixelPtr++;
+			pixelColor.iRed = *pixelPtr++;
+			pixelColor.iSpare = *pixelPtr;
+			return pixelColor;
+			}
+		default:
+			return TRgb(0);
+		}
+	}
+
+unsigned char EpocLoader::GetAlpha(int aXCoord,int aYCoord)
+	{
+	aXCoord%=iPbmHeader.iWidthInPixels;
+	aYCoord%=iPbmHeader.iHeightInPixels;
+	int byteWidth = BitmapUtils::ByteWidth(iPbmHeader.iWidthInPixels,iPbmHeader.iBitsPerPixel);
+	int yOffset = aYCoord * byteWidth;
+
+	assert(iPbmHeader.iBitsPerPixel == 32); // this method must only be called for 32bpp bitmaps!
+
+	char* pixelPtr = iPbmBits + yOffset + (aXCoord * 4);
+	pixelPtr += 3; // skip over RGB
+	unsigned char col = *pixelPtr;
+	return col;	
+	}
+
+int EpocLoader::SaveBitmap(char* aFileName)
+	{
+	TBitmapFileHeader fileheader;
+	TBitmapInfoHeader bmpHeader;
+	char* bmpBits;
+
+	bmpHeader.biSize = sizeof(TBitmapInfoHeader);
+	bmpHeader.biWidth = iPbmHeader.iWidthInPixels;
+	bmpHeader.biHeight = iPbmHeader.iHeightInPixels;
+	bmpHeader.biPlanes = 1;
+	bmpHeader.biBitCount = 24;
+	bmpHeader.biCompression = 0;
+	bmpHeader.biSizeImage = 0;
+	bmpHeader.biXPelsPerMeter = 0;
+	bmpHeader.biYPelsPerMeter = 0;
+	bmpHeader.biClrUsed = 0;
+	bmpHeader.biClrImportant = 0;
+
+	long byteWidth = ((bmpHeader.biWidth * 3) + 3) & ~3;
+	long destlength = bmpHeader.biHeight * byteWidth;
+
+	fileheader.bfType = 'B'+('M'<<8);
+	fileheader.bfSize = sizeof(TBitmapFileHeader)+sizeof(TBitmapInfoHeader)+destlength;
+	fileheader.bfReserved1 = 0;
+	fileheader.bfReserved2 = 0;
+	fileheader.bfOffBits = sizeof(TBitmapFileHeader)+sizeof(TBitmapInfoHeader);
+
+	bmpBits = new char[destlength];
+	if (bmpBits == NULL)
+		return NoMemory;
+	memset(bmpBits,0xff,destlength);
+
+	for(long y=0;y<bmpHeader.biHeight;y++)
+		{
+		char* dest=&bmpBits[(bmpHeader.biHeight-y-1)*byteWidth];
+		for(long x=0;x<bmpHeader.biWidth;x++)
+			{
+			TRgb pixel=GetPixel(x,y);
+			*dest++=pixel.iBlue;
+			*dest++=pixel.iGreen;
+			*dest++=pixel.iRed;
+			}
+		}
+
+	fstream file(aFileName, ios::out | ios::binary);
+
+	if (file.is_open()==0)
+		{
+		delete bmpBits;
+		return DestFile;
+		}
+
+	file.write((char *)&fileheader,sizeof(TBitmapFileHeader));
+	file.write((char *)&bmpHeader,sizeof(TBitmapInfoHeader));
+	file.write((char *)bmpBits,destlength);
+	file.close();
+
+	delete bmpBits;
+
+	if (iPbmHeader.iColor == EColorBitmapAlpha)
+		SaveAlpha(aFileName);
+	return NoError;
+	}
+
+
+int EpocLoader::SaveAlpha(char* aFileName)
+	{
+	TBitmapFileHeader fileheader;
+	TBitmapInfoHeader alphaHeader;
+
+	alphaHeader.biSize = sizeof(TBitmapInfoHeader);
+	alphaHeader.biWidth = iPbmHeader.iWidthInPixels;
+	alphaHeader.biHeight = iPbmHeader.iHeightInPixels;
+	alphaHeader.biPlanes = 1;
+	alphaHeader.biBitCount = 8;
+	alphaHeader.biCompression = 0;
+	alphaHeader.biSizeImage = 0;
+	alphaHeader.biXPelsPerMeter = 0;
+	alphaHeader.biYPelsPerMeter = 0;
+	alphaHeader.biClrUsed = 256;
+	alphaHeader.biClrImportant = 0;
+
+	const long paletteSize = 1024;
+
+	// ensure bytes-per-scanline is a large enough multiple of 4
+	long byteWidth = (alphaHeader.biWidth + 3) & ~3; 
+	long destlength = alphaHeader.biHeight * byteWidth;
+	alphaHeader.biSizeImage = destlength;
+
+	fileheader.bfType = 'B'+('M'<<8);
+	fileheader.bfSize = sizeof(TBitmapFileHeader)+sizeof(TBitmapInfoHeader)+paletteSize+destlength;
+	fileheader.bfReserved1 = 0;
+	fileheader.bfReserved2 = 0;
+	fileheader.bfOffBits = sizeof(TBitmapFileHeader)+sizeof(TBitmapInfoHeader)+paletteSize;
+
+	// we need to output the grayscale palette before the actual alpha data as this is an 8bpp BMP
+	char* palette = new char[paletteSize]; // 256 colors x 4bytes/color (r,g,b,unused)
+	if (palette == NULL)
+		return NoMemory;
+	memset(palette,0,paletteSize);
+	char* pEnt = palette;
+	for (long p=0;p<256;++p)
+		{
+		unsigned char col = (unsigned char)p;
+		*pEnt++=col;
+		*pEnt++=col;
+		*pEnt++=col;
+		pEnt++;
+		}
+
+	char* alphaBits = new char[destlength];
+	if (alphaBits == NULL)
+		{
+		delete [] palette;
+		return NoMemory;
+		}
+	memset(alphaBits,0,destlength);
+
+	for(long y=0;y<alphaHeader.biHeight;y++)
+		{
+		char* dest=&alphaBits[(alphaHeader.biHeight-y-1)*byteWidth];
+		for(long x=0;x<alphaHeader.biWidth;x++)
+			{
+			unsigned char alphaVal=GetAlpha(x,y);
+			*dest++=alphaVal;
+			}
+		}
+
+	int fileNameLen = strlen(aFileName);
+	char* alphaFileName = new char[fileNameLen + 7];// -alpha suffix is 6 chars, plus NUL termination
+	if (alphaFileName == NULL)
+		{
+		delete [] palette;
+		delete [] alphaBits;
+		return NoMemory;
+		}
+	int dotPos = -1;
+	for (int i = 0; i < fileNameLen; ++i)
+		if (aFileName[i]=='.')
+			dotPos=i;
+	int prefixLen = (dotPos>=0?dotPos:fileNameLen);
+	memcpy(alphaFileName,aFileName,prefixLen);
+	const char* suffix = "-alpha";
+	memcpy(alphaFileName+prefixLen,suffix,6);
+	if (dotPos>=0)
+		memcpy(alphaFileName+prefixLen+6,aFileName+dotPos,fileNameLen-dotPos);
+	*(alphaFileName + fileNameLen + 6) = '\0';
+	fstream file(alphaFileName, ios::out | ios::binary);
+	if (file.is_open()==0)
+		{
+		delete [] alphaFileName;
+		delete [] alphaBits;
+		delete [] palette;
+		return DestFile;
+		}
+
+	file.write((char *)&fileheader,sizeof(TBitmapFileHeader));
+	file.write((char *)&alphaHeader,sizeof(TBitmapInfoHeader));
+	file.write((char *)palette,paletteSize);
+	file.write((char *)alphaBits,destlength);
+	file.close();
+
+	delete [] alphaFileName;
+	delete [] alphaBits;
+	delete [] palette;
+	return NoError;
+	}
+
+int EpocLoader::DupBitmap(SEpocBitmapHeader*& aPbm)
+	{
+	char* newPbm = new char[iPbmHeader.iBitmapSize];
+	if (newPbm == NULL)
+		return NoMemory;
+	memcpy(newPbm, &iPbmHeader, sizeof(SEpocBitmapHeader));
+	memcpy(newPbm+sizeof(SEpocBitmapHeader), iPbmBits, iPbmHeader.iBitmapSize - sizeof(SEpocBitmapHeader));
+	aPbm = (SEpocBitmapHeader*)newPbm;
+	return NoError;
+	}