--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmplugins/imagingplugins/codecs/GifCodec/GIFcodec.cpp Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,1662 @@
+// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// Licensed under US Patent No 4,558,302 and foreign counterparts
+//
+//
+
+// CGifWriteCodec::TableEntry() is based on compress_byte() from wrgif.c
+// in the IJG V6 code, Copyright (C) 1991-1997, Thomas G. Lane.
+
+#include <fbs.h>
+#include "GIFcodec.h"
+#include "rawimageprocessor.h"
+
+const TInt KPass2StartLine = 4;
+const TInt KPass3StartLine = 2;
+const TInt KPass4StartLine = 1;
+const TInt KPass1YPosIncrement = 8;
+const TInt KPass2YPosIncrement = 8;
+const TInt KPass3YPosIncrement = 4;
+const TInt KPass4YPosIncrement = 2;
+const TInt KPass1LineRepeat = 7;
+const TInt KPass2LineRepeat = 3;
+const TInt KPass3LineRepeat = 1;
+const TInt KPass4LineRepeat = 0;
+const TUint KLZWLimit = 20000;
+const TInt KSetCommentsLimit = 64;
+const TUint16 KTranspColIdxNotPresent = 0xFFFF;
+
+
+// CGifReadCodec
+CGifReadCodec::CGifReadCodec(TRgb* aGlobalPalette,TInt aBackgroundColorIndex, TSize aScreenSize,TBool aFastDecode):
+ iBackgroundColorIndex(aBackgroundColorIndex),
+ iScreenSize(aScreenSize),
+ iGlobalPalette(aGlobalPalette),
+ iFastDecode(aFastDecode)
+ {
+ }
+
+CGifReadCodec::~CGifReadCodec()
+ {
+ iComment.ResetAndDestroy();
+ delete [] iPrefixIndex;
+ delete [] iSuffixCode;
+ delete [] iOutputString;
+ delete [] i64KPalette;
+ delete [] iPixelBuffer;
+ delete [] iMaskBuffer;
+ }
+
+CGifReadCodec* CGifReadCodec::NewL(TRgb* aGlobalPalette,TInt aBackgroundColorIndex, TSize aScreenSize,TBool aFastDecode)
+ {
+ CGifReadCodec* self = new(ELeave) CGifReadCodec(aGlobalPalette, aBackgroundColorIndex, aScreenSize, aFastDecode);
+ CleanupStack::PushL(self);
+
+ self->ConstructL();
+
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+void CGifReadCodec::ConstructL()
+ {
+ CImageMaskProcessorReadCodec::ConstructL();
+
+ iPrefixIndex = new (ELeave) TInt16[KGifConversionTableSize +1];
+ iSuffixCode = new (ELeave) TUint8 [KGifConversionTableSize+1];
+ iOutputString = new (ELeave) TUint8 [KGifConversionTableSize];
+ iPPos = &Pos();
+ iPixelBuffer = NULL;
+ iMaskBuffer = NULL;
+ }
+
+void CGifReadCodec::InitFrameHeader(TFrameInfo& aFrameSettings, CFrameImageData& aFrameImageData)
+ {
+ ASSERT(aFrameSettings.CurrentFrameState() == TFrameInfo::EFrameInfoUninitialised);
+ iFrameInfo = &aFrameSettings;
+ iFrameData = &aFrameImageData;
+ iFrameImageDesc = NULL;
+ iFrameImageControl = NULL;
+ iReadingOtherExtensionBlock=EFalse;
+ iReadingCommentExtensionBlock = EFalse;
+ iComment.ResetAndDestroy();
+ iCommentIndex = 0;
+ iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrameHeader);
+ }
+
+TFrameState CGifReadCodec::ProcessFrameHeaderL(TBufPtr8& aData)
+ {
+ iStartDataPtr = CONST_CAST(TUint8*,aData.Ptr());
+ iDataPtr = iStartDataPtr;
+ iDataPtrLimit = iStartDataPtr + aData.Length();
+
+ if (iFrameInfo->CurrentFrameState() == TFrameInfo::EFrameInfoProcessingFrame)
+ {
+ do {
+ //we need the block length (iDataPtr[0]+1) + the first byte of the next block
+ //(used in the KGifTerminatorId evaluation below)
+ if((iDataPtr + iDataPtr[0] + 2)<iDataPtrLimit)
+ {// If there is enough data in the buffer to continue processing header info
+ iFrameLZWInfo->iCompressedBytes += iDataPtr[0] + 1;
+
+ // Shift the local buffer data pointer to point to the beginning of the next block
+ iDataPtr += iDataPtr[0] + 1;
+ }
+ else
+ { // Else if there is not enough data in the buffer shift the 'aData' pointer
+ // to point to the degining of the unused data and return to the calling method
+ // so that the buffer can be refilled.
+ aData.Shift((iDataPtr - iStartDataPtr));
+ return EFrameIncomplete;
+ }
+ } while(iDataPtr[0]!=KGifFrameTerminatorId); //Continue until end of frame is reached
+
+ if(iDataPtr[1] == KGifTerminatorId)
+ { // End of Image File
+ DoSaveCommentsL();
+ iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete);
+ return EFrameComplete;
+ }
+ else if ((iDataPtr[1] == KGifImageDescriptorId) || (iDataPtr[1] == KGifExtensionId))
+ { // End of Frame
+ DoSaveCommentsL();
+ aData.Shift((iDataPtr - iStartDataPtr)+1);
+ iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingComplete);
+ return EFrameIncomplete;
+ }
+ else
+ { // Unexpected end of data
+ return EUnexpectedEndOfData;
+ }
+ }
+
+ if (iFrameInfo->CurrentFrameState() == TFrameInfo::EFrameInfoProcessingFrameHeader)
+ {
+ TRAPD(err,DoProcessInfoL(iDataPtr,iDataPtrLimit));
+ if (err == KErrCompletion)
+ {
+ DoSaveCommentsL();
+ return EFrameComplete; // KGifTerminatorId found - no more images
+ }
+
+ if (err == KErrUnderflow)
+ {
+ TInt dataUsed = iDataPtr - iStartDataPtr;
+ aData.Shift(dataUsed);
+ TInt frameDataOffset = iFrameInfo->FrameDataOffset();
+ iFrameInfo->SetFrameDataOffset(frameDataOffset + dataUsed);
+ return EFrameIncomplete;
+ }
+
+ if (err!=KErrNone)
+ {
+ User::LeaveIfError(err); // A real error occured
+ }
+
+ if (iFrameImageDesc == NULL)
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ TInt frameDataOffset = iFrameInfo->FrameDataOffset();
+ iFrameInfo->SetFrameDataOffset(frameDataOffset + (iDataPtr - iStartDataPtr));
+ aData.Shift(iDataPtr - iStartDataPtr);
+
+ iFrameInfo->iFrameSizeInTwips.SetSize(0,0);
+ iFrameInfo->iDelay = (iFrameImageControl) ? iFrameImageControl->iDelayTimeInCentiseconds * KGifCentiSecondsToMicroSeconds : 0;
+ iFrameInfo->iFlags = TFrameInfo::EColor | TFrameInfo::ECanDither | TFrameInfo::EUsesFrameSizeInPixels;
+ if (iFrameImageControl)
+ {
+ if (iFrameImageControl->iTransparentColorIndex != KErrNotFound)
+ {
+ iFrameInfo->iFlags |= TFrameInfo::ETransparencyPossible;
+ if(iFrameImageDesc->iInterlaced)
+ iFrameInfo->iFlags |= TFrameInfo::EPartialDecodeInvalid;
+ }
+ switch (iFrameImageControl->iDisposalMethod)
+ {
+ case TGifImageControl::ELeaveInPlace:
+ iFrameInfo->iFlags |= TFrameInfo::ELeaveInPlace;
+ break;
+ case TGifImageControl::ERestoreToBackground:
+ iFrameInfo->iFlags |= TFrameInfo::ERestoreToBackground;
+ break;
+ case TGifImageControl::ERestoreToPrevious:
+ iFrameInfo->iFlags |= TFrameInfo::ERestoreToPrevious;
+ break;
+ case ENone:
+ default:
+ break;
+ };
+ }
+
+ iFrameInfo->SetCurrentFrameState(TFrameInfo::EFrameInfoProcessingFrame);
+ }
+
+ return EFrameIncomplete;
+ }
+
+TFrameState CGifReadCodec::ProcessFrameL(TBufPtr8& aSrc)
+ {
+ if ((iFrameSize.iWidth == 0) || (iFrameSize.iHeight == 0))
+ return EFrameComplete;
+
+ iPixRead = 0;
+
+ while (aSrc.Length() > 2 && !iComplete)
+ {
+ iDataPtr = aSrc.Ptr();
+ const TInt dataSize = iDataPtr[0];
+ iDataPtr++;
+ if (dataSize == 0 || dataSize >= aSrc.Length())
+ {
+ break;
+ }
+ iDataPtrLimit = iDataPtr + dataSize;
+ DoProcessDataL();
+
+ aSrc.Shift(dataSize + 1);
+ }
+
+ TPoint& pos = Pos();
+ pos = *iPPos;
+ FlushPixBuffer(iLatestPixSize);
+
+ if (iFastAccessMode)
+ {
+ const TInt frameWidth = iFrameSize.iWidth;
+ TInt rowPixels = Min(frameWidth - pos.iX, iPixRead);
+ pos.iX += rowPixels;
+ iPixRead -= rowPixels;
+ if (pos.iX == frameWidth)
+ {
+ TInt rowCount = iPixRead/frameWidth+1;
+ pos.iY += rowCount;
+ iPixRead -= (rowCount-1) * frameWidth;
+ pos.iX = iPixRead;
+ }
+ iPixRead = 0;
+ }
+
+ if (pos.iY >= (iFrameSize.iHeight + iFrameOffset.iY) || iComplete)
+ {
+ ImageProcessor()->FlushPixels();
+ CImageProcessor*const maskProc = MaskProcessor();
+ if (maskProc)
+ maskProc->FlushPixels();
+ pos = iFrameOffset;
+
+ if(iGifImageControl && iGifImageControl->iTransparentColorIndex != KErrNotFound)
+ {
+ // reset the transparency index
+ if (iTranspColIdx != KTranspColIdxNotPresent)
+ {
+ const_cast<TRgb*>(&iPalette[iTranspColIdx])->SetAlpha(0xFF);
+ }
+ }
+
+ return EFrameComplete;
+ }
+
+ return EFrameIncomplete;
+ }
+
+void CGifReadCodec::InitFrameL(TFrameInfo& aFrameInfo, CFrameImageData& aFrameImageData, TBool aDisableErrorDiffusion, CFbsBitmap& aDestination, CFbsBitmap* aDestinationMask)
+ {
+ iFrameInfo = &aFrameInfo;
+ iFrameData = &aFrameImageData;
+
+ //Make the first frame's size equal to the image size
+ if(CurrentFrame()==0)
+ {
+ iFrameSize = iFirstFrameSize;
+ iFrameOffset = iFirstFrameCoords.iTl;
+ }
+ else
+ {
+ iFrameSize = iFrameInfo->iOverallSizeInPixels;
+ iFrameOffset = TPoint(0,0);
+ }
+ iFrameCoords = iFrameInfo->iFrameCoordsInPixels;
+
+ // If the width or height is zero return. A similar test in ProcessFrameL() will complete the image
+ if ((iFrameSize.iWidth == 0) || (iFrameSize.iHeight == 0))
+ {
+ return;
+ }
+
+ if(iPixelBuffer == NULL || CurrentFrame() == 0)
+ {
+ iPixBufferSize = iFrameSize.iWidth * 8;//allocate buffer for first 8 lines
+ if (iPixBufferSize > KPixelBufMaxSize)
+ {
+ iPixBufferSize = KPixelBufMaxSize;
+ }
+ if (aDestinationMask != NULL)
+ {
+ iPixBufferSize /= 2;//reserve space for mask
+ }
+ delete[] iPixelBuffer;
+ iPixelBuffer = new (ELeave) TUint32 [iPixBufferSize];
+ }
+
+ // Extract the image descriptor and control blocks.
+ iGifImageDesc = NULL;
+ iGifLZWInfo = NULL;
+ iGifImageControl = NULL;
+ iGifColorTable = NULL;
+
+ TInt frameDataCount = iFrameData->FrameDataCount();
+ for (TInt frameDataIndex = 0 ; frameDataIndex<frameDataCount ; frameDataIndex++)
+ {
+ TFrameDataBlock* frameData = iFrameData->GetFrameData(frameDataIndex);
+ if (frameData->DataType() == KGIFImageDescriptorUid)
+ {
+ iGifImageDesc = STATIC_CAST(TGifImageDescriptor*, frameData);
+ }
+ else if(frameData->DataType() == KGIFColorTableUid)
+ {
+ iGifColorTable = STATIC_CAST(TGifColorTable*, frameData);
+ }
+ else if(frameData->DataType() == KGIFLZWInfoUid)
+ {
+ iGifLZWInfo = STATIC_CAST(TGifLZWInfo*, frameData);
+ }
+ else if(frameData->DataType() == KGIFImageControlUid)
+ {
+ iGifImageControl = STATIC_CAST(TGifImageControl*, frameData);
+ }
+ }
+ iTranspColIdx = (iGifImageControl) ? iGifImageControl->iTransparentColorIndex : KTranspColIdxNotPresent;
+
+ // Set decode info
+ iPass = 1;
+ iYPosIncrement = KPass1YPosIncrement;
+ iBitBuffer = 0;
+ iBitBuffSize = 0;
+ iFirstChar = 0;
+ iPreviousCode = -1;
+ iComplete = EFalse;
+
+ ASSERT(iGifImageDesc);
+ ASSERT(iGifLZWInfo);
+
+ // Set palette to use
+ if(iGifImageDesc && iGifImageDesc->iLocalColorMap)
+ {
+ //Use of a frame palette was requested but was not found
+ if(iGifColorTable == NULL)
+ {
+ User::Leave(KErrCorrupt);
+ }
+ iPalette = iGifColorTable->iPalette;
+ }
+ else
+ {
+ iPalette = iGlobalPalette;
+ }
+
+ // Set table.Check for iClearCode value between 0 and 4096.
+ iClearCode = (1 << (iGifLZWInfo->iInitialCodeLength - 1));
+ if((iClearCode < 0) || (iClearCode > KGifConversionTableSize))
+ {
+ User::Leave(KErrCorrupt);
+ }
+ iEoiCode = iClearCode + 1;
+ for (TInt singleByteCodes = 0; singleByteCodes < iClearCode; singleByteCodes++)
+ {
+ iSuffixCode[singleByteCodes] = STATIC_CAST(TUint8,singleByteCodes);
+ }
+
+ TInt prefixLength = (KGifConversionTableSize + 1) * sizeof(TInt16);
+ Mem::Fill(iPrefixIndex, prefixLength, TChar(static_cast<TUint>(-1)));
+
+ iNextFree = ResetTableL();
+
+ iReductionFactor = 0;
+
+ CImageProcessor* imageProc = NULL;
+
+ const TSize destinationSize(aDestination.SizeInPixels());
+
+ if(iUseFrameSizeInPixels)
+ {
+ iReductionFactor = ReductionFactor(iFrameInfo->iFrameSizeInPixels,destinationSize);
+ }
+ else
+ {
+ iReductionFactor = ReductionFactor(iFrameInfo->iOverallSizeInPixels,destinationSize);
+ }
+
+ const TDisplayMode destMode = aDestination.DisplayMode();
+
+ iFast64kMode = (iFastDecode &&
+ destMode == EColor64K &&
+ iReductionFactor == 0 &&
+ !iGifImageDesc->iInterlaced
+ );
+
+ if (iFast64kMode && i64KPalette==NULL)
+ {
+ i64KPalette = new (ELeave) T64KPixel[KGifColorTableMaxEntries];
+ Mem::FillZ(i64KPalette, KGifColorTableMaxEntries*sizeof(T64KPixel) );
+ }
+
+
+ if (!iGifImageDesc->iInterlaced && iReductionFactor==0 &&
+ (iFast64kMode || destMode == EColor16M || destMode == EColor16MU || destMode == EColor16MA) )
+ {
+ if (destMode == EColor16M)
+ {
+ imageProc = CRawImageUtilProcessor::NewL();
+ }
+ else
+ {
+ imageProc = CRawImageProcessor::NewL();
+ }
+ }
+
+ if (iFast64kMode)
+ {
+ // calculate fast 64K conversion palette
+ TInt idx = 0;
+ do
+ {
+ i64KPalette[idx] = iPalette[idx]._Color64K();
+ }
+ while (++idx < KGifColorTableMaxEntries);
+
+ iPalette = reinterpret_cast<const TRgb*>( i64KPalette );
+ }
+
+ if (imageProc == NULL)
+ {
+ imageProc = ImageProcessorUtility::NewImageProcessorL(aDestination, iReductionFactor, iFast64kMode? EColor64K:ERgb, aDisableErrorDiffusion);
+ }
+
+ SetImageProcessor(imageProc);
+ iImgProc = imageProc;
+
+ iPositionOffset = TPoint(iFrameCoords.iTl.iX >> iReductionFactor,iFrameCoords.iTl.iY >> iReductionFactor);
+
+ TRect imageRegion(iPositionOffset + iFrameOffset,iPositionOffset + iFrameOffset + iFrameSize);
+ imageProc->PrepareL(aDestination,imageRegion);
+ imageProc->SetPos(iPositionOffset + iFrameOffset);
+ SetPos(iFrameOffset);
+
+ TInt lineLength = (TDisplayModeUtils::NumDisplayModeBitsPerPixel( aDestination.DisplayMode() ) * aDestination.SizeInPixels().iWidth) >> 3;
+
+
+ iFastAccessMode = (iReductionFactor == 0 &&
+ !iGifImageDesc->iInterlaced &&
+ imageRegion.Size() == aDestination.SizeInPixels() &&
+ lineLength == aDestination.ScanLineLength(aDestination.SizeInPixels().iWidth, aDestination.DisplayMode())
+ ); // for direct access to bitmap buffer line length must be aligned to 4 bytes boundary
+
+
+ if (iGifImageDesc->iInterlaced)
+ {
+ imageProc->SetYPosIncrement(iYPosIncrement);
+ if(!iGifImageControl || (iGifImageControl->iTransparentColorIndex==KErrNotFound))
+ {
+ imageProc->SetLineRepeat(KPass1LineRepeat);
+ }
+ }
+
+ CImageProcessor* maskProc = NULL;
+ SetMaskProcessor(NULL);
+ iMaskProc = NULL;
+
+ if(aDestinationMask != NULL)
+ {
+
+ ASSERT(aDestinationMask->SizeInPixels() == aDestination.SizeInPixels());
+ // this should be trapped at CImageDecoder::Convert()
+
+ if (iGifImageControl && (iGifImageControl->iTransparentColorIndex != KErrNotFound) && aDestinationMask->Handle())
+ {
+ ASSERT(MaskProcessor()==NULL);
+
+ if (iMaskBuffer == NULL || CurrentFrame() == 0)
+ {
+ delete [] iMaskBuffer;
+ iMaskBuffer = new (ELeave) TUint32 [iPixBufferSize];
+ }
+
+ iOpaqueMask = 0xFF;// default for 256 mask
+ //Create a mask processor and disable error diffusion
+ if (iGifImageDesc->iInterlaced || iReductionFactor > 0)
+ {
+ maskProc = ImageProcessorUtility::NewImageProcessorL(*aDestinationMask, iReductionFactor, EGray2, ETrue);
+ }
+ else
+ {
+ // use optimized processor for non interlaced mask
+ maskProc = CRawImageUtilProcessor::NewL();
+ if(aDestinationMask->DisplayMode() == EGray2)
+ {
+ iOpaqueMask = 1;
+ }
+ }
+ SetMaskProcessor(maskProc);
+ iMaskProc = maskProc;
+ maskProc->PrepareL(*aDestinationMask,imageRegion);
+ if (iGifImageDesc->iInterlaced)
+ {
+ maskProc->SetYPosIncrement(iYPosIncrement);
+ }
+ // fill mask with black, so by default unknown parts don't draw
+ ClearBitmapL(*aDestinationMask,KRgbBlack);
+ maskProc->SetPos(iPositionOffset + iFrameOffset);
+
+ lineLength = (TDisplayModeUtils::NumDisplayModeBitsPerPixel( aDestinationMask->DisplayMode() ) * aDestinationMask->SizeInPixels().iWidth) >> 3;
+
+ iFastAccessMode = iFastAccessMode && lineLength == aDestinationMask->ScanLineLength(aDestinationMask->SizeInPixels().iWidth, aDestinationMask->DisplayMode()); // for direct access
+ // to bitmap buffer line length must be aligned to 4 bytes boundary
+
+ }
+ }
+ else
+ {
+ if(destMode == EColor16MA && iGifImageControl && iGifImageControl->iTransparentColorIndex != KErrNotFound)
+ {
+ if (iTranspColIdx != KTranspColIdxNotPresent)
+ {
+ const_cast<TRgb*>(&iPalette[iTranspColIdx])->SetAlpha(0);
+ }
+ }
+ delete [] iMaskBuffer;
+ iMaskBuffer = NULL;
+ }
+
+ if ( iGifImageControl && (iGifImageControl->iDisposalMethod == TGifImageControl::ERestoreToBackground) ||
+ maskProc==NULL)
+ {
+ // Clear to background colour if requested or if we have no mask, so that
+ // on streamed partial decodes are background
+ ClearBitmapL(aDestination,iFrameInfo->iBackgroundColor);
+ }
+
+ }
+
+void CGifReadCodec::SetUseFrameSizeInPixels(TBool aUseFrameSizeInPixels)
+ {
+ iUseFrameSizeInPixels = aUseFrameSizeInPixels;
+ }
+
+
+void CGifReadCodec::DoProcessInfoL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
+ {
+ if(!iReadingExtensionBlock)
+ {
+ iBlockId = aDataPtr[0];
+ while (!iBlockId)
+ {
+ aDataPtr++;
+ if (aDataPtr >= aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+
+ iBlockId = aDataPtr[0];
+ }
+ }
+
+ while (iBlockId == KGifExtensionId || iReadingExtensionBlock)
+ {
+ iReadingExtensionBlock = ETrue;
+ DoProcessExtensionL(aDataPtr,aDataPtrLimit);
+ iReadingExtensionBlock = EFalse;
+
+ // we skip empty blocks here
+ while( aDataPtr < aDataPtrLimit && (iBlockId = *aDataPtr) == 0)
+ {
+ ++aDataPtr;
+ }
+
+ if (aDataPtr >= aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+ }
+
+ if (iBlockId == KGifImageDescriptorId)
+ {
+ DoProcessImageDescriptorL(aDataPtr,aDataPtrLimit);
+ }
+ else if (iBlockId == KGifTerminatorId)
+ {
+ User::Leave(KErrCompletion);
+ }
+ else
+ {
+ User::Leave(KErrCorrupt);
+ }
+ }
+
+void CGifReadCodec::DoProcessExtensionL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
+ {
+ // Make sure we leave a block id for DoProcessInfoL()
+ aDataPtrLimit--;
+
+ TInt blockLength;
+ TInt dataPtrVal;
+ const TUint8* dataPtr = aDataPtr;
+ if (iReadingCommentExtensionBlock)
+ {
+ if ((aDataPtr + 2) > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+
+ dataPtrVal = KGifCommentExtensionId;
+ blockLength = aDataPtr[0];
+
+ if ((aDataPtr + blockLength + 2) > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+ }
+ else if (iReadingOtherExtensionBlock)
+ {
+ if ((aDataPtr + 2) > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+
+ dataPtrVal = KGifApplicationExtensionId;
+ blockLength = aDataPtr[0];
+
+ if ((aDataPtr + blockLength + 2) > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+ }
+ else
+ {
+ // we start reading block, there should be at least 3 bytes - id, ext. id, length
+ if ((aDataPtr + KGifExtBlkHeaderSize) > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+
+ ++dataPtr;
+ dataPtrVal = *dataPtr; // get the ext. ID
+ ++dataPtr;
+ blockLength = *dataPtr;
+
+ // ensure that we've got all the block data in the buffer
+ // that's possible since max. block length for GIF is 255 bytes
+ if ((dataPtr + blockLength) > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+ }
+
+ switch (dataPtrVal)
+ {
+ case KGifApplicationExtensionId:
+ case KGifPlainTextExtensionId:
+ {
+ aDataPtr = dataPtr;
+ iReadingOtherExtensionBlock = ETrue;
+
+ while (blockLength != 0)
+ {
+ if (dataPtr + blockLength >= aDataPtrLimit)
+ {
+ aDataPtr = dataPtr;
+ User::Leave(KErrUnderflow);
+ }
+ if (iReadingLoopIterationExtensionBlock)
+ {
+ TInt loopIterations = dataPtr[2] + (dataPtr[3] << 8);
+ TGifLoopIterations* iterationsDataBlock = new(ELeave) TGifLoopIterations;
+ CleanupStack::PushL(iterationsDataBlock);
+ iterationsDataBlock->iLoopIterations = loopIterations;
+ User::LeaveIfError(iFrameData->AppendImageData(iterationsDataBlock));
+ CleanupStack::Pop(iterationsDataBlock);
+ iReadingLoopIterationExtensionBlock = EFalse;
+ }
+ else if (blockLength == KAppIdBlockLength)
+ {
+ TPtrC8 appId(dataPtr + 1, KAppIdLength);
+ TPtrC8 appAuthenticCode(dataPtr + 1 + KAppIdLength, KAppAuthenticCodeLength);
+ if ((appId == KAppIdNetscape) && (appAuthenticCode == KAppAuthenticCode2_0))
+ {
+ iReadingLoopIterationExtensionBlock = ETrue;
+ }
+ }
+
+ dataPtr += blockLength + 1;
+ if (dataPtr >= aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+ blockLength = dataPtr[0];
+ }
+
+ iReadingOtherExtensionBlock = EFalse;
+ aDataPtr = dataPtr;
+ }
+ break;
+
+ case KGifCommentExtensionId:
+ { // The problem here is a Block header is expected when were actually half way through the block!!!!!!!
+ if (!iReadingCommentExtensionBlock)
+ {
+ if(iCommentIndex < KSetCommentsLimit)
+ {
+ ASSERT(iComment.Count()==iCommentIndex);
+ HBufC8* comment = HBufC8::NewL(blockLength);
+ CleanupStack::PushL(comment);
+ User::LeaveIfError(iComment.Append(comment));
+ CleanupStack::Pop(comment);
+ }
+ iReadingCommentExtensionBlock = ETrue;
+ }
+ else
+ {
+ ASSERT(iComment.Count()==iCommentIndex+1);
+ HBufC8* comment = iComment[iCommentIndex];
+ iComment[iCommentIndex] = comment->ReAllocL(comment->Length()+blockLength);
+ }
+
+ TPtr8 commentPtr(NULL,0);
+ if(iCommentIndex < KSetCommentsLimit)
+ {
+ commentPtr.Set( iComment[iCommentIndex]->Des() );
+ }
+ while (blockLength != 0)
+ {
+ // Append the block.
+ TPtrC8 newBlock(&dataPtr[1], blockLength);
+ if(iCommentIndex < KSetCommentsLimit)
+ {
+ commentPtr.Append(newBlock);
+ }
+
+ // Calculate the new block/comment length.
+ dataPtr += blockLength + 1;
+ if(dataPtr > aDataPtrLimit)
+ {
+ aDataPtr = dataPtr;
+ User::Leave(KErrUnderflow);
+ }
+ blockLength = dataPtr[0];
+
+ // Leave if the data for the new block is not present.
+ if (blockLength && ((dataPtr+blockLength+1) >= aDataPtrLimit))
+ {
+ aDataPtr = dataPtr;
+ User::Leave(KErrUnderflow);
+ }
+
+ // Re-allocate the comment, if there is another block.
+ if (blockLength != 0&&(iCommentIndex < KSetCommentsLimit))
+ {
+ HBufC8* comment = iComment[iCommentIndex];
+ iComment[iCommentIndex] = comment->ReAllocL(comment->Length()+blockLength);
+ commentPtr.Set(iComment[iCommentIndex]->Des());
+ }
+ }
+
+ iCommentIndex++;
+
+ aDataPtr = dataPtr;
+ }
+
+ iReadingCommentExtensionBlock = EFalse;
+ break;
+
+ case KGifGraphicControlExtensionId:
+ if (aDataPtr + blockLength + KGifExtBlkHeaderSize + 1 >= aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+
+ aDataPtr += (KGifExtBlkHeaderSize + blockLength);
+ // we ignore malformed extension block
+ if (blockLength < KMinGifGraphicControlBlkLen)
+ {
+ break;
+ }
+
+ ++dataPtr; //go into block data
+ TUint8 flags = *dataPtr++;
+
+ TGifImageControl* gifImageControl = new(ELeave) TGifImageControl;
+ CleanupStack::PushL(gifImageControl);
+
+ gifImageControl->iDelayTimeInCentiseconds = *dataPtr++;
+ gifImageControl->iDelayTimeInCentiseconds |= (*dataPtr++) << 8;
+ if (flags & 0x01)
+ gifImageControl->iTransparentColorIndex = *dataPtr++;
+ else
+ {
+ gifImageControl->iTransparentColorIndex = KErrNotFound;
+ dataPtr++;
+ }
+
+ gifImageControl->iDisposalMethod = TGifImageControl::TDisposalMethod((flags & 0x1c) >> 2);
+ gifImageControl->iUserInputFlag = (flags & 0x02) != 0;
+
+ User::LeaveIfError(iFrameData->AppendFrameData(gifImageControl));
+
+ CleanupStack::Pop(); // gifImageControl
+ iFrameImageControl = gifImageControl;
+ break;
+ }
+
+ if (aDataPtr[0]!=0)
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ aDataPtr++; // Absorb trailing zero
+ }
+
+void CGifReadCodec::DoProcessImageDescriptorL(const TUint8*& aDataPtr,const TUint8* aDataPtrLimit)
+ {
+ if (iFrameImageDesc != NULL)
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ if (aDataPtr + KGifImageInformationSize + KGifInitialCodeLengthSize > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+
+ TPoint topLeftCorner;
+ topLeftCorner.iX = aDataPtr[1] + (aDataPtr[2] << 8);
+ topLeftCorner.iY = aDataPtr[3] + (aDataPtr[4] << 8);
+
+ TSize imageSize;
+ imageSize.iWidth = aDataPtr[5] + (aDataPtr[6] << 8);
+ imageSize.iHeight = aDataPtr[7] + (aDataPtr[8] << 8);
+
+ //Save the first frame's real dimensions, but use the complete
+ //image size for the first frame
+ if(CurrentFrame()==0)
+ {
+ iFirstFrameSize = imageSize;
+ // TPoint (0,0) means that iFrameOffset is set to 0 as all frames are now treated the same for offset data
+ // But real (firstframe) imageSize is saved here as clients use first frames overallsizeinPixels
+ iFirstFrameCoords.SetRect(TPoint(0,0),imageSize);
+ const TSize screenSize(iScreenSize);
+ // there is leniency here for rogue gifs with first frames bigger than global screen size from gif header
+ TSize lenientImageSize = TSize(Max(imageSize.iWidth,screenSize.iWidth),
+ Max(imageSize.iHeight,screenSize.iHeight));
+ iFrameInfo->iOverallSizeInPixels = lenientImageSize;
+ //if the first frame has an offset, put it in the frame info
+ // this will be translated into the iPositionOffset variable
+ iFrameInfo->iFrameCoordsInPixels.SetRect(topLeftCorner,lenientImageSize);
+ }
+ else
+ {
+ iFrameInfo->iOverallSizeInPixels = imageSize;
+ iFrameInfo->iFrameCoordsInPixels.SetRect(topLeftCorner,imageSize);
+ }
+ iFrameInfo->iFrameSizeInPixels = imageSize;
+
+ //adjust iFrameCoordsInPixels for proper behaviour
+ if(iUseFrameSizeInPixels)
+ {
+ iFrameInfo->iFrameCoordsInPixels.SetSize(iFrameInfo->iFrameSizeInPixels);
+ }
+
+ TUint8 flags = aDataPtr[9];
+
+ TGifImageDescriptor* gifImageDesc = new(ELeave) TGifImageDescriptor;
+ CleanupStack::PushL(gifImageDesc);
+
+ gifImageDesc->iLocalColorMap = flags & 0x80;
+ gifImageDesc->iInterlaced = flags & 0x40;
+ gifImageDesc->iSortedLocalMap = flags & 0x20;
+ if (gifImageDesc->iLocalColorMap)
+ {
+ iFrameInfo->iBitsPerPixel = (flags & 0x07) + 1;
+ TInt paletteEntries = 1 << iFrameInfo->iBitsPerPixel;
+ TInt paletteBytes = paletteEntries * KGifPaletteEntrySize;
+ if (aDataPtr + KGifImageInformationSize + paletteBytes + KGifInitialCodeLengthSize > aDataPtrLimit)
+ {
+ User::Leave(KErrUnderflow);
+ }
+
+ aDataPtr += KGifImageInformationSize;
+ const TUint8* paletteLimit = aDataPtr + paletteBytes;
+
+ TGifColorTable* gifColorTable = new(ELeave) TGifColorTable;
+ CleanupStack::PushL(gifColorTable);
+ TRgb* rgbPtr = gifColorTable->iPalette;
+ while (aDataPtr < paletteLimit)
+ {
+ *rgbPtr++ = TRgb(aDataPtr[0],aDataPtr[1],aDataPtr[2]);
+ aDataPtr += 3;
+ }
+ User::LeaveIfError(iFrameData->AppendFrameData(gifColorTable));
+ CleanupStack::Pop(gifColorTable);
+ }
+ else
+ {
+ aDataPtr += KGifImageInformationSize;
+ }
+
+ User::LeaveIfError(iFrameData->AppendFrameData(gifImageDesc));
+ CleanupStack::Pop(gifImageDesc);
+
+ TGifLZWInfo* gifLZWInfo = new(ELeave) TGifLZWInfo;
+ CleanupStack::PushL(gifLZWInfo);
+
+ gifLZWInfo->iInitialCodeLength = aDataPtr[0] + 1;
+ User::LeaveIfError(iFrameData->AppendFrameData(gifLZWInfo));
+ aDataPtr++;
+
+ CleanupStack::Pop(gifLZWInfo);
+ iFrameImageDesc = gifImageDesc;
+ iFrameLZWInfo = gifLZWInfo;
+ }
+
+void CGifReadCodec::DoSaveCommentsL()
+ {
+ //If we have comments add it
+ TInt noOfComments = iComment.Count();
+ //If we have more than KSetCommentsLimit comments just restrict them as KSetCommentsLimit
+ if (noOfComments > KSetCommentsLimit)
+ {
+ noOfComments = KSetCommentsLimit;
+ }
+ for(TInt commentNo=0; commentNo<noOfComments; commentNo++)
+ {
+ TGifComment* comment = new(ELeave) TGifComment;
+ CleanupStack::PushL(comment);
+ comment->iComment = iComment[0];
+ User::LeaveIfError(iFrameData->AppendImageData(comment));
+ CleanupStack::Pop(); // comment
+
+ User::LeaveIfError(iFrameData->AppendImageBuffer(iComment[0]));
+ iComment.Remove(0);
+ }
+ }
+
+void CGifReadCodec::DoProcessDataL()
+ {
+ TInt nextFree = iNextFree;
+ TInt prevCode = iPreviousCode;
+ TBitBuffer bBuf = iBitBuffer;
+ TInt bbSize = iBitBuffSize;
+ TInt curCodeLen = iCurrentCodeLength;
+ iLatestPixSize = iFast64kMode ? sizeof(TUint16) : sizeof (TRgb);
+
+ do
+ {
+ TInt code = NextCode(bBuf, bbSize, curCodeLen);
+ if (code == KErrNotFound)
+ {
+ break; // not enought data in the buffer to get the code
+ }
+
+ if (iTableReset)
+ {
+ iTableReset = EFalse;
+ WriteCodeL(code);
+ prevCode = code;
+ }
+ else if (code == iClearCode)
+ {
+ nextFree = ResetTableL();
+ curCodeLen = iCurrentCodeLength;
+ iTableReset = ETrue;
+ }
+ else if (code == iEoiCode)
+ {
+ iComplete = ETrue;
+ break;
+ }
+ else
+ {
+ if (prevCode >= nextFree)
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ if ( (nextFree == (1 << curCodeLen)-1) && curCodeLen < KGifMaxBits)
+ {
+ ++curCodeLen;
+ }
+
+ if (code < nextFree)
+ {
+ WriteCodeL(code);
+ if (prevCode != -1)
+ {
+ iPrefixIndex[nextFree] = STATIC_CAST(TInt16,prevCode);
+ iSuffixCode[nextFree] = iFirstChar;
+ if (nextFree < KGifConversionTableSize )
+ {
+ nextFree++;
+ }
+ }
+ }
+ else
+ {
+ iPrefixIndex[nextFree] = STATIC_CAST(TInt16,prevCode);
+ iSuffixCode[nextFree] = iFirstChar;
+ WriteCodeL(nextFree);
+ if (nextFree < KGifConversionTableSize )
+ {
+ nextFree++;
+ }
+ }
+
+ if(code < nextFree)
+ {
+ prevCode = code;
+ }
+ else
+ {
+ if(nextFree <= 1)
+ {
+ User::Leave(KErrCorrupt);
+ }
+ prevCode = iPrefixIndex[nextFree-1];
+ }
+
+ }
+ }
+ while (iDataPtr <= iDataPtrLimit);
+
+ ASSERT(iDataPtr <= iDataPtrLimit);
+
+ SetCurrentCodeLengthL(curCodeLen);
+ iBitBuffer = bBuf;
+ iBitBuffSize = bbSize;
+ iNextFree = nextFree;
+ iPreviousCode = prevCode;
+ }
+
+inline
+TInt CGifReadCodec::ResetTableL()
+ {
+ const TInt nFree = iEoiCode + 1;
+
+ SetCurrentCodeLengthL(iGifLZWInfo->iInitialCodeLength);
+ return nFree;
+ }
+
+#if defined(__ARMCC__)
+__forceinline
+#else
+inline
+#endif
+TInt CGifReadCodec::NextCode(TBitBuffer& aBitBuffer, TInt& aBitBufSize,const TInt aCurCodeLen )
+ {
+
+ if (aCurCodeLen > aBitBufSize)
+ {
+ const TUint8* dataPtr = iDataPtr;
+ ASSERT( dataPtr <= iDataPtrLimit );
+
+ TInt bytesToRead = (sizeof(TBitBuffer) * 8 - aBitBufSize) >> 3;
+ if (dataPtr + bytesToRead >= iDataPtrLimit)
+ {
+ bytesToRead = iDataPtrLimit - dataPtr;
+ if (bytesToRead == 0)
+ {
+ return KErrNotFound;
+ }
+ }
+
+ do
+ {
+ aBitBuffer |= *dataPtr++ << aBitBufSize;
+ aBitBufSize +=8;
+ }
+ while (--bytesToRead > 0);
+
+ iDataPtr = dataPtr;
+ if (aBitBufSize < aCurCodeLen)
+ {
+ return KErrNotFound;
+ }
+
+ }
+ TInt code = aBitBuffer;
+ aBitBuffer >>= aCurCodeLen;
+ code &= (1 << aCurCodeLen) - 1;
+ aBitBufSize -= aCurCodeLen;
+
+ return code;
+ }
+
+template <class TPalType, TInt aPtrDelta>
+inline
+TUint8* CGifReadCodec::WriteGifBuffer(TUint8* aOutputStringPtr, TUint8* aOutputStringLimit)
+ {
+ TInt bufUsed = iPixBufCount;
+ TInt bufFree = iPixBufferSize - bufUsed;
+
+ if ((aOutputStringLimit-aOutputStringPtr) > bufFree)
+ {
+ aOutputStringLimit = aOutputStringPtr+bufFree;
+ }
+
+ register TPalType* pixelBufferPtr = reinterpret_cast<TPalType*>( iPixelBuffer ) + bufUsed;
+ const TPalType* tmp = pixelBufferPtr;
+ const TPalType* palette;
+
+ if (aPtrDelta == 4)
+ {
+ palette = reinterpret_cast<const TPalType*>( iPalette );
+ }
+ else
+ {
+ palette = reinterpret_cast<const TPalType*>( i64KPalette );
+ }
+
+ if (iMaskProc)
+ {
+ register TUint32* maskPixelBufferPtr = iMaskBuffer + bufUsed;
+ const TUint16 KTranspIdx= iTranspColIdx;
+ const TUint32 KTranspMask = 0;
+ const TUint32 KOpaqueMask = iOpaqueMask;
+ const TPalType bgCol = palette[ KTranspIdx ];
+
+ while (aOutputStringPtr < aOutputStringLimit)
+ {
+ const TUint8 code = *aOutputStringPtr;
+ if (code == KTranspIdx)
+ {
+ *pixelBufferPtr = bgCol;
+ *maskPixelBufferPtr = KTranspMask;
+ }
+ else
+ {
+ *pixelBufferPtr = palette[code];
+ *maskPixelBufferPtr = KOpaqueMask;
+ }
+ maskPixelBufferPtr++;
+ pixelBufferPtr++;
+ aOutputStringPtr++;
+ }
+ }
+ else
+ {
+
+ while (aOutputStringPtr < aOutputStringLimit)
+ {
+ *pixelBufferPtr = palette[*aOutputStringPtr];
+ pixelBufferPtr++;
+ aOutputStringPtr++;
+ }
+ }
+
+ iPixBufCount += pixelBufferPtr-tmp;
+ return aOutputStringPtr;
+ }
+
+/**
+ That shall only leave with KErrCorrupt
+ which means that image decoding is not possible
+*/
+void CGifReadCodec::WriteCodeL(TInt aCode)
+ {
+ TUint8* outputStringLimit = iOutputString + KGifConversionTableSize;
+ register TUint8* outputStringPtr = outputStringLimit;
+ register TUint16 latestChar=0;
+
+ while (aCode >= 0)
+ {
+ ASSERT((aCode >= 0) && (aCode <= KGifConversionTableSize));
+
+ outputStringPtr--;
+ latestChar = iSuffixCode[aCode];
+ outputStringPtr[0] = latestChar;
+ aCode = iPrefixIndex[aCode];
+ }
+
+ iFirstChar = latestChar;
+ if (aCode != KErrNotFound)
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ while (true)
+ {
+ if (iFast64kMode)
+ {
+ outputStringPtr = WriteGifBuffer<TUint16, sizeof(TUint16)>(outputStringPtr, outputStringLimit);
+ }
+ else
+ {
+ outputStringPtr = WriteGifBuffer<TRgb, sizeof(TRgb)>(outputStringPtr, outputStringLimit);
+ }
+
+ if (outputStringPtr != outputStringLimit)
+ {
+ FlushPixBuffer(iLatestPixSize);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+inline
+void CGifReadCodec::FlushPixBuffer(TInt aPixSize)
+ {
+ if (iPixBufCount)
+ {
+ if (iMaskProc)
+ {
+ WriteStringWithTransparency(reinterpret_cast<TUint8*>(iPixelBuffer), aPixSize, iMaskBuffer, iPixBufCount);
+ }
+ else
+ {
+ WriteStringWithoutTransparency(reinterpret_cast<TUint8*>(iPixelBuffer), aPixSize, iPixBufCount);
+ }
+
+ iPixBufCount = 0;
+ }
+ }
+
+void CGifReadCodec::WriteStringWithoutTransparency(TUint8* aOutputString, TInt aPixSize, TInt aNumOfPixels)
+ {
+ CImageProcessor*const imageProc = iImgProc;
+
+ if (iFastAccessMode)
+ {
+ imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), aNumOfPixels);
+ iPixRead += aNumOfPixels;
+ aNumOfPixels = 0;
+ return;
+ }
+
+ const TInt KImgXLimit = iFrameSize.iWidth + iFrameOffset.iX;
+
+ while ( aNumOfPixels )
+ {
+ TInt rowPixels = Min(KImgXLimit - iPPos->iX, aNumOfPixels);
+
+ imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), rowPixels);
+ aNumOfPixels -= rowPixels;
+ aOutputString += rowPixels * aPixSize;
+
+ iPPos->iX += rowPixels;
+ if (iPPos->iX == KImgXLimit)
+ {
+ imageProc->FlushPixels();
+ UpdateYPos();
+ imageProc->SetPos( TPoint(iPPos->iX+iPositionOffset.iX, iPPos->iY+iPositionOffset.iY) );
+ }
+ }
+ }
+
+void CGifReadCodec::WriteStringWithTransparency(TUint8* aOutputString, TInt aPixSize, TUint32* aMaskString, TInt aNumOfPixels)
+ {
+ CImageProcessor*const imageProc = iImgProc;
+ CImageProcessor*const maskProc = iMaskProc;
+
+ if (iFastAccessMode)
+ {
+ imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), aNumOfPixels);
+ if(maskProc) // For non-single 16MA bitmaps
+ {
+ maskProc->SetMonoPixels(reinterpret_cast<TUint32*>(aMaskString), aNumOfPixels);
+ }
+
+ iPixRead += aNumOfPixels;
+ aNumOfPixels = 0;
+ return;
+ }
+
+ const TInt KImgXLimit = iFrameSize.iWidth + iFrameOffset.iX;
+
+ while ( aNumOfPixels )
+ {
+ TInt rowPixels = Min(KImgXLimit - iPPos->iX, aNumOfPixels);
+
+ imageProc->SetPixels(reinterpret_cast<TRgb*>(aOutputString), rowPixels);
+ maskProc->SetMonoPixels(reinterpret_cast<TUint32*>(aMaskString), rowPixels);
+
+ iPPos->iX += rowPixels;
+ aOutputString += rowPixels * aPixSize;
+ aMaskString += rowPixels;
+ aNumOfPixels -= rowPixels;
+
+ if (iPPos->iX == KImgXLimit)
+ {
+ imageProc->FlushPixels();
+ UpdateYPos();
+ const TPoint newPos(iPPos->iX+iPositionOffset.iX, iPPos->iY+iPositionOffset.iY);
+ imageProc->SetPos(newPos);
+ maskProc->SetPos(newPos);
+ }
+ }
+ }
+
+inline
+void CGifReadCodec::SetCurrentCodeLengthL(TInt aCodeLength)
+ {
+ if ((aCodeLength > KGifMaxBits) || (aCodeLength < 0))
+ {
+ User::Leave(KErrCorrupt);
+ }
+ iCurrentCodeLength = aCodeLength;
+ }
+
+inline
+void CGifReadCodec::UpdateYPos()
+ {
+ iPPos->iX = iFrameOffset.iX;
+
+ if (!iGifImageDesc->iInterlaced)
+ {
+ iPPos->iY++;
+ return;
+ }
+
+ UpdateYPosInterlaced();
+ }
+
+void CGifReadCodec::UpdateYPosInterlaced()
+ {
+ iPPos->iY += iYPosIncrement;
+
+ CImageProcessor*const imageProc = ImageProcessor();
+ while (iPPos->iY >= iFrameSize.iHeight + iFrameOffset.iY)
+ {
+ iPass++;
+ TInt lineRepeat;
+ if (iPass == 2)
+ {
+ iPPos->iY = iFrameOffset.iY + KPass2StartLine;
+ iYPosIncrement = KPass2YPosIncrement;
+ lineRepeat = KPass2LineRepeat;
+ }
+ else if (iPass == 3)
+ {
+ iPPos->iY = iFrameOffset.iY + KPass3StartLine;
+ iYPosIncrement = KPass3YPosIncrement;
+ lineRepeat = KPass3LineRepeat;
+ }
+ else if (iPass == 4)
+ {
+ iPPos->iY = iFrameOffset.iY + KPass4StartLine;
+ iYPosIncrement = KPass4YPosIncrement;
+ lineRepeat = KPass4LineRepeat;
+ }
+ else
+ break;
+ imageProc->SetYPosIncrement(iYPosIncrement);
+ if(!iGifImageControl || (iGifImageControl->iTransparentColorIndex==KErrNotFound))
+ {
+ imageProc->SetLineRepeat(lineRepeat);
+ }
+ }
+ }
+
+// CGifWriteCodec
+CGifWriteCodec* CGifWriteCodec::NewL(const CGifEncoder& aEncoder)
+ {
+ CGifWriteCodec* self = new(ELeave) CGifWriteCodec(aEncoder);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CGifWriteCodec::CGifWriteCodec(const CGifEncoder& aEncoder)
+ :iEncoder(aEncoder)
+ {
+ }
+
+TFrameState CGifWriteCodec::ProcessFrameL(TBufPtr8& aDst)
+ {
+ iDestStartPtr = CONST_CAST(TUint8*,aDst.Ptr());
+ iDestPtr = iDestStartPtr;
+ iDestPtrLimit = iDestPtr + aDst.MaxLength();
+
+ FillBufferL(*Source());
+
+ if (iImageComplete)
+ {
+ while (iBufferSize > 0)
+ {
+ if (!WriteBuffer())
+ {
+ aDst.SetLength(iDestPtr - iDestStartPtr);
+ return EFrameIncomplete;
+ }
+ }
+ TBool terminatorWritten = WriteTerminator();
+ aDst.SetLength(iDestPtr - iDestStartPtr);
+
+ return (terminatorWritten) ? EFrameComplete : EFrameIncomplete;
+ }
+ else
+ {
+ WriteBuffer();
+ }
+
+ aDst.SetLength(iDestPtr - iDestStartPtr);
+ return EFrameIncomplete;
+ }
+
+void CGifWriteCodec::FillBufferL(const CFbsBitmap& aFrame)
+ {
+ TUint pixelsScanned =0;
+ TBool pixelWritten = EFalse;
+
+ const CPalette* palette = iEncoder.Palette();
+ while (iPos.iY < iSourceRect.iBr.iY)
+ {
+ TRgb pixelColor;
+ aFrame.GetPixel(pixelColor,iPos);
+ iPos.iX++;
+ if (iPos.iX == iSourceRect.iBr.iX)
+ {
+ iPos.iX = iSourceRect.iTl.iX;
+ iPos.iY++;
+ }
+ TUint8 nextColorIndex;
+ if (palette)
+ {
+ nextColorIndex = palette->NearestIndex(pixelColor);
+ }
+ else
+ {
+ nextColorIndex = STATIC_CAST(TUint8, pixelColor.Color256());
+ }
+
+ TUint32 hashValue=0;
+ TInt index=-1;
+ if (!TableEntry(iWaitingCode,nextColorIndex,index,hashValue))
+ {
+ if(index!=KErrNotFound)
+ {
+ pixelWritten = ETrue;
+ WriteData(iWaitingCode);
+ AddToTableL(index,hashValue);
+
+ if (iNextFree > iNextLimit)
+ {
+ iCodeLength++;
+ if (iCodeLength > KGifMaxBits)
+ {
+ iCodeLength=KGifMaxBits;
+ ResetTable();
+ }
+ iNextLimit = 1 << iCodeLength;
+ }
+ }
+ iWaitingCode = nextColorIndex;
+ }
+ else
+ {
+ iWaitingCode = iHashCode[index];
+ }
+
+ if ((iBufferFull) || ((pixelsScanned++ > KLZWLimit) && pixelWritten !=EFalse))
+ {
+ return;
+ }
+
+ }
+
+
+ if (iPos.iY == iSourceRect.iBr.iY)
+ {
+ if (iWaitingCode != KErrNotFound)
+ {
+ WriteData(iWaitingCode);
+ }
+ WriteData(iClearCode + 1); // End-Of-Information
+ if (iBitOffset > 0)
+ {
+ iBufferSize++;
+ iBitOffset = 0;
+ }
+ iCodeLength = 8;
+ iBufferPtr++;
+ iImageComplete = ETrue;
+ }
+ }
+
+void CGifWriteCodec::InitFrameL(TBufPtr8& aDst, const CFbsBitmap& aSource)
+ {
+ SetSource(&aSource);
+
+ iDestStartPtr = CONST_CAST(TUint8*,aDst.Ptr());
+ iDestPtr = iDestStartPtr;
+ iDestPtrLimit = iDestPtr + aDst.MaxLength();
+
+ iSourceRect = TRect(aSource.SizeInPixels());
+ iPos.SetXY(0,0);
+
+ iBitsPerPixel = 8;
+
+ iBufferPtr = iBuffer;
+ iBufferSize = 0;
+
+ iClearCode = 1 << iBitsPerPixel;
+ iCodeLength = iBitsPerPixel + 1;
+ iWaitingCode = -1;
+
+ ResetTable();
+
+ *iDestPtr++ = KGifImageDescriptorId; // Image Separator Header
+ *iDestPtr++ = 0; // Coordinate Left Border
+ *iDestPtr++ = 0; // Coordinate Left Border
+ *iDestPtr++ = 0; // Coordinate Top Border
+ *iDestPtr++ = 0; // Coordinate Top Border
+ *iDestPtr++ = TUint8(iSourceRect.Width());
+ *iDestPtr++ = TUint8(iSourceRect.Width() >> 8);
+ *iDestPtr++ = TUint8(iSourceRect.Height());
+ *iDestPtr++ = TUint8(iSourceRect.Height() >> 8);
+ *iDestPtr++ = 0; // Flags
+
+ *iDestPtr++ = TUint8(iCodeLength - 1); // Initial Code Length - 1
+
+ aDst.SetLength(iDestPtr - iDestStartPtr);
+ }
+
+void CGifWriteCodec::AddToTableL(TInt aIndex, TUint32 aHashValue)
+ {
+ ASSERT(aIndex<KGifHashTableSize);
+ ASSERT(iNextFree<=KGifConversionTableSize);
+ iHashCode[aIndex] = static_cast<TInt16>(iNextFree++);
+ iHashValue[aIndex] = aHashValue;
+ }
+
+// The hash algoritm use open addressing double hashing (no chaining)
+// on the prefix code / suffix character combination. A variant of Knuth's
+// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+// secondary probe.
+TBool CGifWriteCodec::TableEntry(TInt16 aWaitingCode, TUint8 aNewCode, TInt& aIndex, TUint32& aHashValue)
+ {
+ TInt offset;
+
+ ASSERT(aWaitingCode>=KErrNotFound && aWaitingCode<KGifConversionTableSize);
+
+ aIndex = KErrNotFound;
+
+ //Very first code
+ if(aWaitingCode==KErrNotFound)
+ return EFalse;
+
+ aIndex = static_cast<TInt>(aNewCode<<(KGifMaxBits-8)) + aWaitingCode;
+ if(aIndex>=KGifHashTableSize)
+ aIndex -= KGifHashTableSize;
+ ASSERT(aIndex < KGifHashTableSize);
+
+ //hash function
+ aHashValue = static_cast<TUint32>(aWaitingCode<<8)|aNewCode;
+
+ if(iHashCode[aIndex]==KErrNotFound) //slot is empty
+ return EFalse;
+
+ //slot has an entry
+ if(iHashValue[aIndex]==aHashValue) //symbol already in table
+ return ETrue;
+
+ if(aIndex==0) // secondary hash (after G. Knott)
+ offset = 1;
+ else
+ offset = KGifHashTableSize - aIndex;
+
+ FOREVER
+ {
+ aIndex -= offset;
+ if(aIndex<0)
+ aIndex += KGifHashTableSize;
+
+ if(iHashCode[aIndex]==KErrNotFound)
+ return EFalse; //slot is empty
+
+ if(iHashValue[aIndex]==aHashValue) //symbol already in table
+ return ETrue;
+ }
+ }
+
+void CGifWriteCodec::ResetTable()
+ {
+ WriteData(iClearCode); // Send reset code
+
+ iNextFree = iClearCode + 2;
+ iCodeLength = iBitsPerPixel + 1;
+ iNextLimit = 1 << iCodeLength;
+
+ for (TInt index = 0; index < KGifHashTableSize; index++)
+ iHashCode[index] = KErrNotFound;
+ }
+
+void CGifWriteCodec::WriteData(TInt aIndex)
+ {
+ aIndex <<= iBitOffset;
+ iBufferPtr[0] |= TUint8(aIndex);
+ iBufferPtr[1] |= TUint8(aIndex >> 8);
+ iBufferPtr[2] |= TUint8(aIndex >> 16);
+
+ TInt newBitOffset = iBitOffset + iCodeLength;
+ iBitOffset = newBitOffset & 7;
+ TInt byteOffset = newBitOffset >> 3;
+ iBufferPtr += byteOffset;
+ iBufferSize += byteOffset;
+
+ if (iBufferSize >= KGifBlockSize)
+ {
+ ASSERT(iBufferSize <= KGifBufferSize);
+ iBufferFull = ETrue;
+ }
+ }
+
+TBool CGifWriteCodec::WriteBuffer()
+ {
+ ASSERT(iBufferSize <= KGifBufferSize);
+ if (iBufferSize == 0)
+ return ETrue;
+
+ TInt blockSize = Min(KGifBlockSize,iBufferSize);
+ if (iDestPtrLimit - iDestPtr < blockSize + 1)
+ return EFalse;
+
+ *iDestPtr++ = STATIC_CAST(TUint8,blockSize);
+ Mem::Copy(iDestPtr,iBuffer,blockSize);
+ iDestPtr += blockSize;
+ iBufferFull = EFalse;
+
+ iBufferSize -= blockSize;
+ iBufferPtr = iBuffer + iBufferSize;
+
+ TInt bitBufferSize = iBufferSize;
+ if (iBitOffset)
+ bitBufferSize++;
+ Mem::Copy(iBuffer,&iBuffer[blockSize],bitBufferSize);
+ Mem::FillZ(iBuffer + bitBufferSize,KGifBufferSize - bitBufferSize);
+
+ return ETrue;
+ }
+
+TBool CGifWriteCodec::WriteTerminator()
+ {
+ if (iDestPtrLimit - iDestPtr < 2)
+ return EFalse;
+
+ *iDestPtr++ = TUint8(0);
+ *iDestPtr++ = TUint8(KGifTerminatorId);
+ return ETrue;
+ }