internetradio2.0/uicontrolssrc/irimageconverterimpl.cpp
changeset 0 09774dfdd46b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/internetradio2.0/uicontrolssrc/irimageconverterimpl.cpp	Mon Apr 19 14:01:53 2010 +0300
@@ -0,0 +1,801 @@
+/*
+* Copyright (c) 2008-2008 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:  Internal image converter implementation
+*
+*/
+
+
+#include <aknlayoutfont.h>
+#include <aknutils.h>
+#include <bitmaptransforms.h>
+#include <coemain.h>
+#include <gulicon.h>
+#include <ihlbitmaputil.h>
+#include <imageconversion.h>
+#include <svgengineinterfaceimpl.h>
+
+
+#include "irimageconverterimpl.h"
+#include "irimageconverterobserver.h"
+#include "irdebug.h"
+// Image decoder options.
+const CImageDecoder::TOptions KIRImageDecoderOptions = CImageDecoder::EOptionAlwaysThread;
+// Bitmap scaler quality.
+const CBitmapScaler::TQualityAlgorithm KIRBitmapScalerQualityAlgorithm = 
+											CBitmapScaler::EMaximumQuality;
+const TInt KFour=4;
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::NewL()
+// ---------------------------------------------------------------------------
+//
+CIRImageConverterImpl* CIRImageConverterImpl::NewL()
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::NewL - Entering" );
+    CIRImageConverterImpl* self = new ( ELeave ) CIRImageConverterImpl;
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// Constructor.
+// ---------------------------------------------------------------------------
+//
+CIRImageConverterImpl::CIRImageConverterImpl()
+    : CActive( CActive::EPriorityStandard ),
+      iEnableAnimations( ETrue ),
+      iMaintainAspectRatio( ETrue )
+    {
+    CActiveScheduler::Add( this );
+    }
+
+// ---------------------------------------------------------------------------
+// Second-phase constructor.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::ConstructL()
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::ConstructL - entering" );
+    iNotifyObserverCallBack = new ( ELeave ) CAsyncCallBack( 
+        TCallBack( StaticNotifyObserverCallBack, this ), CActive::EPriorityHigh );
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::~CIRImageConverter()
+// ---------------------------------------------------------------------------
+//
+CIRImageConverterImpl::~CIRImageConverterImpl()
+    {
+    Cancel();
+
+    iFrames.ResetAndDestroy();
+    iFrames.Close();
+    
+    delete iDecoder;
+    delete iScaler;
+    delete iSvgEngine;
+    delete iProcessedBitmap;
+    delete iProcessedMask;
+    delete iBitmap;
+    delete iMask;
+    delete iLastFrameBitmap;
+    delete iLastFrameMask;
+    delete iNotifyObserverCallBack;
+    delete iFrameTimer;
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::SetDataL( const TDesC8& aData )
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::SetDataL( const TDesC8& aData )
+    {
+    IRRDEBUG2( "CIRImageConverterImpl::SetDataL - Entering", KNullDesC );
+    Cleanup( ETrue );
+    iData.Set( aData );
+    CreateDataHandlerL();
+    IRRDEBUG2( "CIRImageConverterImpl::SetDataL - Exiting", KNullDesC );
+   }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::SetObserver( MIRImageConverterObserver* aObserver )
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::SetObserver( MIRImageConverterObserver* aObserver )
+    {
+    iObserver = aObserver;
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::EnableAnimations( TBool aEnable )
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::EnableAnimations( TBool aEnable )
+    {
+    __ASSERT_DEBUG( iState == EIRStateIdle, User::Invariant() );
+    iEnableAnimations = aEnable;
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::IsAnimated() const
+// ---------------------------------------------------------------------------
+//
+TBool CIRImageConverterImpl::IsAnimated() const
+    {
+    __ASSERT_DEBUG( iState != EIRStateIdle, User::Invariant() );
+    return iIsAnimated;
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::MaintainAspectRatio( TBool aMaintain )
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::MaintainAspectRatio( TBool aMaintain )
+    {
+    __ASSERT_DEBUG( iState == EIRStateIdle, User::Invariant() );
+    iMaintainAspectRatio = aMaintain;
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::StartL( const TSize& aTarget, TInt aId )
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::StartL( const TSize& aTarget, TInt aId )
+	{
+    IRRDEBUG2( "CIRImageConverterImpl::StartL - Entering", KNullDesC );
+	if ( iState != EIRStateBitmapDecoderInitialized && iState != EIRStateSvgEngineInitialized )
+		{
+		if ( iState == EIRStateIdle )
+			{
+		    IRRDEBUG2("CIRImageConverterImpl::StartL, leave with KErrNotReady", KNullDesC);
+			User::Leave( KErrNotReady );
+			}
+		else
+			{
+		    IRRDEBUG2("CIRImageConverterImpl::StartL, leave with KErrInUse", KNullDesC);
+			User::Leave( KErrInUse );
+			}
+		}
+	iTargetSize = aTarget;
+	iId = aId;
+	if ( iState == EIRStateSvgEngineInitialized )
+		{
+		// The SVG-T engine has been initialized with dummy 0x0 bitmaps, so updating those must be handled here.
+	    IRRDEBUG2( "CIRImageConverterImpl::StartL - if begin", KNullDesC );
+		CreateBitmapL( iTargetSize, iBitmap, iMask );
+		iSvgEngine->SetFrameBuffer( iBitmap );
+		iState = EIRStateConvertingSvg;
+		iSvgEngine->Start();
+		IRRDEBUG2( "CIRImageConverterImpl::StartL - if end", KNullDesC );
+		}
+	else
+		{
+	    IRRDEBUG2( "CIRImageConverterImpl::StartL - else begin", KNullDesC );
+		iState = EIRStateConvertingBitmap;
+		IRRDEBUG2( "CIRImageConverterImpl::StartL, iDecorder = %d", iDecoder);
+		iDecoder->Convert( &iStatus, *iBitmap, *iMask );
+		IRRDEBUG2( "CIRImageConverterImpl::StartL - else-exit call setactive", KNullDesC );
+		SetActive();
+		}
+	IRRDEBUG2( "CIRImageConverterImpl::StartL - Exiting", KNullDesC );
+	}
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::Stop()
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::Stop()
+	{
+    IRRDEBUG2( "CIRImageConverterImpl::Stop - Entering", KNullDesC );
+	Cancel();
+	if ( iSvgEngine )
+	    {
+	    iSvgEngine->Stop();
+	    }
+	TIRImageConverterState state = iState;
+	Cleanup();
+	if ( state != EIRStateIdle && state != EIRStateBitmapDecoderInitialized && 
+		state != EIRStateSvgEngineInitialized )
+	    {
+	    NotifyObserver( KErrCancel );
+	    }
+	IRRDEBUG2( "CIRImageConverterImpl::Stop - Exiting",KNullDesC );
+	}
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::Bitmap() const
+// ---------------------------------------------------------------------------
+//
+const CFbsBitmap* CIRImageConverterImpl::Bitmap() const
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::Bitmap - Entering" );
+    return iProcessedBitmap;
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::Mask() const
+// ---------------------------------------------------------------------------
+//
+const CFbsBitmap* CIRImageConverterImpl::Mask() const
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::Mask - Entering" );
+    return iProcessedMask;
+    }
+
+// ---------------------------------------------------------------------------
+// @see CIRImageConverter::TransferBitmapOwnership( CFbsBitmap*& aBitmap, CFbsBitmap*& aMask )
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::TransferBitmapOwnership( CFbsBitmap*& aBitmap, CFbsBitmap*& aMask )
+    {
+    aBitmap = iProcessedBitmap;
+    iProcessedBitmap = NULL;
+    aMask = iProcessedMask;
+    iProcessedMask = NULL;
+    }
+
+// ---------------------------------------------------------------------------
+// From class CActive.
+// Invoked when the active object is cancelled.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::DoCancel()
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::DoCancel - Entering" );
+    if ( iDecoder )
+        {
+        iDecoder->Cancel();
+        }
+    if ( iScaler )
+        {
+        iScaler->Cancel();
+        }
+	IRLOG_DEBUG( "CIRImageConverterImpl::DoCancel - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// From class CActive.
+// Invoked when the active object completes an asynchronous request.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::RunL()
+	{
+	IRLOG_DEBUG( "CIRImageConverterImpl::RunL - Entering" );
+	if ( iStatus.Int() == KErrNone )
+		{
+		switch ( iState )
+			{
+			case EIRStateConvertingBitmap:
+				{
+				if ( iIsAnimated && iEnableAnimations )
+					{
+					CGulIcon* icon = CGulIcon::NewLC();
+
+					icon->SetBitmapsOwnedExternally( EFalse );
+					icon->SetBitmap( iBitmap );
+					icon->SetMask( iMask );
+
+					iBitmap = NULL;
+					iMask = NULL;
+
+					iFrames.AppendL( icon );
+					CleanupStack::Pop( icon );
+
+					if ( iFrames.Count() < iDecoder->FrameCount() )
+						{
+						CreateBitmapL( iDecoder->FrameInfo( iFrames.Count() ).iOverallSizeInPixels,
+								 iBitmap, iMask );
+
+						iDecoder->Convert( &iStatus, *iBitmap, *iMask, iFrames.Count() );
+						SetActive();
+						}
+					else
+						{
+						StartBitmapAnimationL();
+						}
+					}
+				else
+					{
+					iState = EIRStateScalingBitmap;
+					iScaler->Scale( &iStatus, *iBitmap, iTargetSize, iMaintainAspectRatio );
+					SetActive();
+					}
+				}
+			break;
+			case EIRStateScalingBitmap:
+			iState = EIRStateScalingBitmapMask;
+			iScaler->Scale( &iStatus, *iMask, iTargetSize, iMaintainAspectRatio );
+			SetActive();
+			break;
+			case EIRStateScalingBitmapMask:
+			NotifyObserver( KErrNone );
+			break;
+			default:
+			break;
+			}
+		}
+	else
+		{
+		NotifyObserver( iStatus.Int() );
+		}
+	IRLOG_DEBUG( "CIRImageConverterImpl::RunL - Exiting" );
+	}
+
+// ---------------------------------------------------------------------------
+// From class CActive.
+// Invoked when RunL leaves.
+// ---------------------------------------------------------------------------
+//
+TInt CIRImageConverterImpl::RunError( TInt aError )
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::NewL - Entering" );
+    NotifyObserver( aError );
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------------------------
+// From class MSvgRequestObserver.
+// Invoked when the bitmap has been updated by the SVG-T engine.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::UpdateScreen()
+    {
+    iSvgEngine->GenerateMask( iMask );
+    // SVG-T engine MUST NOT be destroyed here as its internal implementation
+    // relies on it existing after execution leaves this method. This means
+    // that the observer notification must be asynchronous
+    NotifyObserver( KErrNone, EFalse );
+    }
+
+// ---------------------------------------------------------------------------
+// From class MSvgRequestObserver.
+// Not implemented.
+// ---------------------------------------------------------------------------
+//
+TBool CIRImageConverterImpl::ScriptCall( const TDesC& /*aScript*/, CSvgElementImpl* 
+											/*aCallerElement*/ )
+    {
+    return EFalse;
+    }
+
+// ---------------------------------------------------------------------------
+// From class MSvgRequestObserver.
+// Not implemented.
+// ---------------------------------------------------------------------------
+//
+TInt CIRImageConverterImpl::FetchImage( const TDesC& /*aUri*/, RFs& /*aSession*/, RFile& 
+																		/*aFileHandle*/ )
+    {
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------------------------
+// From class MSvgRequestObserver.
+// Not implemented.
+// ---------------------------------------------------------------------------
+//
+TInt CIRImageConverterImpl::FetchFont( const TDesC& /*aUri*/, RFs& /*aSession*/, RFile& 
+																		/*aFileHandle*/ )
+    {
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------------------------
+// From class MSvgRequestObserver.
+// Not implemented.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::UpdatePresentation( const TInt32& /*aNoOfAnimation*/ )
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// Performs cleanup on the converter object.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::Cleanup( TBool aThorough )
+    {
+    Cancel();
+    if ( aThorough )
+        {
+        delete iProcessedBitmap;
+        iProcessedBitmap = NULL;
+        
+        delete iProcessedMask;
+        iProcessedMask = NULL;
+        }
+    if(iDecoder)
+	    {
+	    delete iDecoder;
+	    iDecoder = NULL;
+	    }
+    if(iScaler)
+	    {
+	    delete iScaler;
+	    iScaler = NULL;
+	    }
+	    
+	if(iSvgEngine)
+		{
+	    delete iSvgEngine;
+	    iSvgEngine = NULL;
+		}
+	if(iFrameTimer)
+		{
+	    delete iFrameTimer;
+	    iFrameTimer = NULL;
+		}
+	if(iBitmap)
+		{
+	    delete iBitmap;
+	    iBitmap = NULL;
+		}
+	if(iMask)
+		{
+	    delete iMask;
+	    iMask = NULL;
+		}
+	if(iLastFrameBitmap)
+		{
+	    delete iLastFrameBitmap;
+	    iLastFrameBitmap = NULL;
+		}
+	if(iLastFrameMask)
+		{
+	    delete iLastFrameMask;
+	    iLastFrameMask = NULL;
+		}
+
+    iFrames.ResetAndDestroy();
+
+    iNotifyObserverCallBack->Cancel();
+
+    iFrameIndex = 0;
+    iIsAnimated = EFalse;
+    iError = KErrNone;
+    iState = EIRStateIdle;
+    }
+
+// ---------------------------------------------------------------------------
+// Creates the proper handler for the raw image data.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::CreateDataHandlerL()
+    {
+    IRRDEBUG2( "CIRImageConverterImpl::CreateDataHandlerL - Entering", KNullDesC );
+    TBuf8<KMaxDataTypeLength> mimeType;
+    TRAPD( err, CImageDecoder::GetMimeTypeDataL( iData, mimeType ) )
+
+	if ( err == KErrNone ) // Image decoder can handle this MIME type.
+		{
+
+		TRAPD(err1,iDecoder = CImageDecoder::DataNewL( CCoeEnv::Static()->FsSession(), iData,
+		KIRImageDecoderOptions );)
+		if(err1!=KErrNone)
+			{  
+			if ( iObserver )
+				{
+			    IRRDEBUG2( "CIRImageConverterImpl::CreateDataHandlerL, err = %d", err1);
+				NotifyObserver( err1 );
+				IRRDEBUG2( "CIRImageConverterImpl::CreateDataHandlerL, error is notified", KNullDesC);
+				}
+			}
+		else
+			{
+        iScaler = CBitmapScaler::NewL();
+        iScaler->SetQualityAlgorithm( KIRBitmapScalerQualityAlgorithm );
+        iScaler->UseLowMemoryAlgorithm( ETrue ); // Return value ignored on purpose; it's ok if low memory algorithm cannot be used when scaling.
+
+        if ( iDecoder->FrameCount() > 1 )
+            {
+            iFrameTimer = CPeriodic::NewL( CActive::EPriorityStandard );
+            iIsAnimated = ETrue;
+            }
+
+        CreateBitmapL( iDecoder->FrameInfo().iOverallSizeInPixels, iBitmap, iMask );
+        }
+		}
+    else // Try using the SVG-T engine for decoding the image.
+        {
+        
+        TFontSpec fontSpec = AknLayoutUtils::LayoutFontFromId( 
+        						EAknLogicalFontSecondaryFont )->FontSpecInTwips();
+
+        // The SVG-T engine requires that the bitmap is created prior to its instantiation.
+        // As the dummy bitmaps created here are 0x0, StartL must take care of instantiating them to
+        // proper sizes and updating the SVG-T engine's frame buffer manually before starting the conversion.
+        
+        CreateBitmapL( TSize( 0, 0 ), iBitmap, iMask );
+        
+        iSvgEngine = CSvgEngineInterfaceImpl::NewL( iBitmap, this, fontSpec );
+        MSvgError* svgErr = iSvgEngine->Load( iData );
+        if ( svgErr && svgErr->HasError() )
+            {
+            err = svgErr->SystemErrorCode();
+            }
+        else
+            {
+            err = KErrNone;
+            iIsAnimated = iSvgEngine->SvgHasAnimation( iSvgEngine->SvgDocument() );
+            }
+        }
+
+    if ( err )
+        {
+        Cleanup( ETrue );
+        User::Leave( KErrNotSupported );
+        }
+    else
+        {
+        iState = iSvgEngine ? EIRStateSvgEngineInitialized : EIRStateBitmapDecoderInitialized;
+        }
+
+    IRRDEBUG2( "CIRImageConverterImpl::CreateDataHandlerL - Exiting" , KNullDesC);
+   }
+
+// ---------------------------------------------------------------------------
+// Creates new bitmaps of the given size.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::CreateBitmapL( const TSize& aSize, CFbsBitmap*& aBitmap, 
+										CFbsBitmap*& aMask )
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::CreateBitmapL - Entering" );
+    CFbsBitmap* bitmap = new ( ELeave ) CFbsBitmap;
+    CleanupStack::PushL( bitmap );
+    User::LeaveIfError( bitmap->Create( aSize, CCoeEnv::Static()->ScreenDevice()
+    									->DisplayMode() ) );
+    
+    CFbsBitmap* mask = new ( ELeave ) CFbsBitmap;
+    CleanupStack::PushL( mask );
+    User::LeaveIfError( mask->Create( aSize, EGray256 ) );
+    
+    delete aBitmap;
+    aBitmap = bitmap;
+    
+    delete aMask;
+    aMask = mask;
+    
+    CleanupStack::Pop( 2, bitmap );
+	IRLOG_DEBUG( "CIRImageConverterImpl::CreateBitmapL - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// Creates exact copies of the supplied bitmaps.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::CreateBitmapCopyL( const CFbsBitmap& aSourceBitmap, 
+                                               const CFbsBitmap& aSourceMask, 
+                                               CFbsBitmap*& aBitmap, 
+                                               CFbsBitmap*& aMask )
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::CreateBitmapCopyL - Entering" );
+    CFbsBitmap* bitmap = IHLBitmapUtil::CopyBitmapLC( aSourceBitmap );
+    CFbsBitmap* mask = IHLBitmapUtil::CopyBitmapLC( aSourceMask );
+    
+    delete aBitmap;
+    aBitmap = bitmap;
+    
+    delete aMask;
+    aMask = mask;
+
+    CleanupStack::Pop( 2, bitmap );
+	IRLOG_DEBUG( "CIRImageConverterImpl::CreateBitmapCopyL - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// Starts the bitmap animation.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::StartBitmapAnimationL()
+    {
+    __ASSERT_DEBUG( iFrames.Count() == iDecoder->FrameCount(), User::Invariant() );
+    
+    RenderBitmapAnimationFrameL( ETrue );
+    }
+
+// ---------------------------------------------------------------------------
+// Renders the current bitmap animation frame.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::RenderBitmapAnimationFrameL( TBool aFirstTime )
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::RenderBitmapAnimationFrameL - Entering" );
+    __ASSERT_DEBUG( iFrames.Count() && iFrames.Count() > iFrameIndex, User::Invariant() );
+    
+    
+    const TGifImageControl* gifImageControl = static_cast<const TGifImageControl*>
+    							( iDecoder->FrameData( iFrameIndex ).GetFrameData( 0 ) );
+    if ( gifImageControl )
+        {
+        if ( aFirstTime )
+            {
+            // When rendering the animation for the very first time, we just make a copy of the first frame.
+            CreateBitmapCopyL( *iFrames[0]->Bitmap(), *iFrames[0]->Mask(), 
+            				iLastFrameBitmap, iLastFrameMask );
+            }
+        else
+            {
+            CFbsBitmapDevice* lastFrameBitmapDevice = CFbsBitmapDevice::NewL( iLastFrameBitmap );
+            CleanupStack::PushL( lastFrameBitmapDevice );
+            CFbsBitGc* lastFrameBitmapGc = CFbsBitGc::NewL();
+            CleanupStack::PushL( lastFrameBitmapGc );
+            lastFrameBitmapGc->Activate( lastFrameBitmapDevice );
+            
+            CFbsBitmapDevice* lastFrameMaskDevice = CFbsBitmapDevice::NewL( iLastFrameMask );
+            CleanupStack::PushL( lastFrameMaskDevice );
+            CFbsBitGc* lastFrameMaskGc = CFbsBitGc::NewL();
+            CleanupStack::PushL( lastFrameMaskGc );
+            lastFrameMaskGc->Activate( lastFrameMaskDevice );
+            
+            TRect rect = iDecoder->FrameInfo( iFrameIndex ).iFrameCoordsInPixels;
+            
+
+            switch ( gifImageControl->iDisposalMethod )
+                {
+                case TGifImageControl::ENone:
+                    // The new frame is completely self-sufficient, so no information about the previous frame is required.
+                    lastFrameBitmapGc->BitBlt( rect.iTl, iFrames[iFrameIndex]->Bitmap(), 
+                    							TRect( TPoint(), rect.Size() ) );
+                    lastFrameMaskGc->BitBlt( rect.iTl, iFrames[iFrameIndex]->Mask(),
+                    						 TRect( TPoint(), rect.Size() ) );
+                    break;
+                case TGifImageControl::ELeaveInPlace:
+                    // The new frame only contains a partial update on the image, so we have to retain the previous frame state.
+                    lastFrameBitmapGc->BitBltMasked( rect.iTl, iFrames[iFrameIndex]->Bitmap(), 
+                    								TRect( TPoint(), rect.Size() ),
+                    								iFrames[iFrameIndex]->Mask(), EFalse );
+                    break;
+                case TGifImageControl::ERestoreToBackground:
+                    // The new frame is restored to the background color defined in its data.
+                    lastFrameBitmapGc->SetBrushColor( 
+                    					iDecoder->FrameInfo( iFrameIndex ).iBackgroundColor );
+                    lastFrameBitmapGc->Clear();
+                    lastFrameMaskGc->SetBrushColor( KRgbBlack );
+                    lastFrameMaskGc->Clear();
+                    lastFrameBitmapGc->BitBlt( rect.iTl, iFrames[iFrameIndex]->Bitmap(), 
+                    						TRect( TPoint(), rect.Size() ) );
+                    lastFrameMaskGc->BitBlt( rect.iTl, iFrames[iFrameIndex]->Mask(),
+                    						 TRect( TPoint(), rect.Size() ) );
+                    break;
+                case TGifImageControl::ERestoreToPrevious:
+                    // The new frame is exactly the same as the previous one, so no special processing is required.
+                    break;
+                default:
+                    break;
+                }
+    
+            CleanupStack::PopAndDestroy( KFour, lastFrameBitmapDevice );
+            }
+        
+        CreateBitmapCopyL( *iLastFrameBitmap, *iLastFrameMask, iBitmap, iMask );
+        }
+    else
+        {
+        User::Leave( KErrNotReady );
+        }
+
+    iState = EIRStateScalingBitmap;
+    iScaler->Scale( &iStatus, *iBitmap, iTargetSize, iMaintainAspectRatio );
+    SetActive();
+	IRLOG_DEBUG( "CIRImageConverterImpl::RenderBitmapAnimationFrameL - Exiting" );
+    }
+
+// ---------------------------------------------------------------------------
+// Notifies the observer either synchronously or asynchronously.
+// ---------------------------------------------------------------------------
+//
+void CIRImageConverterImpl::NotifyObserver( TInt aError, TBool aSynchronous )
+    {
+    IRRDEBUG2( "CIRImageConverterImpl::NotifyObserver - Entering, aError = %d", aError );
+    iError = aError;
+    iNotifyObserverCallBack->Cancel();
+
+    if ( aSynchronous )
+        {
+        StaticNotifyObserverCallBack( this );
+        }
+    else
+        {
+        iNotifyObserverCallBack->CallBack();
+        }
+    IRRDEBUG2( "CIRImageConverterImpl::NotifyObserver - Exiting", KNullDesC );
+    }
+
+// ---------------------------------------------------------------------------
+// Call back for notifying the observer.
+// ---------------------------------------------------------------------------
+//
+TInt CIRImageConverterImpl::StaticNotifyObserverCallBack( TAny* aSelf )
+    {
+	IRLOG_DEBUG( "CIRImageConverterImpl::StaticNotifyObserverCallBack - Entering" );
+    CIRImageConverterImpl* self = static_cast<CIRImageConverterImpl*>( aSelf );
+    if ( self )
+        {
+        TInt err = self->iError;
+        MIRImageConverterObserver::TIRImageConversionEvent event = MIRImageConverterObserver::
+        														EIRImageConversionCompleted;
+        
+        if ( err == KErrNone )
+            {
+            if ( self->iSvgEngine && self->iIsAnimated && self->iEnableAnimations )
+                {
+                TRAP( err, self->CreateBitmapCopyL( *self->iBitmap, *self->iMask, 
+                		self->iProcessedBitmap, self->iProcessedMask ) )
+                }
+            else
+                {
+                delete self->iProcessedBitmap;
+                self->iProcessedBitmap = self->iBitmap;
+                self->iBitmap = NULL;
+                delete self->iProcessedMask;
+                self->iProcessedMask = self->iMask;
+                self->iMask = NULL;
+                }
+            
+            if ( !err && self->iIsAnimated && self->iEnableAnimations )
+                {
+                event = MIRImageConverterObserver::EIRFrameConversionCompleted;
+
+                if ( self->iDecoder )
+                    {
+                    self->iState = EIRStateBetweenBitmapAnimationFrames;
+                    
+                    self->iFrameTimer->Cancel();
+                    self->iFrameTimer->Start( static_cast<TInt>( self->iDecoder->
+                    				FrameInfo( self->iFrameIndex ).iDelay.Int64() ), 0, 
+                                              TCallBack( StaticNextFrameCallBack, self ) );
+
+                    self->iFrameIndex++;
+                    if ( self->iFrameIndex >= self->iDecoder->FrameCount() )
+                        {
+                        self->iFrameIndex = 0;
+                        }
+                    }
+                }
+            }
+
+        if ( err || event == MIRImageConverterObserver::EIRImageConversionCompleted )
+            {
+            self->Cleanup();
+            }
+        
+        if ( self->iObserver )
+            {
+            TRAP_IGNORE( self->iObserver->HandleImageConversionEventL( event, self->iId, err ) )
+            }
+        }
+    
+    return KErrNone;
+    }
+
+// ---------------------------------------------------------------------------
+// Call back for advancing to the next frame when bitmap animations are used.
+// ---------------------------------------------------------------------------
+//
+TInt CIRImageConverterImpl::StaticNextFrameCallBack( TAny* aSelf )
+    {
+    CIRImageConverterImpl* self = static_cast<CIRImageConverterImpl*>( aSelf );
+    if ( self )
+        {
+        self->iFrameTimer->Cancel();
+        TRAPD( err, self->RenderBitmapAnimationFrameL() )
+        if ( err )
+            {
+            self->NotifyObserver( err );
+            }
+        }
+    return KErrNone;
+    }