mmplugins/imagingplugins/codecs/PNGCodec/PNGCodec.cpp
changeset 0 40261b775718
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmplugins/imagingplugins/codecs/PNGCodec/PNGCodec.cpp	Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,1177 @@
+// Copyright (c) 1999-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 <fbs.h>
+#include "ImageClientMain.h"
+#include "ImageUtils.h"
+#include "PNGCodec.h"
+
+// Constants.
+const TInt KTwipsPerMeter = 56693;
+
+
+// Helper classes.
+// TPngImageInformation
+TPngImageInformation::TPngImageInformation()
+	{
+	iSize.SetSize(0,0);
+	iBitDepth = 0;
+	iColorType = EGrayscale;
+	iCompressionMethod = EDeflateInflate32K;
+	iFilterMethod = EAdaptiveFiltering;
+	iInterlaceMethod = ENoInterlace;
+	iPalettePresent = EFalse;
+
+#if defined(_DEBUG)
+	ASSERT(sizeof(TRgb)==sizeof(TUint32)); // ie no new fields
+	ASSERT(KPngMaxPLTESize%4==0);
+#endif  // defined(_DEBUG)
+
+	TRgb* palette=iPalette;
+	TInt i=KPngMaxPLTESize>>2;
+	do 
+		{
+		*palette++ = KRgbBlack;
+		*palette++ = KRgbBlack;
+		*palette++ = KRgbBlack;
+		*palette++ = KRgbBlack;
+		} while (--i);
+
+	iBackgroundPresent = EFalse;
+	iBackgroundColor = KRgbWhite;
+	iPhysicalPresent = EFalse;
+	iPhysicalUnits = EUnknownUnits;
+	iPhysicalSize.SetSize(0,0);
+	iTransparencyPresent = EFalse;
+	Mem::Fill(iTransparencyValue,KPngMaxPLTESize,0xff);
+	}
+
+
+// CMdaPngReadCodec
+CPngReadCodec::~CPngReadCodec()
+	{
+	delete iDecoder;
+	delete iDecompressor;
+	if (iOwnsImageProcessor)
+		{
+		delete iImageProc;
+		}
+	if (iOwnsMaskProcessor)
+		{
+		delete iMaskProc;
+		}
+	if (iOwnsFastProcessor)
+		{
+		delete iFastProc;
+		}		
+	}
+
+CPngReadCodec* CPngReadCodec::NewL(MPngDecoder& aDecoderIFace)
+	{
+	CPngReadCodec* self = new(ELeave) CPngReadCodec(aDecoderIFace);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self); 
+	return self;
+	}
+
+
+CPngReadCodec::CPngReadCodec(MPngDecoder& aDecoderIFace):	
+	iDecoderIFace(aDecoderIFace), iNewPosition(0), iReadMore(EFalse)
+	{
+	}
+
+void CPngReadCodec::ConstructL()
+	{
+	CImageMaskProcessorReadCodec::ConstructL();
+	}
+
+void CPngReadCodec::InitFrameL(TFrameInfo& /*aFrameInfo*/, CFrameImageData& /*aFrameImageData*/, TBool aDisableErrorDiffusion, CFbsBitmap& aDestination, CFbsBitmap* aDestinationMask)
+	{
+	CFbsBitmap& newFrame = aDestination;
+
+	iChunkBytesRemaining = 0;
+	iChunkId = KNullDesC8;
+	iPreviousChunkReadFailed = EFalse;
+	
+	const TSize destinationSize(newFrame.SizeInPixels());
+	TInt reductionFactor = ReductionFactor(iImageInfo.iSize, destinationSize);
+	
+	TBool fastProcessorMode = EFalse;
+	
+	CImageProcessor* imageProc = NULL;
+	SetImageProcessor(NULL);
+	
+	CImageProcessor* maskProc = NULL;
+	SetMaskProcessor(NULL);
+	
+	CFastProcessor* fastProc = NULL;
+	SetFastProcessor(NULL);
+	
+	if (!SkipImageProcessor(aDestination))
+		{
+		imageProc = ImageProcessorUtility::NewImageProcessorL(newFrame, reductionFactor, ERgb, aDisableErrorDiffusion);
+		SetImageProcessor(imageProc);
+		imageProc->PrepareL(newFrame,iImageInfo.iSize);
+
+		if ((iImageInfo.iTransparencyPresent || (iImageInfo.iColorType & TPngImageInformation::EAlphaChannelUsed))
+			&& aDestinationMask)
+			{
+			maskProc = ImageProcessorUtility::NewImageProcessorL(*aDestinationMask, iImageInfo.iSize, ERgb, aDisableErrorDiffusion);
+			SetMaskProcessor(maskProc);
+			maskProc->PrepareL(*aDestinationMask,iImageInfo.iSize);
+			// set mask to black so that unknown parts on streamed image are not drawn
+			ClearBitmapL(*aDestinationMask, KRgbBlack);
+			}
+		}
+	else
+		{
+		fastProc = CFastProcessor::NewL(iImageInfo, &aDestination, aDestinationMask, EFalse);
+		SetFastProcessor(fastProc);
+		fastProcessorMode = ETrue;
+		}
+
+	delete iDecoder;
+	iDecoder = NULL;
+	iDecoder = CPngReadSubCodec::NewL(imageProc,maskProc,iImageInfo, fastProc, fastProcessorMode);
+	
+	if (!iDecompressor)
+		{
+		iDecompressor = CEZDecompressor::NewL(*this);
+		}
+	else
+		{
+		iDecompressor->ResetL(*this);
+		}
+
+	if (!aDestinationMask)
+		{
+		// if no mask, clear destination for sensible behaviour on streamed partial images
+		TRgb background = iImageInfo.iBackgroundPresent ? iImageInfo.iBackgroundColor : KRgbWhite;
+		ClearBitmapL(aDestination, background);
+		
+		if (aDestination.DisplayMode() == EColor16MA && iFrameInfo->iFlags & TFrameInfo::ETransparencyPossible)
+			{
+			iDecoder->SetAlphaMode(ETrue);
+			}
+		}
+	}
+
+void CPngReadCodec::InitFrameHeader(TFrameInfo& aFrameSettings, CFrameImageData& /* aFrameImageData */)
+	{
+	ASSERT(aFrameSettings.CurrentFrameState() == TFrameInfo::EFrameInfoUninitialised);
+	iFrameInfo = &aFrameSettings;
+	iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrameHeader);
+	}
+
+TFrameState CPngReadCodec::ProcessFrameHeaderL(TBufPtr8& aData)
+	{
+	const TUint8* startDataPtr = aData.Ptr();
+	const TUint8* dataPtr = startDataPtr;
+	const TUint8* dataPtrLimit = startDataPtr + aData.Length();
+
+	
+	if (iFrameInfo->CurrentFrameState() == TFrameInfo::EFrameInfoProcessingFrameHeader)
+		{
+		if (dataPtr + KPngChunkLengthSize + KPngChunkIdSize + KPngIHDRChunkSize + KPngChunkCRCSize > dataPtrLimit)
+			User::Leave(KErrUnderflow);
+
+		TInt chunkLength = PtrReadUtil::ReadBigEndianUint32Inc(dataPtr);
+		TPtrC8 chunkId(dataPtr,KPngChunkIdSize);
+
+		if (chunkLength != KPngIHDRChunkSize || chunkId != KPngIHDRChunkId)
+			User::Leave(KErrNotFound);
+
+		dataPtr += KPngChunkIdSize;
+
+		DoProcessIHDRL(dataPtr,chunkLength);
+
+		dataPtr += KPngIHDRChunkSize + KPngChunkCRCSize;
+		}
+
+	//When there is not enough buffer to read the chunk length and chunk id,
+	//the input buffer is not increased, the decoder will not be able to read more data,
+	//the test will stuck in infinite loop.
+	//To break the infinite loop, here it checkes whether it's reading the same data as last time,
+	//if so, leaves.	
+	if ((iPreviousDataPos == iNewPosition) && (iPreviousDataLength == aData.Length()))
+		{
+		User::Leave(KErrUnderflow);
+		}
+	
+	TRAPD(err, DoProcessInfoL(dataPtr, dataPtrLimit));
+			
+	iPreviousDataPos = iNewPosition;
+	iPreviousDataLength = aData.Length();
+
+	if (err != KErrNone)
+		{
+		if (err == KErrNotFound)
+			return EFrameComplete;
+		User::Leave(err); // A real error occured
+		}
+	if(iReadMore)
+		{
+		iReadMore = EFalse;
+		iNewPosition += dataPtr - startDataPtr;
+		return EFrameIncompleteRepositionRequest;
+		}
+	aData.Shift(dataPtr - startDataPtr);
+
+	iFrameInfo->iFrameCoordsInPixels.SetRect(TPoint(0,0),iImageInfo.iSize);
+	iFrameInfo->iOverallSizeInPixels = iImageInfo.iSize;
+	if (iImageInfo.iPhysicalPresent && iImageInfo.iPhysicalUnits == TPngImageInformation::EMeters)
+		iFrameInfo->iFrameSizeInTwips = iImageInfo.iPhysicalSize;
+	else
+		iFrameInfo->iFrameSizeInTwips.SetSize(0,0);
+
+	iFrameInfo->iBitsPerPixel = iImageInfo.iBitDepth;
+	if (iImageInfo.iColorType & TPngImageInformation::EColorUsed 
+		 && iImageInfo.iColorType != TPngImageInformation::EIndexedColor)
+		iFrameInfo->iBitsPerPixel *= 3;
+	
+	iFrameInfo->iDelay = 0;
+	iFrameInfo->iFlags = TFrameInfo::ECanDither;
+	
+	if (iImageInfo.iColorType & (TPngImageInformation::EPaletteUsed | TPngImageInformation::EColorUsed))
+		iFrameInfo->iFlags |= TFrameInfo::EColor;
+	
+	if (iImageInfo.iColorType & TPngImageInformation::EAlphaChannelUsed)
+		{
+		iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible;
+		iFrameInfo->iFlags |= TFrameInfo::EAlphaChannel;
+		}
+	else if (iImageInfo.iTransparencyPresent)
+		iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible;
+
+	const TInt bitsPerPixel = iFrameInfo->iBitsPerPixel;
+	// the best mode for colour-indexed images is 16m
+	if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor)
+		iFrameInfo->iFrameDisplayMode = EColor16M;
+	else if(bitsPerPixel == 1)
+		iFrameInfo->iFrameDisplayMode = EGray2;
+	else if(bitsPerPixel == 2)
+		iFrameInfo->iFrameDisplayMode = EGray4;
+	else if((bitsPerPixel > 2)&&(bitsPerPixel <= 4))
+		iFrameInfo->iFrameDisplayMode = EGray16;
+	else if((bitsPerPixel > 4)&&(bitsPerPixel <= 8))
+		iFrameInfo->iFrameDisplayMode = EGray256;
+	else if((bitsPerPixel > 8)&&(bitsPerPixel <= 12))
+		iFrameInfo->iFrameDisplayMode = EColor4K;
+	else if((bitsPerPixel > 8) && (!(iFrameInfo->iFlags & TFrameInfo::EColor)))
+		iFrameInfo->iFrameDisplayMode = EGray256;
+	else if((bitsPerPixel > 12)&&(bitsPerPixel <= 16))
+		iFrameInfo->iFrameDisplayMode = EColor64K;
+	else if((bitsPerPixel > 16)&&(bitsPerPixel <= 48))
+		iFrameInfo->iFrameDisplayMode = EColor16M;
+	else
+		User::Leave(KErrCorrupt);
+
+
+	if (iImageInfo.iBackgroundPresent)
+		iFrameInfo->iBackgroundColor = iImageInfo.iBackgroundColor;
+
+	iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete);
+	return EFrameComplete;
+	}
+
+TFrameState CPngReadCodec::ProcessFrameL(TBufPtr8& aSrc)
+	{
+	CImageProcessor*const imageProc = ImageProcessor();
+	CImageProcessor*const maskProc = MaskProcessor();
+
+	
+	TUint8* startDataPtr = CONST_CAST(TUint8*,aSrc.Ptr());
+	TUint8* dataPtr = startDataPtr;
+	const TUint8* dataPtrLimit = dataPtr + aSrc.Length();
+	TBool chunkReadFailed = EFalse;
+	while (dataPtr < dataPtrLimit)
+		{
+		if (iChunkBytesRemaining == 0)
+			{
+			if (iChunkId != KNullDesC8)
+				{
+				// this is not the first chunk - need to skip the previous chunk's CRC
+				// as well as reading this chunk's length and id
+				if (dataPtr + KPngChunkCRCSize + KPngChunkLengthSize + KPngChunkIdSize > dataPtrLimit)
+					{
+					// not enough data
+					chunkReadFailed = ETrue;
+					break;
+					}
+
+				dataPtr += KPngChunkCRCSize;
+				}
+			else
+				{				
+				if (dataPtr + KPngChunkLengthSize + KPngChunkIdSize > dataPtrLimit)
+					{
+					// not enough data
+					break;
+					}
+				}
+
+			// read the current chunk's length and id
+			iChunkBytesRemaining = PtrReadUtil::ReadBigEndianUint32Inc(const_cast<const TUint8*&>(dataPtr));
+			
+			if(iChunkBytesRemaining < 0 )
+			    {
+			    User::Leave(KErrCorrupt);
+			    }
+			
+			iChunkId = TPtr8(dataPtr,KPngChunkIdSize,KPngChunkIdSize);
+			dataPtr += KPngChunkIdSize;
+			}
+
+		if (iChunkId == KPngIDATChunkId)
+			{
+			if(SetupProcessData(aSrc, dataPtr, const_cast<const TUint8*&>(dataPtr), dataPtrLimit))
+				iDecoderIFace.GoToProcessDataState();
+			break;
+			}
+		else if (iChunkId == KPngIENDChunkId)
+			{
+			iDecompressor->InflateL();
+			if (imageProc)
+				{
+				imageProc->FlushPixels();	
+				}
+			if (maskProc)
+				{
+				maskProc->FlushPixels();	
+				}
+			return EFrameComplete;
+			}
+		else // Skip other chunks
+			{
+			TInt bytesLeft = dataPtrLimit - dataPtr;
+			if (bytesLeft >= iChunkBytesRemaining)
+				{
+				dataPtr += iChunkBytesRemaining;
+				iChunkBytesRemaining = 0;
+				}
+			else
+				{
+				dataPtr += bytesLeft;
+				iChunkBytesRemaining -= bytesLeft;
+				}
+			}
+		}
+
+	// allow decode of png files with missing IEND chunks
+	if (!iMissingiENDChunkFail && chunkReadFailed && iPreviousChunkReadFailed)
+		{
+		// we're completely out of data but have finished a whole chunk
+		// try to decode the image
+		TBool moreDataNeeded = EFalse;
+		TRAPD(err, moreDataNeeded = iDecompressor->InflateL());
+		if ((err == KErrNone) && !moreDataNeeded)
+			{
+			if(imageProc)
+				{
+			 	imageProc->FlushPixels();
+				}			
+			if (maskProc)
+				{
+				maskProc->FlushPixels();
+				}
+
+			return EFrameComplete;
+			}
+		}
+	iPreviousChunkReadFailed = chunkReadFailed;
+	
+	aSrc.Shift(dataPtr - startDataPtr);
+	return EFrameIncomplete;
+	}
+
+void CPngReadCodec::DoProcessInfoL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
+	{
+	FOREVER
+		{
+		if (aDataPtr + KPngChunkLengthSize + KPngChunkIdSize > aDataPtrLimit) // Check there is enough data to read the chunk length
+			{
+			iNewPosition = 0 ;
+			iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrame);
+			iReadMore = ETrue;
+			return;
+			}
+		TInt chunkLength = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
+		TPtrC8 chunkId (&aDataPtr[0],KPngChunkIdSize);
+
+		if (chunkId == KPngIDATChunkId)
+			{
+			aDataPtr -= KPngChunkLengthSize; // Rewind to start of chunkLength
+			break;
+			}
+
+		if (aDataPtr + KPngChunkIdSize + chunkLength + KPngChunkCRCSize > aDataPtrLimit
+			 || chunkLength < 0) // Check there is enough data to read the whole chunk
+			{
+			if (	chunkId == KPngPLTEChunkId || chunkId == KPngbKGDChunkId ||
+					chunkId == KPngpHYsChunkId || chunkId == KPngtRNSChunkId )
+				{
+				aDataPtr -= KPngChunkLengthSize; // Rewind to start of chunkLength
+				User::Leave(chunkLength < 0 ? KErrCorrupt : KErrUnderflow);
+				}
+			iNewPosition = KPngChunkIdSize + chunkLength + KPngChunkCRCSize;
+			iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrame);
+			iReadMore = ETrue;
+			return;
+			}
+
+		aDataPtr += KPngChunkIdSize;
+
+		if (chunkId == KPngPLTEChunkId)
+			DoProcessPLTEL(aDataPtr,chunkLength);
+		else if (chunkId == KPngbKGDChunkId)
+			DoProcessbKGDL(aDataPtr,chunkLength);
+		else if (chunkId == KPngpHYsChunkId)
+			DoProcesspHYsL(aDataPtr,chunkLength);
+		else if (chunkId == KPngtRNSChunkId)
+			DoProcesstRNSL(aDataPtr,chunkLength);
+		else if (chunkId == KPngIHDRChunkId || chunkId == KPngIENDChunkId)
+			User::Leave(KErrCorrupt);
+
+		aDataPtr += chunkLength;
+		PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr); // Skip crc value
+		}
+	}
+
+void CPngReadCodec::DoProcessIHDRL(const TUint8* aDataPtr,TInt aChunkLength)
+	{
+	if (aChunkLength != KPngIHDRChunkSize)
+		User::Leave(KErrCorrupt);
+	
+	iImageInfo.iSize.iWidth = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
+	iImageInfo.iSize.iHeight = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
+	iImageInfo.iBitDepth = aDataPtr[0];
+	iImageInfo.iColorType = TPngImageInformation::TColorType(aDataPtr[1]);
+	iImageInfo.iCompressionMethod = TPngImageInformation::TCompressionMethod(aDataPtr[2]);
+	iImageInfo.iFilterMethod = TPngImageInformation::TFilterMethod(aDataPtr[3]);
+	iImageInfo.iInterlaceMethod = TPngImageInformation::TInterlaceMethod(aDataPtr[4]);
+	
+	// Check is one of the PNG formats we support
+	if (iImageInfo.iSize.iWidth < 1 || iImageInfo.iSize.iHeight < 1
+		|| iImageInfo.iCompressionMethod != TPngImageInformation::EDeflateInflate32K
+		|| iImageInfo.iFilterMethod != TPngImageInformation::EAdaptiveFiltering
+		|| (iImageInfo.iInterlaceMethod != TPngImageInformation::ENoInterlace &&
+		iImageInfo.iInterlaceMethod != TPngImageInformation::EAdam7Interlace))
+		User::Leave(KErrCorrupt);
+	
+	/*
+		Check if color type and bit depths are valid. 
+		PNG image type		Color type		Allowed bit depths
+		--------------      -----------     ------------------
+		Greyscale				0				1, 2, 4, 8, 16
+		Truecolour				2				8, 16
+		Indexed-colour			3				1, 2, 4, 8
+		Greyscale with alpha	4				8, 16
+		Truecolour with alpha	6				8, 16
+		
+		  (See http://www.w3.org/TR/PNG/#11IHDR)
+	*/
+	switch(iImageInfo.iColorType)
+		{
+		case TPngImageInformation::EGrayscale: // 0
+			{
+				if (! (iImageInfo.iBitDepth == 1 
+					|| iImageInfo.iBitDepth == 2 
+					|| iImageInfo.iBitDepth == 4
+					|| iImageInfo.iBitDepth == 8 
+					|| iImageInfo.iBitDepth == 16))
+				{
+					User::Leave(KErrCorrupt); // Invalid bit depth for color type 0
+				}
+			}
+			break;
+		case TPngImageInformation::EDirectColor: // 2
+		case TPngImageInformation::EAlphaGrayscale: // 4
+		case TPngImageInformation::EAlphaDirectColor: // 6
+			{
+				if (! (iImageInfo.iBitDepth == 8 
+					|| iImageInfo.iBitDepth == 16))
+				{
+					User::Leave(KErrCorrupt); // Invalid bit depth for color type 2 or 4 or 6
+				}
+			}
+			break;
+		case TPngImageInformation::EIndexedColor: //3
+			{
+				if (! (iImageInfo.iBitDepth == 1 
+					|| iImageInfo.iBitDepth == 2 
+					|| iImageInfo.iBitDepth == 4
+					|| iImageInfo.iBitDepth == 8))
+				{
+					User::Leave(KErrCorrupt); // Invalid bit depth for color type 3
+				}
+			}
+			break;
+		default: // Invalid color depth.
+			{
+				User::Leave(KErrCorrupt);
+			}
+		}
+	}
+
+void CPngReadCodec::DoProcessPLTEL(const TUint8* aDataPtr,TInt aChunkLength)
+	{
+	const TInt paletteEntries = aChunkLength / 3;
+
+	if ((aChunkLength % 3 != 0)||(paletteEntries > KPngMaxPLTESize))
+		User::Leave(KErrCorrupt);
+
+	iImageInfo.iPalettePresent = ETrue;
+
+	const TUint8* dataPtrLimit = aDataPtr + aChunkLength;
+	TRgb* palettePtr = iImageInfo.iPalette;
+
+	while (aDataPtr < dataPtrLimit)
+		{
+		*palettePtr++ = TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]);
+		aDataPtr += 3;
+		}
+	}
+
+void CPngReadCodec::DoProcessbKGDL(const TUint8* aDataPtr,TInt aChunkLength)
+	{
+	iImageInfo.iBackgroundPresent = ETrue;
+	if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor) // 3
+		{
+		if (aChunkLength < 1)
+			User::Leave(KErrCorrupt);
+
+		iImageInfo.iBackgroundColor = iImageInfo.iPalette[aDataPtr[0]];
+		}
+	else if (iImageInfo.iColorType & TPngImageInformation::EColorUsed) // 2 & 6
+		{
+		if (aChunkLength < 6)
+			User::Leave(KErrCorrupt);
+
+		TInt red = PtrReadUtil::ReadBigEndianUint16(&aDataPtr[0]);
+		TInt green = PtrReadUtil::ReadBigEndianUint16(&aDataPtr[2]);
+		TInt blue = PtrReadUtil::ReadBigEndianUint16(&aDataPtr[4]);
+
+		//Allow negative shift on 48 bpp images
+		TInt offset = 8-iImageInfo.iBitDepth;
+		if(offset > 0)
+			{
+			red <<= offset;
+			green <<= offset;
+			blue <<= offset;
+			}
+		else
+			{
+			offset = -offset;
+			red >>= offset;
+			green >>= offset;
+			blue >>= offset;
+			}
+
+		iImageInfo.iBackgroundColor = TRgb(red,green,blue);
+		}
+	else
+		{
+		// Monochome images (iColorType 0 & 4)
+		ASSERT((iImageInfo.iColorType == TPngImageInformation::EGrayscale) || (iImageInfo.iColorType == TPngImageInformation::EAlphaGrayscale));
+		if (aChunkLength < 2)
+			User::Leave(KErrCorrupt);
+
+		TInt grayLevel = PtrReadUtil::ReadBigEndianUint16(aDataPtr);
+		switch (iImageInfo.iBitDepth)
+			{
+			case 16:
+				grayLevel >>= 8;
+				iImageInfo.iBackgroundColor = TRgb::Gray256(grayLevel);
+				break;
+
+			case 8:
+				iImageInfo.iBackgroundColor = TRgb::Gray256(grayLevel);
+				break;
+
+			case 4:
+				iImageInfo.iBackgroundColor = TRgb::Gray16(grayLevel);
+				break;
+
+			case 2:
+				iImageInfo.iBackgroundColor = TRgb::Gray4(grayLevel);
+				break;
+
+			case 1:
+				iImageInfo.iBackgroundColor = TRgb::Gray2(grayLevel);
+				break;
+
+			default:
+				ASSERT(0);
+			}
+		}
+	}
+
+void CPngReadCodec::DoProcesspHYsL(const TUint8* aDataPtr,TInt aChunkLength)
+	{
+	if (aChunkLength != KPngpHYsChunkSize)
+		User::Leave(KErrCorrupt);
+
+	iImageInfo.iPhysicalUnits = TPngImageInformation::TPhysicalUnits(aDataPtr[8]);
+
+	if (iImageInfo.iPhysicalUnits == TPngImageInformation::EMeters)
+		{
+		iImageInfo.iPhysicalPresent = ETrue;
+
+		TInt horzPixelsPerMeter = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
+		TInt vertPixelsPerMeter = PtrReadUtil::ReadBigEndianUint32Inc(aDataPtr);
+
+		if (horzPixelsPerMeter > 0)
+			iImageInfo.iPhysicalSize.iWidth = iImageInfo.iSize.iWidth * KTwipsPerMeter / horzPixelsPerMeter;
+		if (vertPixelsPerMeter > 0)
+			iImageInfo.iPhysicalSize.iHeight = iImageInfo.iSize.iHeight * KTwipsPerMeter / vertPixelsPerMeter;
+		}
+	}
+
+void CPngReadCodec::DoProcesstRNSL(const TUint8* aDataPtr,TInt aChunkLength)
+	{
+	iImageInfo.iTransparencyPresent = ETrue;
+
+	if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor) // 3
+		{
+#if defined(_DEBUG)
+	ASSERT(sizeof(iImageInfo.iTransparencyValue)>sizeof(TAny*)); // ie it is not an allocated ptr.
+#endif  // defined
+		if (aChunkLength < 1 || TInt(sizeof(iImageInfo.iTransparencyValue)) < aChunkLength)
+			User::Leave(KErrCorrupt);
+
+		Mem::Copy(iImageInfo.iTransparencyValue,aDataPtr,aChunkLength);
+		}
+	else if (iImageInfo.iColorType == TPngImageInformation::EGrayscale) // 0
+		{
+		if (aChunkLength < 2)
+			User::Leave(KErrCorrupt);
+
+		iImageInfo.iTransparentGray = TUint16((aDataPtr[0] << 8) | aDataPtr[1]);
+		}
+	else if (iImageInfo.iColorType == TPngImageInformation::EDirectColor) // 2
+		{
+		if (aChunkLength < 6)
+			User::Leave(KErrCorrupt);
+
+		iImageInfo.iTransparentRed = TUint16((aDataPtr[0] << 8) | aDataPtr[1]);
+		iImageInfo.iTransparentGreen = TUint16((aDataPtr[2] << 8) | aDataPtr[3]);
+		iImageInfo.iTransparentBlue = TUint16((aDataPtr[4] << 8) | aDataPtr[5]);
+		}
+	}
+
+TBool CPngReadCodec::SetupProcessData(TBufPtr8& aSrc,
+									 TUint8* aStartDataPtr,
+									 const TUint8*& aDataPtr,
+									 const TUint8* aDataPtrLimit)
+	{
+	iSavedSrc = &aSrc;
+	iStartDataPtr = aStartDataPtr;
+	TInt bytesToProcess = Min(aDataPtrLimit - aDataPtr,iChunkBytesRemaining);
+	if(bytesToProcess<=0)
+		return EFalse;
+	iDataPtr = aDataPtr;
+	iDataDes.Set(aDataPtr,bytesToProcess);
+	iDecompressor->SetInput(iDataDes);
+	return ETrue;
+	}
+
+const TInt KInflateLimit=4; // max times we try to Inflate on each call
+
+TBool CPngReadCodec::DoProcessDataL()
+	{	
+	TInt bytesToProcess = iDataDes.Length(); // we stored this in SetupProcessData()
+	ASSERT(bytesToProcess>0);
+	TBool result = EFalse;
+	TBool callAgain = ETrue;
+	for (TInt count = 0; count < KInflateLimit &&
+						 callAgain &&
+						 iDecompressor->AvailIn() != 0; count++)
+		{
+		callAgain = iDecompressor->InflateL();
+		}
+
+	const TInt availData = iDecompressor->AvailIn();
+	if (!availData)
+		{
+		// Run out of data, get next buffer
+		iPreviousChunkReadFailed = ETrue;
+		result = ETrue;
+
+		// Advance the buffer
+		iDataPtr += bytesToProcess - availData;
+		iChunkBytesRemaining -= bytesToProcess - availData;
+		iSavedSrc->Shift(iDataPtr - iStartDataPtr);
+		}
+	return result;
+	}
+
+void CPngReadCodec::InitializeL(CEZZStream& aZStream)
+	{
+	aZStream.SetOutput(iDecoder->FirstBuffer());
+	}
+
+void CPngReadCodec::NeedInputL(CEZZStream& /*aZStream*/)
+	{
+	}
+
+void CPngReadCodec::NeedOutputL(CEZZStream& aZStream)
+	{
+	aZStream.SetOutput(iDecoder->DecodeL());
+	}
+
+void CPngReadCodec::FinalizeL(CEZZStream& aZStream)
+	{
+	TPtrC8 buffer(aZStream.OutputDescriptor());
+	if(buffer.Length())
+		iDecoder->DecodeL();
+	}
+void CPngReadCodec::GetNewDataPosition(TInt& aPosition, TInt&  /*aLength*/ )
+	{
+	aPosition += iNewPosition;
+	}
+
+void CPngReadCodec::SetMissingiENDChunkFail(TBool aValue)
+	{
+	iMissingiENDChunkFail = aValue;
+	}
+
+//Checks if Image processor is to be used or skipped. Returns ETrue if ImageProcessor is to be ignored.
+TBool CPngReadCodec::SkipImageProcessor(CFbsBitmap& aDestination)
+	{
+	TBool skipImgProc = EFalse;
+	
+	// If the image is interlaced, has transparency or is required to be scaled, then use the generic image processors
+	if(iImageInfo.iInterlaceMethod != TPngImageInformation::ENoInterlace || iImageInfo.iTransparencyPresent || aDestination.SizeInPixels().iWidth != iImageInfo.iSize.iWidth || aDestination.SizeInPixels().iHeight != iImageInfo.iSize.iHeight)
+		{
+		return EFalse;
+		}
+	
+	TDisplayMode mode = aDestination.DisplayMode();
+	// Skip ImageProcessor only when decoding 24 or 32 bpp images
+	switch (iImageInfo.iBitDepth)
+		{
+		case 8:
+			switch (iImageInfo.iColorType)
+				{
+				case TPngImageInformation::EDirectColor:
+					if(EColor16MAP == mode || EColor16MA == mode || EColor16MU == mode || EColor16M == mode)
+						{
+						skipImgProc = ETrue;	
+						}
+					break;
+				case TPngImageInformation::EAlphaDirectColor:
+					if(EColor16MAP == mode || EColor16MA == mode || EColor16MU == mode)
+						{
+						skipImgProc = ETrue;	
+						}
+					break;			
+				default:
+					break;
+				}
+			break;
+		default:
+			break;
+		}
+		
+	return skipImgProc;
+	}
+
+// CPngWriteCodec
+CPngWriteCodec::CPngWriteCodec(CPngEncoder& aPlugin, TInt aBpp, TBool aColor, TBool aPaletted, TInt aCompressionLevel):
+	iCompressionLevel(aCompressionLevel),
+	iCompressorPtr(NULL, 0),
+	iPlugin(aPlugin)
+	{
+	// Set bpp
+	iImageInfo.iBitsPerPixel = aBpp;
+	switch (aBpp)
+		{
+		case 1:
+			iImageInfo.iBitDepth = 1;
+			break;
+		case 2:
+			iImageInfo.iBitDepth = 2;
+			break;
+		case 4:
+			iImageInfo.iBitDepth = 4;
+			break;
+		case 8:
+		case 24:
+			iImageInfo.iBitDepth = 8;
+			break;
+		default:
+			break;
+		}
+
+	// Set color type
+	if (aColor && aPaletted)
+		iImageInfo.iColorType = TPngImageInformation::EIndexedColor;
+	else if (aColor)
+		iImageInfo.iColorType = TPngImageInformation::EDirectColor;
+	else
+		iImageInfo.iColorType = TPngImageInformation::EGrayscale;
+	}
+
+CPngWriteCodec::~CPngWriteCodec()
+	{
+	delete iCompressor;
+	delete iEncoder;
+	}
+
+CPngWriteCodec* CPngWriteCodec::NewL(CPngEncoder& aPlugin, TInt aBpp, TBool aColor, TBool aPaletted, TInt aCompressionLevel)
+{
+	CPngWriteCodec* self = new(ELeave) CPngWriteCodec(aPlugin, aBpp, aColor, aPaletted, aCompressionLevel);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self); 
+	return self;
+}
+
+
+
+// from CImageWriteCodec
+void CPngWriteCodec::InitFrameL(TBufPtr8& aDst, const CFbsBitmap& aSource)
+	{
+	if (aDst.Length() == 0)
+		User::Leave(KErrArgument);	// Not enough length for anything
+
+	SetSource(&aSource);
+	iDestStartPtr = const_cast<TUint8*>(aDst.Ptr());
+	iDestPtr = iDestStartPtr;
+	iDestPtrLimit = iDestPtr + aDst.MaxLength();
+
+	// Set image information
+	const SEpocBitmapHeader& header = aSource.Header();
+	iImageInfo.iSize = header.iSizeInPixels;
+
+	switch (iImageInfo.iBitDepth)
+		{
+		case 1:
+		case 2:
+		case 4:
+			if (iImageInfo.iColorType == TPngImageInformation::EDirectColor)
+				{
+				// Bit depths 1, 2 and 4 don't support RGB colour (color mode 2)
+				// Must use paletted colour or greyscale
+				User::Leave(KErrNotSupported);
+				break;
+				}
+			// fall through to case 8
+		case 8:
+			break;
+		default:
+			User::Leave(KErrNotSupported);	// unsupported bit depth
+			break;
+		}
+
+	iImageInfo.iCompressionMethod = TPngImageInformation::EDeflateInflate32K;
+	iImageInfo.iFilterMethod = TPngImageInformation::EAdaptiveFiltering;
+	iImageInfo.iInterlaceMethod = TPngImageInformation::ENoInterlace;
+
+	// Create encoder
+	if (iEncoder)
+		{
+		delete iEncoder;
+		iEncoder = NULL;
+		}
+	iEncoder = CPngWriteSubCodec::NewL(iImageInfo, &aSource);
+
+	// Create compressor
+	if (iCompressor)
+		{
+		delete iCompressor;
+		iCompressor = NULL;
+		}
+	iCompressor = CEZCompressor::NewL(*this, iCompressionLevel);
+
+	// Initial encoder state
+	if (iImageInfo.iColorType == TPngImageInformation::EIndexedColor)
+		iEncoderState = EPngWritePLTE;	
+	else
+		iEncoderState = EPngInit;
+	iCallAgain = ETrue;		// to make sure we call DeflateL
+
+	// Write header
+	User::LeaveIfError(WriteHeaderChunk(aDst));
+	}
+
+TFrameState CPngWriteCodec::ProcessFrameL(TBufPtr8& aDst)
+	{
+	if (aDst.MaxLength() == 0)
+		User::Leave(KErrArgument);	// Not enough length for anything
+
+	TFrameState state = EFrameIncomplete;
+	iDestStartPtr = const_cast<TUint8*>(aDst.Ptr());
+	iDestPtr = iDestStartPtr;
+	iDestPtrLimit = iDestPtr + aDst.MaxLength();
+
+	// Set return buffer length to 0 initially
+	aDst.SetLength(0);
+
+	while (aDst.Length() == 0 && state != EFrameComplete)
+		{
+		// Loop round until we have some data to return or
+		// the image is encoded
+		switch (iEncoderState)
+			{
+			case EPngWritePLTE:
+				WritePLTEChunk(aDst);
+				break;
+			case EPngInit:
+				InitializeCompressorL(aDst);
+				ASSERT(iEncoderState==EPngDeflate);
+				break;
+			case EPngDeflate:
+			    SetCompressorOutputL(aDst);
+				iPlugin.GoToProcessDataState();
+				return state;
+			case EPngWriteIDAT:
+				WriteIDATChunk(aDst);
+				ASSERT(iEncoderState==EPngDeflate || iEncoderState==EPngEndChunk);
+				break;
+			case EPngEndChunk:
+				WriteEndChunk(aDst);
+				state = EFrameComplete;
+				break;
+			default:
+				ASSERT(EFalse);
+				break;
+			}
+		}
+
+	return state;
+	}
+
+void CPngWriteCodec::SetCompressorOutputL(TBufPtr8& aDst)
+    {
+	// Set ptr for compressed data
+	const TInt dataLength = aDst.MaxLength() - KPngChunkLengthSize - KPngChunkIdSize - KPngChunkCRCSize;
+
+	if(dataLength <= 0)
+	    {
+	    // this effectively means that framework is broken
+	    // and can't provide encoder with sufficiently big buffer
+	    ASSERT( EFalse );
+	    User::Leave(KErrArgument);
+	    }
+
+	iCompressorPtr.Set(iDestPtr + KPngChunkIdSize + KPngChunkLengthSize, dataLength, dataLength);    
+	iCompressor->SetOutput(iCompressorPtr);
+    }
+
+void CPngWriteCodec::InitializeCompressorL(TBufPtr8& aDst)
+	{
+	// Initialise input/output for compressor
+	iCompressor->SetInput(iEncoder->EncodeL(iScanline));
+	
+	SetCompressorOutputL(aDst);
+    iScanline++;
+	iEncoderState = EPngDeflate;
+	}
+
+TBool CPngWriteCodec::DeflateEncodedDataL()
+	{
+	ASSERT(iCallAgain);
+	iCallAgain = iCompressor->DeflateL();
+	//finished when NeedOutputL() or FinalizeL() change the state to EPngWriteIDAT
+	ASSERT(iEncoderState==EPngDeflate || iEncoderState==EPngWriteIDAT);
+	return(iEncoderState!=EPngDeflate);
+	}
+
+void CPngWriteCodec::WritePLTEChunk(TBufPtr8& aDst)
+	{
+	ASSERT(iEncoder->Palette() &&
+		   (iImageInfo.iColorType == TPngImageInformation::EIndexedColor ||
+		    iImageInfo.iColorType == TPngImageInformation::EDirectColor ||
+		    iImageInfo.iColorType == TPngImageInformation::EAlphaDirectColor));	// allowed color types for PLTE chunk
+
+	// Get palette entries
+	CPalette* palette = iEncoder->Palette();
+	ASSERT(palette);
+	const TInt count = palette->Entries();
+	TUint8* ptr = iDestPtr + KPngChunkIdSize + KPngChunkLengthSize;
+	TInt length = count * 3;
+	TPtr8 data(ptr, length, length);
+	for (TInt i=0; i < count; i++)
+		{
+		TRgb rgb = palette->GetEntry(i);
+		*ptr = TUint8(rgb.Red());
+		ptr++;
+		*ptr = TUint8(rgb.Green());
+		ptr++;
+		*ptr = TUint8(rgb.Blue());
+		ptr++;
+		}
+	// Write PLTE chunk
+	WritePngChunk(iDestPtr, KPngPLTEChunkId, data, length);
+	ASSERT(length % 3 == 0);	// length must be divisible by 3
+	aDst.SetLength(length);
+	iEncoderState = EPngInit;
+	}
+
+void CPngWriteCodec::WriteIDATChunk(TBufPtr8& aDst)
+	{
+	TPtrC8 ptr(iCompressor->OutputDescriptor());
+	if (ptr.Length())
+		{
+		TInt length = 0;
+		WritePngChunk(iDestPtr, KPngIDATChunkId, ptr, length);
+		aDst.SetLength(length);
+
+		// New output can write to the same compressor ptr
+		iCompressor->SetOutput(iCompressorPtr);
+		}
+
+	if (iCallAgain)
+		iEncoderState = EPngDeflate;
+	else
+		iEncoderState = EPngEndChunk;
+	}
+
+void CPngWriteCodec::WriteEndChunk(TBufPtr8& aDst)
+	{
+	// Write IEND chunk
+	TInt length = 0;
+	WritePngChunk(iDestPtr, KPngIENDChunkId, KNullDesC8, length);
+	aDst.SetLength(length);
+	}
+
+TInt CPngWriteCodec::WriteHeaderChunk(TBufPtr8& aDst)
+	{
+	// Write signature
+	Mem::Copy(iDestPtr, &KPngSignature[0], KPngFileSignatureLength);
+	iDestPtr += KPngFileSignatureLength;
+
+	// Write IHDR chunk
+	TBuf8<KPngIHDRChunkSize> buffer;
+	TUint8* ptr = const_cast<TUint8*>(buffer.Ptr());
+	// Set length of data
+	buffer.SetLength(KPngIHDRChunkSize);
+	// Chunk data
+	// width (4 bytes)
+	if ((iImageInfo.iSize.iWidth == 0) ||
+		(static_cast<TUint>(iImageInfo.iSize.iWidth) > KPngMaxImageSize))
+		{
+		return KErrArgument;	// invalid width
+		}
+	PtrWriteUtil::WriteBigEndianInt32(ptr, iImageInfo.iSize.iWidth);
+	ptr += 4;
+	// height (4 bytes)
+	if ((iImageInfo.iSize.iHeight == 0) ||
+		(static_cast<TUint>(iImageInfo.iSize.iHeight) > KPngMaxImageSize))
+		{
+		return KErrArgument;	// invalid height
+		}
+	PtrWriteUtil::WriteBigEndianInt32(ptr, iImageInfo.iSize.iHeight);
+	ptr += 4;
+	// bit depth (1 byte)
+	PtrWriteUtil::WriteInt8(ptr, iImageInfo.iBitDepth);
+	ptr++;
+	// colour type (1 byte)
+	PtrWriteUtil::WriteInt8(ptr, iImageInfo.iColorType);
+	ptr++;
+	// compression method (1 byte)
+	PtrWriteUtil::WriteInt8(ptr, iImageInfo.iCompressionMethod);
+	ptr++;
+	// filter method (1 byte)
+	PtrWriteUtil::WriteInt8(ptr, iImageInfo.iFilterMethod);
+	ptr++;
+	// interlace method (1 byte)
+	PtrWriteUtil::WriteInt8(ptr, iImageInfo.iInterlaceMethod);
+	ptr++;
+
+	TInt length = 0;
+	WritePngChunk(iDestPtr, KPngIHDRChunkId, buffer, length);
+	aDst.SetLength(KPngFileSignatureLength + length);
+
+	return KErrNone;
+	}
+
+void CPngWriteCodec::WritePngChunk(TUint8*& aDestPtr, const TDesC8& aChunkId, const TDesC8& aData, TInt& aLength)
+	{
+	// Chunk length (4 bytes)
+	PtrWriteUtil::WriteBigEndianInt32(aDestPtr, aData.Length());
+	aDestPtr += KPngChunkLengthSize;
+	TUint8* crcPtr = aDestPtr;	// start position for calculating CRC
+	// Chunk type (4 bytes)
+	Mem::Copy(aDestPtr, aChunkId.Ptr(), KPngChunkIdSize);
+	aDestPtr += KPngChunkIdSize;
+	// Chunk data (0...n bytes)
+	Mem::Copy(aDestPtr, aData.Ptr(), aData.Length());
+	aDestPtr += aData.Length();
+	// CRC (4 bytes)
+	TUint32 crc = KPngCrcMask;
+	GetCrc(crc, crcPtr, KPngChunkIdSize + aData.Length());
+	crc ^= KPngCrcMask;
+	PtrWriteUtil::WriteBigEndianInt32(aDestPtr, crc);
+	aDestPtr += KPngChunkCRCSize;
+	// Length of chunk
+	aLength = KPngChunkLengthSize + KPngChunkIdSize + aData.Length() + KPngChunkCRCSize;
+	}
+
+// from MEZBufferManager
+void CPngWriteCodec::InitializeL(CEZZStream& /*aZStream*/)
+	{
+	}
+
+void CPngWriteCodec::NeedInputL(CEZZStream& aZStream)
+	{
+	// Give compressor more data from encoder
+	aZStream.SetInput(iEncoder->EncodeL(iScanline));
+	if (iCompressor->AvailIn() != 0)
+		iScanline++;
+	}
+
+void CPngWriteCodec::NeedOutputL(CEZZStream& /*aZStream*/)
+	{
+	// Signal to write an IDAT chunk
+	iEncoderState = EPngWriteIDAT;
+	}
+
+void CPngWriteCodec::FinalizeL(CEZZStream& /*aZStream*/)
+	{
+	// Signal to write an IDAT chunk
+	iEncoderState = EPngWriteIDAT;
+	}
+
+// New functions
+void CPngWriteCodec::GetCrc(TUint32& aCrc, const TUint8* aPtr, const TInt aLength)
+	{
+	if (!iCrcTableCalculated)
+		CalcCrcTable();
+	TUint32 code = aCrc;
+	for (TInt i=0; i < aLength; i++)
+		code = iCrcTable[(code ^ aPtr[i]) & 0xff] ^ (code >> 8);
+	aCrc = code;
+	}
+
+void CPngWriteCodec::CalcCrcTable()
+	{
+	for (TInt i=0; i < KPngCrcTableLength; i++)
+		{
+		TUint32 code = static_cast<TUint32>(i);
+
+		for (TInt j = 0; j < 8; j++)
+			{
+			if (code & 1)
+				code = 0xedb88320 ^ (code >> 1);
+			else
+				code = code >> 1;
+			}
+		iCrcTable[i] = code;
+		}
+	iCrcTableCalculated = ETrue;
+	}