diff -r 000000000000 -r 094583676ce7 wvuing/wvuieng/EngSrc/CCAImageLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wvuing/wvuieng/EngSrc/CCAImageLoader.cpp Thu Dec 17 08:41:52 2009 +0200 @@ -0,0 +1,707 @@ +/* +* Copyright (c) 2005 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: Image loader and scaler +* +*/ + + +#include "CCAImageLoader.h" +#include "MCAChatInterface.h" +#include "CCAContentMessage.h" + +#include "ChatDebugPrint.h" +#include "chatdebugassert.h" +#include "impsbuilddefinitions.h" + +#include "mcaimpsfactory.h" +#include "mcaimpsimclient.h" +#include "wvengutils.h" +#include "imnginternalcrkeys.h" +#include "imprivatecrkeys.h" +#include "imvariant.hrh" + +#include +#include +#include + +// CONSTANTS +const TInt KChatCustomBitmapWidth = 320; +const TInt KChatCustomBitmapHeight = 240; + +// From ICL documentation: +// "all decoder plugins also support thumbnail decodes with ratios of 1:2, 1:4 and 1:8." +const TInt KDecodeScaleRatioMin = 2; +const TInt KDecodeScaleRatioMax = 8; + + +//----------------------------------------------------------------------------- +// CCAImageLoader::CCAImageLoader +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +CCAImageLoader::CCAImageLoader( + MCAChatInterface& aChatInterface, MCAImpsFactory& aImpsFactory ) + : CActive( CActive::EPriorityLow ), + iChatInterface( aChatInterface ), + iImpsFactory( aImpsFactory ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::CCAImageLoader" ); + CActiveScheduler::Add( this ); + CHAT_DP_FUNC_DONE( "CCAImageLoader::CCAImageLoader" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::CCAImageLoader +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +CCAImageLoader::~CCAImageLoader() + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::~CCAImageLoader" ); + iFs.Close(); + + delete iDecoder; + delete iScaler; + delete iEncoder; + delete iBitmap; + delete iContent; + + iMessages.Close(); + + CHAT_DP_FUNC_DONE( "CCAImageLoader::~CCAImageLoader" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::NewL +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +CCAImageLoader* CCAImageLoader::NewL( MCAChatInterface& aChatInterface, + MCAImpsFactory& aImpsFactory ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::NewL" ); + CCAImageLoader* self = + new ( ELeave ) CCAImageLoader( aChatInterface, aImpsFactory ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + CHAT_DP_FUNC_DONE( "CCAImageLoader::NewL" ); + return self; + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::ConstructL +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::ConstructL() + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::ConstructL" ); + User::LeaveIfError( iFs.Connect() ); + + iSendNotScalable = WVEngUtils::CRKeyL( KCRUidIMVariation, KIMVariationKey ) + & EIMFeatSendNotScalable; + CHAT_DP_FUNC_DONE( "CCAImageLoader::ConstructL" ); + } + + +//----------------------------------------------------------------------------- +// CCAImageLoader::RunL +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::RunL() + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::RunL" ); + TInt status = iStatus.Int(); + CHAT_DP( D_CHAT_LIT( "Status %d" ), status ); + switch ( status ) + { + case KErrUnderflow: + { + if ( iDecoder ) + { + CHAT_DP( D_CHAT_LIT( "Continue converting." ) ); + iStatus = KRequestPending; + iDecoder->ContinueConvert( &iStatus ); + } + else + { + CompleteRequestL(); + } + break; + } + case KErrNone: + { + if ( iDecoder ) + { + delete iDecoder; + iDecoder = NULL; + switch ( iCurrentMessage->ContentProcessState() ) + { + case MCAMessage::EContentNotProcessed: + { + CHAT_DP( D_CHAT_LIT( "-> Start scaling." ) ); + // Decode ready -> Start scaling. + iCurrentMessage->SetProcessState( + MCAMessage::EBitmapDecoded ); + StartProcessingL( *iCurrentMessage ); + break; + } + case MCAMessage::EContentReady: + { + CHAT_DP( + D_CHAT_LIT( "-> Start scaling to thumbnail." ) ); + // Decode ready -> Start making thumbnail. + iCurrentMessage->SetProcessState( + MCAMessage::EContentDecoded ); + StartProcessingL( *iCurrentMessage ); + break; + } + default: + { + iStatus = KErrNotSupported; + CompleteRequestL(); + break; + } + } + + } + else if ( iScaler ) + { + // scaling ready -> start encoding + delete iScaler; + iScaler = NULL; + switch ( iCurrentMessage->ContentProcessState() ) + { + case MCAMessage::EBitmapDecoded: + { + CHAT_DP( D_CHAT_LIT( "-> Start encoding." ) ); + iCurrentMessage->SetProcessState( + MCAMessage::EBitmapScaled ); + StartProcessingL( *iCurrentMessage ); + break; + } + case MCAMessage::EContentDecoded: + { + CHAT_DP( D_CHAT_LIT( "Thumbnail ready." ) ); + CCAContentMessage* message = + static_cast< CCAContentMessage* >( iCurrentMessage ); + message->SetThumbnail( iBitmap ); + iBitmap = NULL; + iCurrentMessage->SetProcessState( + MCAMessage::EThumbnailReady ); + CompleteRequestL(); + break; + } + default: + { + iStatus = KErrNotSupported; + CompleteRequestL(); + break; + } + } + } + else if ( iEncoder ) + { + CHAT_DP( D_CHAT_LIT( "Content ready. -> Stop processing." ) ); + delete iEncoder; + iEncoder = NULL; + iCurrentMessage->SetProcessState( MCAMessage::EContentReady ); + iCurrentMessage->SetContentData( iContent ); + iContent = NULL; + delete iBitmap; + iBitmap = NULL; + CompleteRequestL(); + } + else + { + iStatus = KErrNotSupported; + CompleteRequestL(); + } + break; + } + case KErrNoMemory: + { + // RunError will handle this case + User::Leave( KErrNoMemory ); + } + default: + { + CompleteRequestL(); + break; + } + } + CHAT_DP_FUNC_DONE( "CCAImageLoader::RunL" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::DoCancel +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::DoCancel() + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::DoCancel" ); + + ClearState(); + iCurrentMessage->HandleProcessingComplete( *this, iStatus.Int() ); + CHAT_DP_FUNC_DONE( "CCAImageLoader::DoCancel" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::RunError +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +TInt CCAImageLoader::RunError( TInt aError ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::RunError" ); + if ( IsActive() ) + { + iStatus = aError; + Cancel(); + } + + if ( aError == KErrNoMemory ) + { + CActiveScheduler::Current()->Error( KErrNoMemory ); + } + + // Clean up and reset all pending messages + ClearState(); + TRAP_IGNORE( ResetProcessingL( aError ) ); + + CHAT_DP_FUNC_DONE( "CCAImageLoader::RunError" ); + return KErrNone; + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::RequestProcessing +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::RequestProcessingL( MCAMessage& aMessage ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::RequestProcessingL" ); + // Append to scheduler + iMessages.AppendL( &aMessage ); + LaunchProcessingL(); + CHAT_DP_FUNC_DONE( "CCAImageLoader::RequestProcessingL" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::StartProcessing +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::StartProcessingL( MCAMessage& aMessage ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::StartProcessingL" ); + // File -> bitmap + // bitmap -> scaled bitmap + // scaled bitmap -> encoded content + + iCurrentMessage = &aMessage; + switch ( aMessage.ContentProcessState() ) + { + case MCAMessage::EContentNotProcessed: // File -> bitmap + { + __CHAT_ASSERT_DEBUG( !iDecoder ); + iDecoder = CImageDecoder::FileNewL( iFs, aMessage.Text() ); + + TSize origSize( iDecoder->FrameInfo().iOverallSizeInPixels ); + ScaleSize( origSize, iTargetSize ); + TBool fullyScaleable( iDecoder->FrameInfo().iFlags & TFrameInfo::EFullyScaleable ); + + TInt frameCount = iDecoder->FrameCount(); + if ( frameCount > 1 && iSendNotScalable ) + { + // Animated gif, try to send file as it is + // Animation would be lost in decoding/scaling process + + delete iDecoder; // Delete -> unlocks image file + iDecoder = NULL; + + // Open file + RFile file; + User::LeaveIfError( file.Open( iFs, aMessage.Text(), EFileRead ) ); + CleanupClosePushL( file ); + + // Get file size + TInt fileSize = 0; + User::LeaveIfError( file.Size( fileSize ) ); + + // Check file size, if bigger than max transaction content -> leave + if ( fileSize > + iImpsFactory.CreateImClientL()->MaxTransactionContentLengthL() ) + { + User::Leave( KErrOverflow ); + } + + // Get file content + HBufC8* content = HBufC8::NewLC( fileSize ); + TPtr8 ptr( content->Des() ); + User::LeaveIfError( file.Read( ptr, fileSize ) ); + + // Set content to message, ownership is transferred + CleanupStack::Pop( content ); + CleanupStack::PopAndDestroy(); // file.Close() + iCurrentMessage->SetProcessState( MCAMessage::EContentReady ); + iCurrentMessage->SetContentData( content ); + + // Complete + CompleteRequestL(); + break; + } + + TSize decodeSize( DecodeSize( origSize, iTargetSize, fullyScaleable ) ); + + __CHAT_ASSERT_DEBUG( !iBitmap ); + iBitmap = new ( ELeave ) CFbsBitmap(); + User::LeaveIfError( iBitmap->Create( decodeSize, + iDecoder->FrameInfo().iFrameDisplayMode ) ); + iDecoder->Convert( &iStatus, *iBitmap ); + if ( !IsActive() ) + { + SetActive(); + } + break; + } + case MCAMessage::EBitmapDecoded: // scale bitmap to size 320x240 + { + TSize currentSize( iBitmap ? iBitmap->SizeInPixels() : iTargetSize ); + + if ( currentSize != iTargetSize ) + { + // bitmap exists and the size is wrong, + // we have to scale the bitmap + __CHAT_ASSERT_DEBUG( !iScaler ); + iScaler = CBitmapScaler::NewL(); + iScaler->Scale( &iStatus, *iBitmap, iTargetSize ); + if ( !IsActive() ) + { + SetActive(); + } + } + else + { + // the size is already correct => skip this step + aMessage.SetProcessState( MCAMessage::EBitmapScaled ); + StartProcessingL( aMessage ); + } + break; + } + case MCAMessage::EBitmapScaled: // encode bitmap for sending + { + __CHAT_ASSERT_DEBUG( !iEncoder ); + iEncoder = CImageEncoder::DataNewL( iContent, aMessage.MimeType() ); + iEncoder->Convert( &iStatus, *iBitmap ); + if ( !IsActive() ) + { + SetActive(); + } + break; + } + case MCAMessage::EContentReady: // decode bitmap for creating thumbnail + { + __CHAT_ASSERT_DEBUG( !iDecoder ); + iDecoder = CImageDecoder::DataNewL( iFs, aMessage.ContentData() ); + + CCAContentMessage* message = + static_cast< CCAContentMessage* >( iCurrentMessage ); + iThumbSize = message->ThumbnailSize(); + + TSize origSize( iDecoder->FrameInfo().iOverallSizeInPixels ); + TBool fullyScaleable( iDecoder->FrameInfo().iFlags & TFrameInfo::EFullyScaleable ); + + TSize decodeSize( DecodeSize( origSize, iThumbSize, fullyScaleable ) ); + + __CHAT_ASSERT_DEBUG( !iBitmap ); + iBitmap = new ( ELeave ) CFbsBitmap(); + User::LeaveIfError( iBitmap->Create( decodeSize, + iDecoder->FrameInfo().iFrameDisplayMode ) ); + iDecoder->Convert( &iStatus, *iBitmap ); + if ( !IsActive() ) + { + SetActive(); + } + break; + } + case MCAMessage::EContentDecoded: // scale bitmap to thumbnail size. + { + TSize currentSize( iBitmap ? iBitmap->SizeInPixels() : iThumbSize ); + + if ( currentSize != iThumbSize ) + { + // bitmap exists and the size is wrong, + // we have to scale the bitmap + __CHAT_ASSERT_DEBUG( !iScaler ); + iScaler = CBitmapScaler::NewL(); + iScaler->Scale( &iStatus, *iBitmap, iThumbSize ); + if ( !IsActive() ) + { + SetActive(); + } + } + else + { + // bitmap size is already correct, + // set the thumbnail and complete request + CCAContentMessage* message = + static_cast< CCAContentMessage* >( iCurrentMessage ); + message->SetThumbnail( iBitmap ); + iBitmap = NULL; + aMessage.SetProcessState( MCAMessage::EThumbnailReady ); + CompleteRequestL(); + } + break; + } +#ifdef RD_SEND_NOT_SUPPORTED_CONTENT + case MCAMessage::EContentNotSupported: + case MCAMessage::EContentNotSupportedDrm: + case MCAMessage::EContentCorrupted: + { + RFileReadStream fileStream; + User::LeaveIfError( fileStream.Open( iFs, + aMessage.Text(), + EFileRead ) ); + CleanupClosePushL( fileStream ); + TInt length = fileStream.Source()->SizeL(); + HBufC8* temp = HBufC8::NewLC( length ); + TPtr8 ptr( temp->Des() ); + fileStream.ReadL( ptr, length ); + CleanupStack::Pop( temp ); + iCurrentMessage->SetContentData( temp ); + CleanupStack::PopAndDestroy(); // fileStream + CompleteRequestL(); + break; + } +#endif //RD_SEND_NOT_SUPPORTED_CONTENT + default: + { + // State is not known for processing. Complete it. + CompleteRequestL(); + break; + } + } + CHAT_DP_FUNC_DONE( "CCAImageLoader::StartProcessingL" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::CancelProcessing +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::CancelProcessing( MCAMessage& aMessage ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::CancelProcessing" ); + if ( iCurrentMessage == &aMessage ) + { + if ( IsActive() ) + { + Cancel(); + } + iCurrentMessage = NULL; + } + CHAT_DP_FUNC_DONE( "CCAImageLoader::CancelProcessing" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::CancelProcessing +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::RemoveProcessingL( MCAMessage& aMessage ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::RemoveProcessingL" ); + // Cancel if message is processed at the moment. + CancelProcessing( aMessage ); + // Remove message from scheduler + TInt messageIndex( iMessages.Find( &aMessage ) ); + if ( messageIndex >= 0 ) + { + iMessages.Remove( messageIndex ); + LaunchProcessingL(); + } + CHAT_DP_FUNC_DONE( "CCAImageLoader::RemoveProcessingL" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::LaunchProcessing +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::LaunchProcessingL() + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::LaunchProcessingL" ); + TInt count( iMessages.Count() ); + if ( count > 0 && !IsActive() ) + { + TRAPD( error, StartProcessingL( *iMessages[ 0 ] ) ); + if ( error == KErrNoMemory ) + { + // if no memory: stop the whole process + TRAP_IGNORE( ResetProcessingL( error ) ); + } + else if ( error < 0 ) + { + iMessages[ 0 ]->SetProcessState( MCAMessage::EContentCorrupted ); + TInt ignore; + TRAP( ignore, CompleteRequestL() ); + User::Leave( error ); + } + } + CHAT_DP_FUNC_DONE( "CCAImageLoader::LaunchProcessingL" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::DecodeSize +// Scale to nearest (larger or equal) size with ratio 1:1, 1:2, 1:4 or 1:8 +//----------------------------------------------------------------------------- +TSize CCAImageLoader::DecodeSize( const TSize& aSize, const TSize& aTargetSize, + TBool aAnyRatio ) + { + if ( aAnyRatio || aSize == aTargetSize ) + { + // decoder can scale to any ratio or the size is already correct + return aTargetSize; + } + + // 1:1 is always valid ratio for decode scaling + TInt lastValidRatio( 1 ); + for ( TInt ratio( KDecodeScaleRatioMin ); ratio <= KDecodeScaleRatioMax; ratio <<= 1 ) + { + if ( aSize.iWidth % ratio + aSize.iHeight % ratio == 0 ) + { + // this ratio is valid + if ( aSize.iWidth / ratio < aTargetSize.iWidth || + aSize.iHeight / ratio < aTargetSize.iHeight ) + { + // the decoded size was smaller in some dimension, + // the last valid ratio should be used + break; + } + + // this scale ratio results to greater or equal size + lastValidRatio = ratio; + } + } + + // return the size scaled with correct ratio + return TSize( aSize.iWidth / lastValidRatio, + aSize.iHeight / lastValidRatio ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::ScaleSize +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +TBool CCAImageLoader::ScaleSize( const TSize& aSize, TSize& aNewSize ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::ScaleSize" ); + if ( Max( aSize.iHeight, aSize.iWidth ) > KChatCustomBitmapWidth ) + { + // scaling needed + if ( aSize.iHeight > aSize.iWidth ) + { + aNewSize.SetSize( KChatCustomBitmapHeight, KChatCustomBitmapWidth ); + } + else + { + aNewSize.SetSize( KChatCustomBitmapWidth, KChatCustomBitmapHeight ); + } + return ETrue; + } + + // old size is correct + aNewSize = aSize; + CHAT_DP_FUNC_DONE( "CCAImageLoader::ScaleSize" ); + return EFalse; + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::CompleteRequestL +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::CompleteRequestL() + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::CompleteRequestL" ); + iCurrentMessage->HandleProcessingComplete( *this, iStatus.Int() ); + TInt index = iMessages.Find( iCurrentMessage ); + if ( index >= 0 ) + { + iMessages.Remove( index ); + } + iCurrentMessage = NULL; + LaunchProcessingL(); + CHAT_DP_FUNC_DONE( "CCAImageLoader::CompleteRequestL" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::ResetProcessingL +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::ResetProcessingL( TInt aReason ) + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::ResetProcessingL" ); + // cancel operation + if ( IsActive() ) + { + iStatus = aReason; + Cancel(); + } + + // show note + if ( aReason == KErrNoMemory ) + { + CActiveScheduler::Current()->Error( KErrNoMemory ); + } + + // reset all pending messages + TInt count = iMessages.Count(); + for ( TInt i = count - 1; i >= 0; --i ) + { + iMessages[ i ]->SetProcessState( MCAMessage::EContentCorrupted ); + iMessages[ i ]->HandleProcessingComplete( *this, aReason ); + iMessages.Remove( i ); + } + CHAT_DP_FUNC_DONE( "CCAImageLoader::ResetProcessingL" ); + } + +//----------------------------------------------------------------------------- +// CCAImageLoader::ClearState +// ( Other items commented in header ) +//----------------------------------------------------------------------------- +void CCAImageLoader::ClearState() + { + CHAT_DP_FUNC_ENTER( "CCAImageLoader::ClearState" ); + if ( iScaler ) + { + CHAT_DP_TXT( "CCAImageLoader::ClearState - Cancel scaler" ); + iScaler->Cancel(); + delete iScaler; + iScaler = NULL; + } + if ( iDecoder ) + { + CHAT_DP_TXT( "CCAImageLoader::ClearState - Cancel decoder" ); + iDecoder->Cancel(); + delete iDecoder; + iDecoder = NULL; + } + if ( iEncoder ) + { + CHAT_DP_TXT( "CCAImageLoader::ClearState - Cancel encoder" ); + iEncoder->Cancel(); + delete iEncoder; + iEncoder = NULL; + } + if ( iBitmap ) + { + delete iBitmap; + iBitmap = NULL; + } + CHAT_DP_FUNC_DONE( "CCAImageLoader::ClearState" ); + } + +// End of file