imagehandlinglib/Src/CIHLFileImage.cpp
changeset 0 2014ca87e772
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imagehandlinglib/Src/CIHLFileImage.cpp	Tue Jan 26 15:18:05 2010 +0200
@@ -0,0 +1,992 @@
+/*
+* Copyright (c) 2004 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:  Implementation of Image class.
+*
+*/
+
+
+// INCLUDE FILES
+#include "CIHLFileImage.h"
+
+#include "CIHLBitmap.h"
+#include "IHLImplementationIds.h"
+#include "IHLDebugPrint.h" // Debug print
+#include <IHLInterfaceIds.h>
+
+// Private namespace for constants and functions
+namespace
+	{
+	// Fixed scale factors
+	enum TScaleFactors
+		{
+		EFull		= 1,
+		EHalf		= 2,
+		EQuarter	= 4,
+		EEighth		= 8,
+		};
+
+	// Panic function
+    _LIT( KIHLPanicString, "IHLImage" );
+    void Panic( TInt aPanicCode ) { User::Panic( KIHLPanicString, aPanicCode ); }
+	}
+
+// ============================ MEMBER FUNCTIONS ===============================
+// -----------------------------------------------------------------------------
+//
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+CIHLFileImage::CIHLFileImage( TInt aImageIndex )
+:CActive( CActive::EPriorityStandard ),
+iImageIndex( aImageIndex )
+    {
+	CActiveScheduler::Add( this );
+    }
+
+// -----------------------------------------------------------------------------
+//
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+CIHLFileImage* CIHLFileImage::NewL( RFile& aFile, TInt aImageIndex, const TUint32 aOptions )
+	{
+    CIHLFileImage* self = new (ELeave) CIHLFileImage( aImageIndex );
+	CleanupStack::PushL( self );
+	self->ConstructL( aFile, aOptions );
+	CleanupStack::Pop(); // self
+    return self;
+	}
+
+CIHLFileImage* CIHLFileImage::NewL( RFs& aFs, const TDesC8& aDataBuf,
+                                    TInt aImageIndex, const TUint32 aOptions )
+    {
+    CIHLFileImage* self = new (ELeave) CIHLFileImage( aImageIndex );
+	CleanupStack::PushL( self );
+	self->ConstructL( aFs, aDataBuf, aOptions );
+	CleanupStack::Pop(); // self
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+//
+// Symbian constructor can leave.
+// -----------------------------------------------------------------------------
+void CIHLFileImage::ConstructL( RFile& aFile, const TUint32 aOptions )
+	{
+    TInt decoderOptions( CImageDecoder::EOptionNoDither | CImageDecoder::EOptionUseFrameSizeInPixels );
+
+	// Open decoder
+    IHL_DEBUG1( KIHLDebug1, "IHL - CIHLFileImage - Start create ICL image decoder" );
+	if( aOptions & MIHLFileImage::EOptionNoDRMConsume )
+	    {
+	    iDecoder = CImageDecoder::FileNewL( aFile, ContentAccess::EPeek,
+	                             (CImageDecoder::TOptions)decoderOptions );
+	    }
+	else
+	    {
+	    iDecoder = CImageDecoder::FileNewL( aFile, ContentAccess::EView,
+	                             (CImageDecoder::TOptions)decoderOptions );
+	    }
+	ConstructCommonL();
+    IHL_DEBUG1( KIHLDebug2, "IHL - CIHLFileImage - ICL image decoder ready!" );
+	}
+
+void CIHLFileImage::ConstructL( RFs& aFs, const TDesC8& aDataBuf, const TUint32 /*aOptions*/ )
+    {
+    TInt decoderOptions( CImageDecoder::EOptionNoDither | CImageDecoder::EOptionUseFrameSizeInPixels );
+
+    IHL_DEBUG1( KIHLDebug1, "IHL - CIHLFileImage - Start create buffered ICL image decoder" );
+
+    iDecoder = CImageDecoder::DataNewL( aFs, aDataBuf, (CImageDecoder::TOptions)decoderOptions );
+	ConstructCommonL();
+
+    IHL_DEBUG1( KIHLDebug2, "IHL - CIHLFileImage - Buffered ICL image decoder ready!" );
+    }
+
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ConstructCommonL
+// -----------------------------------------------------------------------------
+
+void CIHLFileImage::ConstructCommonL()
+    {
+	// Check frame count and image index
+	iImageCount = iDecoder->FrameCount();
+	__ASSERT_ALWAYS( iImageCount > 0, User::Leave( KErrCorrupt ) );
+
+	// Get image types
+	iDecoder->ImageType( iImageIndex, iImageType, iImageSubType );
+
+	if( KImageTypeGIFUid == iImageType )
+		{
+		iGif = ETrue;
+		if( iImageCount > 1 )
+		    {
+    		iAnimation = ETrue;
+    		iAnimationFrameCount = iImageCount;
+    		iImageCount = 1; // Handled as one animated image
+		    }
+		}
+	__ASSERT_ALWAYS( iImageIndex >= 0 && iImageIndex < iImageCount, User::Leave( KErrArgument ) );
+
+	// cache frame info and set scale sizes
+	iFrameInfo = iDecoder->FrameInfo( iImageIndex );
+	if( !iAnimation )
+		{
+        // Animation must always be loaded 1:1
+		if( !iGif &&
+		    iFrameInfo.iFlags & TFrameInfo::EFullyScaleable )
+			{
+			// Gif cannot be fully scaleable
+			iFullyScaleable = ETrue;
+			}
+		else
+			{
+			TSize size( iFrameInfo.iOverallSizeInPixels );
+			iLoadSizeArray.AppendL( ScaledLoadSize( size, EEighth ) );
+			iLoadSizeArray.AppendL( ScaledLoadSize( size, EQuarter ) );
+			iLoadSizeArray.AppendL( ScaledLoadSize( size, EHalf ) );
+			}
+		}
+    }
+
+// -----------------------------------------------------------------------------
+// Destructor
+// -----------------------------------------------------------------------------
+CIHLFileImage::~CIHLFileImage()
+    {
+	Cancel();
+	delete iPrevAnimationFrame;
+	delete iSubFrameBitmap;
+	delete iDecoder;
+	iLoadSizeArray.Reset();
+    }
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::Type
+// -----------------------------------------------------------------------------
+TIHLInterfaceType CIHLFileImage::Type() const
+	{
+	return TIHLInterfaceType( KIHLInterfaceIdFileImage,
+							  KIHLImplementationIdFileImage );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ImageType
+// -----------------------------------------------------------------------------
+const TUid& CIHLFileImage::ImageType() const
+	{
+	return iImageType;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ImageSubType
+// -----------------------------------------------------------------------------
+const TUid& CIHLFileImage::ImageSubType() const
+	{
+	return iImageSubType;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ImageIndex
+// -----------------------------------------------------------------------------
+TInt CIHLFileImage::ImageIndex() const
+	{
+	return iImageIndex;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ImageCount
+// -----------------------------------------------------------------------------
+TInt CIHLFileImage::ImageCount() const
+	{
+	return iImageCount;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::Size
+// -----------------------------------------------------------------------------
+TSize CIHLFileImage::Size() const
+	{
+	return iFrameInfo.iOverallSizeInPixels;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::DisplayMode
+// -----------------------------------------------------------------------------
+TDisplayMode CIHLFileImage::DisplayMode() const
+	{
+	if( iGif )
+		{
+		// We cannot trust iFrameDisplayMode for GIF images. It always return EColor256.
+		// This is error because palette sure holds only 256 colors but these colors can
+		// be still any RGB values and so for cannot be directly put to 8 bit bitmap (EColor256).
+		// To decrypt image correctly and without color dithering, we must use 24 bit (EColor16M)
+		// destination bitmap. Note that CFbsBitmap has palette methods but they are
+		// not supported currently.
+		// Return maximum color mode to ensure best image quality.
+		return EColor16MU;
+		}
+	else if( iFrameInfo.iFrameDisplayMode < EColor16MU ||
+	         iFrameInfo.iFrameDisplayMode == EColor4K )
+		{
+		return EColor64K;
+		}
+	return EColor16MU;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::MaskDisplayMode
+// -----------------------------------------------------------------------------
+TDisplayMode CIHLFileImage::MaskDisplayMode() const
+	{
+	if( iFrameInfo.iFlags & TFrameInfo::ETransparencyPossible )
+		{
+		if( iFrameInfo.iFlags & TFrameInfo::EAlphaChannel )
+			{
+			return EGray256;
+			}
+		return EGray2;
+		}
+	return ENone;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::BackgroundColor
+// -----------------------------------------------------------------------------
+TRgb CIHLFileImage::BackgroundColor() const
+	{
+	return iFrameInfo.iBackgroundColor;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::CustomLoadSizeArray
+// -----------------------------------------------------------------------------
+const RArray<TSize>& CIHLFileImage::CustomLoadSizeArray() const
+	{
+	return iLoadSizeArray;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::CustomLoadSizeArray
+// -----------------------------------------------------------------------------
+TBool CIHLFileImage::IsFullyScaleable() const
+	{
+	return iFullyScaleable;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::IsAnimation
+// -----------------------------------------------------------------------------
+TBool CIHLFileImage::IsAnimation() const
+	{
+	return iAnimation;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::AnimationFrameCount
+// -----------------------------------------------------------------------------
+TInt CIHLFileImage::AnimationFrameCount() const
+	{
+	return iAnimationFrameCount;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::AnimationFrameDelay
+// -----------------------------------------------------------------------------
+TTimeIntervalMicroSeconds32 CIHLFileImage::AnimationFrameDelay( TInt aAnimationFrameIndex ) const
+	{
+	__ASSERT_ALWAYS( aAnimationFrameIndex >= 0 &&
+		aAnimationFrameIndex < iAnimationFrameCount, Panic( KErrArgument ) );
+
+	return I64INT( iDecoder->FrameInfo( aAnimationFrameIndex ).iDelay.Int64() );
+	}
+
+// ------------------------------------------------------------------------------
+// CIHLFileImage::Load
+// ------------------------------------------------------------------------------
+
+TInt CIHLFileImage::Load( TRequestStatus& aStatus, MIHLBitmap& aDestination, TInt aFrameIndex )
+	{
+	iImageIndex = aFrameIndex;	
+	return LoadRequest( aStatus, aDestination, iImageIndex );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::LoadAnimation
+// -----------------------------------------------------------------------------
+TInt CIHLFileImage::LoadAnimation( TRequestStatus& aStatus, MIHLBitmap& aDestination,
+							  TInt aAnimationFrameIndex )
+	{
+	__ASSERT_ALWAYS( aAnimationFrameIndex >= 0 &&
+		aAnimationFrameIndex < iAnimationFrameCount, Panic( KErrArgument ) );
+
+	return LoadRequest( aStatus, aDestination, aAnimationFrameIndex );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::IsBusy
+// -----------------------------------------------------------------------------
+TBool CIHLFileImage::IsBusy() const
+	{
+	return ( iImageState != EInactive );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::CancelLoad
+// -----------------------------------------------------------------------------
+void CIHLFileImage::CancelLoad()
+	{
+	Cancel();
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::SetFilter
+// -----------------------------------------------------------------------------
+void CIHLFileImage::SetFilter( MIHLFilter* /*aFilter*/ )
+	{
+	// Not in use
+	}
+
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::Decoder
+// -----------------------------------------------------------------------------
+const CImageDecoder& CIHLFileImage::Decoder() const
+	{
+	return *iDecoder;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::DoCancel
+// -----------------------------------------------------------------------------
+void CIHLFileImage::DoCancel()
+	{
+	iDecoder->Cancel();
+
+	// Delete all processed bitmaps
+	ErrorCleanup();
+
+	// Complete with cancel
+	iImageState = EInactive;
+	RequestComplete( KErrCancel );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::RunL
+// -----------------------------------------------------------------------------
+void CIHLFileImage::RunL()
+	{
+	__ASSERT_DEBUG( iDestination, Panic( KErrGeneral ) );
+	User::LeaveIfError( iStatus.Int() );
+
+	switch( iImageState )
+		{
+		case EStartLoad:
+			{
+			// start loading the bitmaps
+			StartLoadL();
+			break;
+			}
+		case ECompleteLoad:
+			{
+			// complete loading the bitmaps
+			CompleteLoadL();
+			break;
+			}
+		default:
+			{
+			Panic( KErrTotalLossOfPrecision );
+			}
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::RunError
+// -----------------------------------------------------------------------------
+TInt CIHLFileImage::RunError( TInt aError )
+	{
+    IHL_DEBUG2( KIHLDebug, "IHL - CIHLFileImage - Loading error: %d", aError );
+
+	// Delete all processed bitmaps
+	ErrorCleanup();
+
+	// Complete with error
+	iImageState = EInactive;
+	RequestComplete( aError );
+	return KErrNone;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::LoadRequest
+// -----------------------------------------------------------------------------
+TInt CIHLFileImage::LoadRequest( TRequestStatus& aStatus,
+                                 MIHLBitmap& aDestination,
+                                 TInt aFrameIndex )
+	{
+	if( IsBusy() )
+		{
+		return KErrNotReady;
+		}
+
+	if( aFrameIndex < 0 || aFrameIndex >= iDecoder->FrameCount() )
+		{
+		return KErrArgument;
+		}
+
+	const CFbsBitmap& dstBitmap = aDestination.Bitmap();
+	if( !dstBitmap.Handle() )
+		{
+		return KErrArgument;
+		}
+
+	TSize dstSize( dstBitmap.SizeInPixels() );
+	if( dstSize != Size() &&
+		!iFullyScaleable )
+		{
+		TBool sizeFound( EFalse );
+		const TInt count( iLoadSizeArray.Count() );
+		for( TInt i( 0 ); i < count; ++i )
+			{
+			if( dstSize == iLoadSizeArray[ i ] )
+				{
+				sizeFound = ETrue;
+				}
+			}
+		if( !sizeFound )
+			{
+			return KErrArgument;
+			}
+		}
+
+    IHL_DEBUG1( KIHLDebug, "IHL - CIHLFileImage - Frame loading requested" );
+
+	iImageStatus = &aStatus;
+	*iImageStatus = KRequestPending;
+
+	iDestination = static_cast<CIHLBitmap*>( &aDestination ); //lint !e826
+	iFrameIndex = aFrameIndex;
+
+	// Start the active object
+	iImageState = EStartLoad;
+	SelfComplete();
+	return KErrNone;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::StartLoadL
+// -----------------------------------------------------------------------------
+void CIHLFileImage::StartLoadL()
+	{
+	__ASSERT_DEBUG( !iSubFrameBitmap, Panic( KErrGeneral ) );
+
+    IHL_DEBUG1( KIHLDebug, "IHL - CIHLFileImage - Start ICL convert" );
+
+	if( iAnimation )
+		{
+		// Start animation from first frame by default
+		iSubFrameIndex = 0;
+
+		// Check is animation can be continued on top of destination bitmap
+		if( iDestination->IsCreated() &&
+			iDestination->EditorPtr() == this &&
+			iDestination->EditorValue() < iFrameIndex )
+			{
+			// Editor value means frame index
+			iSubFrameIndex = iDestination->EditorValue() + 1;
+			}
+
+		StartLoadSubFrameL( iSubFrameIndex, ETrue );
+		}
+    else if( iGif )
+        {
+		StartLoadSubFrameL( iFrameIndex, EFalse );
+        }
+	else
+		{
+        // Frame fills the whole image -> normal load
+	    StartLoadNormalFrame( iFrameIndex );
+		}
+
+	iImageState = ECompleteLoad;
+	SetActive();
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::StartLoadNormalFrame
+// -----------------------------------------------------------------------------
+void CIHLFileImage::StartLoadNormalFrame( TInt aFrameIndex )
+	{
+	CFbsBitmap& dstBitmap = iDestination->BitmapModifyable();
+	CFbsBitmap& dstMask = iDestination->MaskModifyable();
+
+	if( MaskDisplayMode() && dstMask.Handle() )
+		{
+		iDecoder->Convert( &iStatus, dstBitmap, dstMask, aFrameIndex );
+		}
+	else
+		{
+		dstMask.Reset();
+		iDecoder->Convert( &iStatus, dstBitmap, aFrameIndex );
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::StartLoadSubFrameL
+// -----------------------------------------------------------------------------
+void CIHLFileImage::StartLoadSubFrameL( TInt aFrameIndex, TBool aAnimation )
+	{
+	__ASSERT_DEBUG( !iSubFrameBitmap, Panic( KErrGeneral ) );
+
+	// Create animation bitmap
+	iSubFrameBitmap = CIHLBitmap::NewL();
+	CFbsBitmap& subBitmap = iSubFrameBitmap->BitmapModifyable();
+	CFbsBitmap& subMask = iSubFrameBitmap->MaskModifyable();
+
+    TSize dstSize( iDestination->Bitmap().SizeInPixels() );
+	TFrameInfo subFrameInfo( iDecoder->FrameInfo( aFrameIndex ) );
+
+    // Check is client uses downscaling (not used in animations)
+	TSize loadSize( subFrameInfo.iFrameSizeInPixels );   
+    iSubFrameScaleFactor = EFull;
+    if( !aAnimation &&
+        dstSize != iFrameInfo.iOverallSizeInPixels )
+        {
+        if( dstSize == ScaledLoadSize( iFrameInfo.iOverallSizeInPixels, EHalf ) )
+            {
+            iSubFrameScaleFactor = EHalf;
+            loadSize = ScaledLoadSize( loadSize, EHalf );
+            }
+        else if( dstSize == ScaledLoadSize( iFrameInfo.iOverallSizeInPixels, EQuarter ) )
+            {
+            iSubFrameScaleFactor = EQuarter;
+            loadSize = ScaledLoadSize( loadSize, EQuarter );
+            }
+        else if( dstSize == ScaledLoadSize( iFrameInfo.iOverallSizeInPixels, EEighth ) )
+            {
+            iSubFrameScaleFactor = EEighth;
+            loadSize = ScaledLoadSize( loadSize, EEighth );
+            }
+        }
+    User::LeaveIfError( subBitmap.Create( loadSize, EColor16M ) );
+
+	if( subFrameInfo.iFlags & TFrameInfo::ETransparencyPossible )
+		{
+		User::LeaveIfError( subMask.Create( loadSize,
+			subFrameInfo.iFlags & TFrameInfo::EAlphaChannel ? EGray256 : EGray2 ) );
+		iDecoder->Convert( &iStatus, subBitmap, subMask, aFrameIndex );
+		}
+	else
+		{
+		iDecoder->Convert( &iStatus, subBitmap, aFrameIndex );
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::CompleteLoadL
+// -----------------------------------------------------------------------------
+void CIHLFileImage::CompleteLoadL()
+	{
+    IHL_DEBUG1( KIHLDebug1, "IHL - CIHLFileImage - ICL convert complete!" );
+
+	if( iSubFrameBitmap )
+		{
+        IHL_DEBUG1( KIHLDebug2, "IHL - CIHLFileImage - Start build animation frame" );
+
+		// Copy animation bitmap to destination
+		BuildSubFrameL();
+		delete iSubFrameBitmap;
+		iSubFrameBitmap = NULL;
+
+        IHL_DEBUG1( KIHLDebug3, "IHL - CIHLFileImage - Animation frame complete!" );
+
+		// Save source info destination
+		iDestination->SetEditorPtr( this );
+		iDestination->SetEditorValue( iSubFrameIndex );
+
+		if( iSubFrameIndex < iFrameIndex )
+			{
+			// re-start the active object and load next subframe
+			iSubFrameIndex++;
+			iImageState = EStartLoad;
+			SelfComplete();
+			}
+		else
+			{
+			// Animation/subframe image ready
+			iDestination = NULL;
+			iImageState = EInactive;
+			RequestComplete( KErrNone );
+			}
+		}
+	else
+		{
+		// Save source info destination
+		iDestination->SetEditorPtr( this );
+		iDestination->SetEditorValue( iFrameIndex );
+
+		// Normal image ready
+		iDestination = NULL;
+		iImageState = EInactive;
+		RequestComplete( KErrNone );
+		}
+
+    IHL_DEBUG1( KIHLDebug4, "IHL - CIHLFileImage - Frame loading request complete!" );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::BuildSubFrameL
+// -----------------------------------------------------------------------------
+void CIHLFileImage::BuildSubFrameL()
+	{
+	__ASSERT_DEBUG( iSubFrameBitmap, Panic( KErrGeneral ) );
+	const CFbsBitmap& subBitmap = iSubFrameBitmap->Bitmap();
+	const CFbsBitmap& subMask = iSubFrameBitmap->Mask();
+	__ASSERT_DEBUG( subBitmap.Handle(), Panic( KErrGeneral ) );
+
+	if( !iAnimation ||
+	    ( iAnimation && iSubFrameIndex == 0 ) )
+		{
+		TFrameInfo frameInfo( iDecoder->FrameInfo( iSubFrameIndex ) );
+        if( iDestination->Bitmap().SizeInPixels() == subBitmap.SizeInPixels() &&
+            frameInfo.iFrameCoordsInPixels.iTl == TPoint(0,0) )
+            {
+    		// Sub frame is same size as destination image and has no offset
+    		// -> put directly into destination
+    		User::LeaveIfError( iDestination->Copy( subBitmap, subMask, ETrue ) );
+            }
+        else
+            {
+            // Sub frame size differs from destination image size
+    		CFbsBitmap& desBitmap = iDestination->BitmapModifyable();
+    		CFbsBitmap& desMask = iDestination->MaskModifyable();
+
+    		// Other frames must be build on top of previous frames
+    		__ASSERT_DEBUG( desBitmap.Handle(), Panic( KErrGeneral ) );
+
+            // Fill destination using background color
+		    FillL( desBitmap, frameInfo.iBackgroundColor );
+
+    		// Copy loaded frame on top of background
+    		CFbsBitGc* bitGc;
+    		CFbsBitmapDevice* bitDevice = CFbsBitmapDevice::NewL( &desBitmap );
+    		CleanupStack::PushL( bitDevice );
+    		User::LeaveIfError( bitDevice->CreateContext( bitGc ) );
+    		CleanupStack::PushL( bitGc );
+
+            TPoint framePos( ScaledFramePosition(
+                    frameInfo.iFrameCoordsInPixels.iTl, iSubFrameScaleFactor ) );
+    		if( subMask.Handle() )
+    			{
+    			bitGc->BitBltMasked( framePos, &subBitmap,
+    								 subBitmap.SizeInPixels(), &subMask, EFalse );
+    			}
+    		else
+    			{
+    			bitGc->BitBlt( framePos, &subBitmap, subBitmap.SizeInPixels() );
+    			}
+    		CleanupStack::PopAndDestroy( 2, bitDevice ); // bitGc, bitDevice
+
+            if( desMask.Handle() )
+                {
+                // Fill mask to transparent
+    		    FillL( desMask, KRgbBlack );
+
+                // Fill bg mask with transparency (=black)
+        		CFbsBitmapDevice* maskDev = CFbsBitmapDevice::NewL( &desMask );
+        		CleanupStack::PushL( maskDev );
+    		    CFbsBitGc* maskGc;
+        		User::LeaveIfError( maskDev->CreateContext( maskGc ) );
+        		CleanupStack::PushL( maskGc );
+
+                // Combine bg mask with first frame mask
+				maskGc->BitBlt( framePos, &subMask, subMask.SizeInPixels() );
+
+    		    CleanupStack::PopAndDestroy( 2, maskDev ); // maskGc, maskDev
+                }
+            }
+
+		// Create "previous frame" if animation
+		if( iAnimation )
+		    {
+    		if( !iPrevAnimationFrame )
+    		    {
+    		    iPrevAnimationFrame = CIHLBitmap::NewL();
+    		    }
+    		CFbsBitmap& desBitmap = iDestination->BitmapModifyable();
+    		CFbsBitmap& desMask = iDestination->MaskModifyable();
+    		if( iSubFrameBitmap->HasMask() )
+    		    {
+        		User::LeaveIfError(
+        		    iPrevAnimationFrame->Create( desBitmap.SizeInPixels(),
+        		        desBitmap.DisplayMode(), desMask.DisplayMode() ) );
+        		FillL( iPrevAnimationFrame->BitmapModifyable(), frameInfo.iBackgroundColor );
+    		    FillL( iPrevAnimationFrame->MaskModifyable(), KRgbBlack );
+    		    }
+    		else
+    		    {
+        		User::LeaveIfError(
+        		    iPrevAnimationFrame->Create( desBitmap.SizeInPixels(),
+        		                                 desBitmap.DisplayMode() ) );
+        		FillL( iPrevAnimationFrame->BitmapModifyable(), frameInfo.iBackgroundColor );
+    		    }
+		    }
+		}
+	else // same as iAnimation && iSubFrameIndex > 0
+		{
+		TFrameInfo prevFrameInfo( iDecoder->FrameInfo( iSubFrameIndex - 1 ) );
+	    if ( prevFrameInfo.iFlags & TFrameInfo::ERestoreToPrevious )
+	        {
+            // Restore destination to "previous frame"
+            User::LeaveIfError( iDestination->Copy( *iPrevAnimationFrame, EFalse ) );
+	        }
+
+		CFbsBitmap& prevBitmap = iDestination->BitmapModifyable();
+		CFbsBitmap& prevMask = iDestination->MaskModifyable();
+
+		// Other frames must be build on top of previous frames
+		__ASSERT_DEBUG( prevBitmap.Handle(), Panic( KErrGeneral ) );
+
+		// Restore area in destination bitmap if needed
+		TRect restoreRect;
+		TBool restoreToBackground( EFalse );
+		if( prevFrameInfo.iFlags & TFrameInfo::ERestoreToBackground )
+			{
+			restoreToBackground = ETrue;
+			restoreRect = prevFrameInfo.iFrameCoordsInPixels;
+		    FillRectL( prevBitmap, restoreRect, prevFrameInfo.iBackgroundColor );
+
+			// Cache new "previous frame"
+			User::LeaveIfError( iPrevAnimationFrame->Copy( *iDestination, EFalse ) );
+			}
+	    else if( prevFrameInfo.iFlags & TFrameInfo::ELeaveInPlace )
+	        {
+			// Cache new "previous frame"
+			User::LeaveIfError( iPrevAnimationFrame->Copy( *iDestination, EFalse ) );
+	        }
+
+		// Copy animation frame to destination bitmap
+		TFrameInfo frameInfo( iDecoder->FrameInfo( iSubFrameIndex ) );
+		CFbsBitGc* bitGc;
+		CFbsBitmapDevice* bitDevice = CFbsBitmapDevice::NewL( &prevBitmap );
+		CleanupStack::PushL( bitDevice );
+		User::LeaveIfError( bitDevice->CreateContext( bitGc ) );
+		CleanupStack::PushL( bitGc );
+		if( subMask.Handle() )
+			{
+			bitGc->BitBltMasked( frameInfo.iFrameCoordsInPixels.iTl, &subBitmap,
+									subBitmap.SizeInPixels(), &subMask, EFalse );
+			}
+		else
+			{
+			bitGc->BitBlt( frameInfo.iFrameCoordsInPixels.iTl, &subBitmap,
+									subBitmap.SizeInPixels() );
+			}
+		CleanupStack::PopAndDestroy( 2 ); // bitGc, bitDevice
+
+		// Combine masks if any
+		if( prevMask.Handle() && subMask.Handle() )
+			{
+			//////////////////////////////////////////////////////////////////////////
+			// ALTERNATIVE WAY TO COMBINE MASKS!
+			// Current solution doesn't combine soft masks.
+			// Following code could be used if soft masks are enabled in animations.
+			// Do not delete!
+			//////////////////////////////////////////////////////////////////////////
+			/*
+			if( restoreToBackground )
+				{
+				bitDevice = CFbsBitmapDevice::NewL( &prevMask );
+				CleanupStack::PushL( bitDevice );
+				User::LeaveIfError( bitDevice->CreateContext( bitGc ) );
+				CleanupStack::PushL( bitGc );
+
+				bitGc->SetBrushColor( KRgbBlack );
+				bitGc->SetBrushStyle( CGraphicsContext::ESolidBrush );
+				bitGc->DrawRect( restoreRect );
+				bitGc->SetBrushStyle( CGraphicsContext::ENullBrush );
+
+				CleanupStack::PopAndDestroy( 2 ); // bitDevice, bitGc
+				}
+
+			prevMask.LockHeap();
+
+			TUint8* srcAddress = reinterpret_cast<TUint8*>( animMask.DataAddress() );
+			TSize srcSize( animMask.SizeInPixels() );
+			TPoint srcPos( 0,0 );
+			TInt srcScanLen8 = CFbsBitmap::ScanLineLength( srcSize.iWidth, EGray256 );
+
+			TUint8* dstAddress = reinterpret_cast<TUint8*>( prevMask.DataAddress() );
+			TSize dstSize( prevMask.SizeInPixels() );
+			TPoint dstPos( frameInfo.iFrameCoordsInPixels.iTl );
+			TPoint dstEndPos( frameInfo.iFrameCoordsInPixels.iBr );
+			TInt dstScanLen8 = CFbsBitmap::ScanLineLength( dstSize.iWidth, EGray256 );
+
+			while( dstPos.iY < dstEndPos.iY )
+				{
+				TUint8* srcAddressCur = srcAddress + ( srcPos.iY * srcScanLen8 );
+				TUint8* dstAddressCur = dstAddress + ( dstPos.iY * dstScanLen8 );
+				while( dstPos.iX < dstEndPos.iX )
+					{
+					TUint8& srcPixel = srcAddressCur[ srcPos.iX++ ];
+					TUint8& dstPixel = dstAddressCur[ dstPos.iX++ ];
+					if( srcPixel > dstPixel )
+						{
+						dstPixel = srcPixel;
+						}
+					}
+
+				srcPos.iX = 0;
+				srcPos.iY++;
+				dstPos.iX = frameInfo.iFrameCoordsInPixels.iTl.iX;
+				dstPos.iY++;
+				}
+
+			animMask.UnlockHeap();
+			*/
+
+			if( restoreToBackground )
+				{
+		        FillRectL( prevMask, restoreRect, KRgbBlack );
+				}
+
+			bitDevice = CFbsBitmapDevice::NewL( &prevMask );
+			CleanupStack::PushL( bitDevice );
+			User::LeaveIfError( bitDevice->CreateContext( bitGc ) );
+			CleanupStack::PushL( bitGc );
+
+			CFbsBitmap* tmpMask = new(ELeave) CFbsBitmap;
+			CleanupStack::PushL( tmpMask );
+			User::LeaveIfError( tmpMask->Create( prevMask.SizeInPixels(), prevMask.DisplayMode() ) );
+			CFbsBitmapDevice* tmpMaskDev = CFbsBitmapDevice::NewL( tmpMask );
+			CleanupStack::PushL( tmpMaskDev );
+			CFbsBitGc* tmpMaskGc;
+			User::LeaveIfError( tmpMaskDev->CreateContext( tmpMaskGc ) );
+			CleanupStack::PushL( tmpMaskGc );
+
+			tmpMaskGc->BitBlt( TPoint( 0, 0 ), &prevMask, frameInfo.iFrameCoordsInPixels );
+
+			bitGc->BitBltMasked( frameInfo.iFrameCoordsInPixels.iTl, &subMask,
+								 subMask.SizeInPixels(), tmpMask, ETrue );
+
+			CleanupStack::PopAndDestroy( 5 ); // tmpMaskGc,tmpMaskDev,tmpMask,bitGc,bitDevice
+			}
+		else
+			{
+			prevMask.Reset(); // Mask not valid anymore -> reset
+			}
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::FillL
+// -----------------------------------------------------------------------------
+void CIHLFileImage::FillL( CFbsBitmap& aBitmap, const TRgb& aColor )
+    {
+	CFbsBitGc* bitGc;
+	CFbsBitmapDevice* bitDevice = CFbsBitmapDevice::NewL( &aBitmap );
+	CleanupStack::PushL( bitDevice );
+	User::LeaveIfError( bitDevice->CreateContext( bitGc ) );
+	CleanupStack::PushL( bitGc );
+
+	bitGc->SetBrushColor( aColor );
+	bitGc->SetPenColor( aColor );
+	bitGc->SetBrushStyle( CGraphicsContext::ESolidBrush );
+	bitGc->Clear();
+	bitGc->SetBrushStyle( CGraphicsContext::ENullBrush );
+
+	CleanupStack::PopAndDestroy( 2 ); // bitGc, bitDevice
+    }
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::FillRectL
+// -----------------------------------------------------------------------------
+void CIHLFileImage::FillRectL( CFbsBitmap& aBitmap, const TRect& aRect,
+                               const TRgb& aColor )
+    {
+	CFbsBitGc* bitGc;
+	CFbsBitmapDevice* bitDevice = CFbsBitmapDevice::NewL( &aBitmap );
+	CleanupStack::PushL( bitDevice );
+	User::LeaveIfError( bitDevice->CreateContext( bitGc ) );
+	CleanupStack::PushL( bitGc );
+
+	bitGc->SetBrushColor( aColor );
+	bitGc->SetPenColor( aColor );
+	bitGc->SetBrushStyle( CGraphicsContext::ESolidBrush );
+	bitGc->DrawRect( aRect );
+	bitGc->SetBrushStyle( CGraphicsContext::ENullBrush );
+
+	CleanupStack::PopAndDestroy( 2 ); // bitGc, bitDevice
+    }
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ErrorCleanup
+// -----------------------------------------------------------------------------
+void CIHLFileImage::ErrorCleanup()
+	{
+	if( iSubFrameBitmap )
+		{
+		delete iSubFrameBitmap;
+		iSubFrameBitmap = NULL;
+		}
+
+	if( iDestination )
+		{
+		iDestination = NULL;
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::SelfComplete
+// -----------------------------------------------------------------------------
+void CIHLFileImage::SelfComplete()
+	{
+	SetActive();
+	iStatus = KRequestPending;
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete( status, KErrNone );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::RequestComplete
+// -----------------------------------------------------------------------------
+void CIHLFileImage::RequestComplete( TInt aReason )
+	{
+	ASSERT( iImageStatus );
+	User::RequestComplete( iImageStatus, aReason );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ScaledLoadSize
+// -----------------------------------------------------------------------------
+TSize CIHLFileImage::ScaledLoadSize( const TSize& aOriginalSize, TInt aScaleFactor )
+	{
+	// Divide original size with scalefactor
+	// Round always up if result is not integer
+	return ( aScaleFactor == EFull ) ? aOriginalSize :
+		TSize( aOriginalSize.iWidth / aScaleFactor + ( aOriginalSize.iWidth % aScaleFactor ? 1 : 0 ),
+			   aOriginalSize.iHeight / aScaleFactor + ( aOriginalSize.iHeight % aScaleFactor ? 1 : 0 ) );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLFileImage::ScaledLoadSize
+// -----------------------------------------------------------------------------
+TPoint CIHLFileImage::ScaledFramePosition( const TPoint& aOriginalPos, TInt aScaleFactor )
+	{
+	// Divide original position with scalefactor
+	// Round always up if result is not integer
+	return ( aScaleFactor == EFull ) ? aOriginalPos :
+		TPoint( aOriginalPos.iX / aScaleFactor + ( aOriginalPos.iX % aScaleFactor ? 1 : 0 ),
+			    aOriginalPos.iY / aScaleFactor + ( aOriginalPos.iY % aScaleFactor ? 1 : 0 ) );
+	}
+//  End of File