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