diff -r 000000000000 -r 40261b775718 mmplugins/imagingplugins/codecs/GifCodec/GIFcodec.cpp --- /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 +#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)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(&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 ; frameDataIndexGetFrameData(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(-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( 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(&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; commentNoiComment = 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 +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( iPixelBuffer ) + bufUsed; + const TPalType* tmp = pixelBufferPtr; + const TPalType* palette; + + if (aPtrDelta == 4) + { + palette = reinterpret_cast( iPalette ); + } + else + { + palette = reinterpret_cast( 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(outputStringPtr, outputStringLimit); + } + else + { + outputStringPtr = WriteGifBuffer(outputStringPtr, outputStringLimit); + } + + if (outputStringPtr != outputStringLimit) + { + FlushPixBuffer(iLatestPixSize); + } + else + { + break; + } + } + } + +inline +void CGifReadCodec::FlushPixBuffer(TInt aPixSize) + { + if (iPixBufCount) + { + if (iMaskProc) + { + WriteStringWithTransparency(reinterpret_cast(iPixelBuffer), aPixSize, iMaskBuffer, iPixBufCount); + } + else + { + WriteStringWithoutTransparency(reinterpret_cast(iPixelBuffer), aPixSize, iPixBufCount); + } + + iPixBufCount = 0; + } + } + +void CGifReadCodec::WriteStringWithoutTransparency(TUint8* aOutputString, TInt aPixSize, TInt aNumOfPixels) + { + CImageProcessor*const imageProc = iImgProc; + + if (iFastAccessMode) + { + imageProc->SetPixels(reinterpret_cast(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(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(aOutputString), aNumOfPixels); + if(maskProc) // For non-single 16MA bitmaps + { + maskProc->SetMonoPixels(reinterpret_cast(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(aOutputString), rowPixels); + maskProc->SetMonoPixels(reinterpret_cast(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(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(aNewCode<<(KGifMaxBits-8)) + aWaitingCode; + if(aIndex>=KGifHashTableSize) + aIndex -= KGifHashTableSize; + ASSERT(aIndex < KGifHashTableSize); + + //hash function + aHashValue = static_cast(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; + }