imagehandlinglib/Src/CIHLFileImage.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 15:18:05 +0200
changeset 0 2014ca87e772
permissions -rw-r--r--
Revision: 201004

/*
* 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