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