diff -r 000000000000 -r 2014ca87e772 imagehandlinglib/Src/CIHLFileImage.cpp --- /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 + +// 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& 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( &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( animMask.DataAddress() ); + TSize srcSize( animMask.SizeInPixels() ); + TPoint srcPos( 0,0 ); + TInt srcScanLen8 = CFbsBitmap::ScanLineLength( srcSize.iWidth, EGray256 ); + + TUint8* dstAddress = reinterpret_cast( 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