/*
* 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 Viewer class.
*
*/
// INCLUDE FILES
#include "CIHLImageViewer.h"
#include "CIHLBitmap.h"
#include "CIHLBitmapProcessor.h"
#include "IHLImplementationIds.h"
#include "IHLDebugPrint.h" // Debug print
#include <IHLInterfaceIds.h>
#include <MIHLViewerObserver.h>
#include <MIHLFileImage.h>
#include <fbs.h>
#include <e32math.h>
// Private namespace for constants and functions
namespace
{
// Panic function
_LIT( KIHLPanicString, "IHLImageViewer" );
void Panic( TInt aPanicCode ) { User::Panic( KIHLPanicString, aPanicCode ); }
}
// ============================ MEMBER FUNCTIONS ===============================
// -----------------------------------------------------------------------------
//
// C++ constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
CIHLImageViewer::CIHLImageViewer( const TSize& aViewerSize,
MIHLFileImage& aSource,
MIHLBitmap& aDestination,
MIHLViewerObserver& aObserver,
const TUint32 aOptions )
:CTimer( EPriorityNormal ), iObserver( aObserver ),
iSource( aSource ), iDestination( aDestination ),
iOptions( aOptions ), iViewerSize( aViewerSize ), iSrcBitmapScaleFactor(1)
{
CActiveScheduler::Add( this );
iAnimDelay = 0;
iAnimLastFrameDrawTime = 0;
iAnimDrawStartFastTime = 0;
}
// -----------------------------------------------------------------------------
//
// Two-phased constructor.
// -----------------------------------------------------------------------------
CIHLImageViewer* CIHLImageViewer::NewL( const TSize& aViewerSize,
MIHLFileImage& aSource,
MIHLBitmap& aDestination,
MIHLViewerObserver& aObserver,
const TUint32 aOptions )
{
CIHLImageViewer* self = new( ELeave ) CIHLImageViewer(
aViewerSize, aSource, aDestination, aObserver, aOptions );
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop(); // self
return self;
}
// -----------------------------------------------------------------------------
//
// Symbian constructor can leave.
// -----------------------------------------------------------------------------
void CIHLImageViewer::ConstructL()
{
CTimer::ConstructL();
HAL::Get(HALData::EFastCounterFrequency, iTickPeriod);
iCacheSource = CIHLBitmap::NewL();
iCacheDestination = CIHLBitmap::NewL();
iProcessor = CIHLBitmapProcessor::NewL( iOptions );
// Initialize settings and start load (default is zoom to fit)
iSourceSize = iSource.Size();
CalculateZoomToFitRatio();
User::LeaveIfError( SetZoomRatio( iZoomToFitRatio ) );
}
// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
CIHLImageViewer::~CIHLImageViewer()
{
Cancel();
delete iProcessor;
delete iCacheDestination;
delete iCacheSource;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::Id
// -----------------------------------------------------------------------------
TIHLInterfaceType CIHLImageViewer::Type() const
{
return TIHLInterfaceType( KIHLInterfaceIdImageViewer, KIHLImplementationIdImageViewer );
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::IsAnimation
// -----------------------------------------------------------------------------
TBool CIHLImageViewer::IsAnimation() const
{
return iSource.IsAnimation();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SheduleNextFrame
// -----------------------------------------------------------------------------
void CIHLImageViewer::SheduleNextFrame(TTimeIntervalMicroSeconds32 aDelay)
{
TInt delay=aDelay.Int();
if (delay == 0) // 0 second deleay set in GIF file
{
iAnimDelay = TTimeIntervalMicroSeconds32(100000-iAnimLastFrameDrawTime);
}
else
{
iAnimDelay = delay - iAnimLastFrameDrawTime;
}
if (iAnimDelay.Int() <=0) //last frame drawn longer than animation frame interval.
{
iAnimDelay = KMinimumInterval;
}
iAnimInitFastTime=User::FastCounter(); //store the start time
HighRes(iAnimDelay);
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::Play
// -----------------------------------------------------------------------------
void CIHLImageViewer::Play()
{
if( iSource.IsAnimation() )
{
iIsPlaying = ETrue;
if( iViewerState == EInactive)
{
iViewerState = EAnimation;
SheduleNextFrame( iSource.AnimationFrameDelay( iAnimationIndex ) );
}
}
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::Stop
// -----------------------------------------------------------------------------
void CIHLImageViewer::Stop()
{
iIsPlaying = EFalse;
if( iViewerState == EAnimation )
{
Cancel();
}
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::IsPlaying
// -----------------------------------------------------------------------------
TBool CIHLImageViewer::IsPlaying() const
{
return iIsPlaying;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::AnimationFrameCount
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::AnimationFrameCount() const
{
return iSource.AnimationFrameCount();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::AnimationFrame
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::AnimationFrame() const
{
return iAnimationIndex;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetAnimationFrame
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SetAnimationFrame( TInt aFrameIndex )
{
// Public API, boundary check -> panic also on hardware
__ASSERT_ALWAYS( aFrameIndex >= 0 &&
aFrameIndex < iSource.AnimationFrameCount(), Panic( KErrArgument ) );
// Save new index and clear old source cache to force load new frame
iAnimationIndex = aFrameIndex;
iCacheSource->Reset();
// Start load
return ApplySettings();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetViewerSize
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SetViewerSize( const TSize& aViewerSize, int aSrcBitmapScaleFactor )
{
// Public API, negative value check -> panic also on hardware
__ASSERT_ALWAYS( aViewerSize.iWidth >= 0 &&
aViewerSize.iHeight >= 0, Panic( KErrArgument ) );
iSrcBitmapScaleFactor = aSrcBitmapScaleFactor;
// Save new viewer size
iViewerSize = aViewerSize;
// Recalculate source rect and destination size
CalculateSourceRectAndDestinationSize();
// Recalculate zoom to fit ratio
CalculateZoomToFitRatio();
// Start load
return ApplySettings();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::ViewerSize
// -----------------------------------------------------------------------------
TSize CIHLImageViewer::ViewerSize() const
{
return iViewerSize;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::MoveSourceRect
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::MoveSourceRect( TInt aDx, TInt aDy )
{
TInt dx( aDx );
TInt dy( aDy );
switch( iRotationAngle )
{
case ERotationAngle0:
{
break;
}
case ERotationAngle90:
{
dx = aDy;
dy = -aDx;
break;
}
case ERotationAngle180:
{
dx = -aDx;
dy = -aDy;
break;
}
case ERotationAngle270:
{
dx = -aDy;
dy = aDx;
break;
}
default:
{
// Internal state error, debug panic only
#ifdef _DEBUG
Panic( KErrGeneral );
#endif
}
}
if( iHorizontalMirroring )
{
dx = -dx;
}
if( iVerticalMirroring )
{
dy = -dy;
}
if( iSourceRect.iTl.iX + dx < 0 ||
iSourceRect.iBr.iX + dx > iSourceSize.iWidth ||
iSourceRect.iTl.iY + dy < 0 ||
iSourceRect.iBr.iY + dy > iSourceSize.iHeight )
{
return KErrArgument;
}
iSourceRect.Move( dx, dy );
// Start load
return ApplySettings();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetSourceRectPosition
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SetSourceRectPosition( const TPoint& aPosition )
{
TPoint newPosition( aPosition );
TSize sourceRectSize( iSourceRect.Size() );
switch( iRotationAngle )
{
case ERotationAngle0:
{
break;
}
case ERotationAngle90:
{
newPosition.iX = aPosition.iY;
newPosition.iY = iSourceSize.iHeight - sourceRectSize.iHeight - aPosition.iX;
break;
}
case ERotationAngle180:
{
newPosition.iX = iSourceSize.iWidth - sourceRectSize.iWidth - aPosition.iX;
newPosition.iY = iSourceSize.iHeight - sourceRectSize.iHeight - aPosition.iY;
break;
}
case ERotationAngle270:
{
newPosition.iX = iSourceSize.iWidth - sourceRectSize.iWidth - aPosition.iY;
newPosition.iY = aPosition.iX;
break;
}
default:
{
// Internal state error, debug panic only
#ifdef _DEBUG
Panic( KErrGeneral );
#endif
}
}
if( iHorizontalMirroring )
{
newPosition.iX = iSourceSize.iWidth - sourceRectSize.iWidth - newPosition.iX;
}
if( iVerticalMirroring )
{
newPosition.iY = iSourceSize.iHeight - sourceRectSize.iHeight - newPosition.iY;
}
if( newPosition.iX < 0 ||
newPosition.iX > ( iSourceSize.iWidth - sourceRectSize.iWidth ) ||
newPosition.iY < 0 ||
newPosition.iY > ( iSourceSize.iHeight - sourceRectSize.iHeight ) )
{
return KErrArgument;
}
iSourceRect = TRect( newPosition, sourceRectSize );
// Start load
return ApplySettings();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SourceRect
// -----------------------------------------------------------------------------
TRect CIHLImageViewer::SourceRect() const
{
TRect mirroredSourceRect( iSourceRect );
if( iHorizontalMirroring )
{
mirroredSourceRect.iTl.iX = iSourceSize.iWidth - iSourceRect.iBr.iX;
mirroredSourceRect.iBr.iX = iSourceSize.iWidth - iSourceRect.iTl.iX;
}
if( iVerticalMirroring )
{
mirroredSourceRect.iTl.iY = iSourceSize.iHeight - iSourceRect.iBr.iY;
mirroredSourceRect.iBr.iY = iSourceSize.iHeight - iSourceRect.iTl.iY;
}
TRect rotatedSourceRect( mirroredSourceRect );
switch( iRotationAngle )
{
case ERotationAngle0:
{
break;
}
case ERotationAngle90:
{
rotatedSourceRect.iTl.iX = iSourceSize.iHeight - mirroredSourceRect.iBr.iY;
rotatedSourceRect.iTl.iY = mirroredSourceRect.iTl.iX;
rotatedSourceRect.iBr.iX = iSourceSize.iHeight - mirroredSourceRect.iTl.iY;
rotatedSourceRect.iBr.iY = mirroredSourceRect.iBr.iX;
break;
}
case ERotationAngle180:
{
rotatedSourceRect.iTl.iX = iSourceSize.iWidth - mirroredSourceRect.iBr.iX;
rotatedSourceRect.iTl.iY = iSourceSize.iHeight - mirroredSourceRect.iBr.iY;
rotatedSourceRect.iBr.iX = iSourceSize.iWidth - mirroredSourceRect.iTl.iX;
rotatedSourceRect.iBr.iY = iSourceSize.iHeight - mirroredSourceRect.iTl.iY;
break;
}
case ERotationAngle270:
{
rotatedSourceRect.iTl.iX = mirroredSourceRect.iTl.iY;
rotatedSourceRect.iTl.iY = iSourceSize.iWidth - mirroredSourceRect.iBr.iX;
rotatedSourceRect.iBr.iX = mirroredSourceRect.iBr.iY;
rotatedSourceRect.iBr.iY = iSourceSize.iWidth - mirroredSourceRect.iTl.iX;
break;
}
default:
{
// Internal state error, debug panic only
#ifdef _DEBUG
Panic( KErrGeneral );
#endif
}
}
return rotatedSourceRect;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SourceSize
// -----------------------------------------------------------------------------
TSize CIHLImageViewer::SourceSize() const
{
if( iRotationAngle == ERotationAngle90 ||
iRotationAngle == ERotationAngle270 )
{
return TSize( iSourceSize.iHeight, iSourceSize.iWidth );
}
else
{
return iSourceSize;
}
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetZoomRatio
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SetZoomRatio( TReal aZoomRatio )
{
if( aZoomRatio <= 0 || Math::IsInfinite( aZoomRatio ) )
{
return KErrArgument;
}
iZoomRatio = aZoomRatio;
// Recalculate source rect and destination size
CalculateSourceRectAndDestinationSize();
// Start load
return ApplySettings();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::ZoomRatio
// -----------------------------------------------------------------------------
TReal CIHLImageViewer::ZoomRatio() const
{
return iZoomRatio;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::ZoomToFitRatio
// -----------------------------------------------------------------------------
TReal CIHLImageViewer::ZoomToFitRatio() const
{
return iZoomToFitRatio;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::RotateClockwise
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::RotateClockwise()
{
TInt rotationAngle( iRotationAngle + ERotationAngle90 );
if( rotationAngle > ERotationAngle270 )
{
rotationAngle = ERotationAngle0;
}
return SetRotationAngle( rotationAngle );
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::RotateCounterClockwise
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::RotateCounterClockwise()
{
TInt rotationAngle( iRotationAngle - ERotationAngle90 );
if( rotationAngle < ERotationAngle0 )
{
rotationAngle = ERotationAngle270;
}
return SetRotationAngle( rotationAngle );
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetRotationAngle
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SetRotationAngle( TInt aRotationAngle )
{
if( aRotationAngle != ERotationAngle0 &&
aRotationAngle != ERotationAngle90 &&
aRotationAngle != ERotationAngle180 &&
aRotationAngle != ERotationAngle270 )
{
return KErrArgument;
}
iRotationAngle = aRotationAngle;
// Recalculate source rect and destination size
CalculateSourceRectAndDestinationSize();
// Recalculate zoom to fit ratio
CalculateZoomToFitRatio();
// Start load
return ApplySettings();
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::RotationAngle
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::RotationAngle() const
{
return iRotationAngle;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetVerticalMirroring
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SetVerticalMirroring( TBool aValue )
{
if( iVerticalMirroring != aValue )
{
iVerticalMirroring = aValue;
// Start load
return ApplySettings();
}
return KErrNone;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::VerticalMirroring
// -----------------------------------------------------------------------------
TBool CIHLImageViewer::VerticalMirroring() const
{
return iVerticalMirroring;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetHorizontalMirroring
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SetHorizontalMirroring( TBool aValue )
{
if( iHorizontalMirroring != aValue )
{
iHorizontalMirroring = aValue;
return ApplySettings();
}
return KErrNone;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::HorizontalMirroring
// -----------------------------------------------------------------------------
TBool CIHLImageViewer::HorizontalMirroring() const
{
return iHorizontalMirroring;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SetFilter
// -----------------------------------------------------------------------------
void CIHLImageViewer::SetFilter( MIHLFilter* /*aFilter*/ )
{
// Not in use
}
// Private methods
// -----------------------------------------------------------------------------
// CIHLImageViewer::DoCancel
// -----------------------------------------------------------------------------
void CIHLImageViewer::DoCancel()
{
CTimer::DoCancel();
switch( iViewerState )
{
case ELoad:
{
// Cancel asynchronous source loading
iSource.CancelLoad();
iCacheSource->Reset();
break;
}
case EProcess:
{
// Cancel asynchronous processing
iProcessor->CancelProcess();
iCacheDestination->Reset();
break;
}
default:
{
break;
}
}
// Set state to inactive
iViewerState = EInactive;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::RunL
// -----------------------------------------------------------------------------
void CIHLImageViewer::RunL()
{
User::LeaveIfError( iStatus.Int() );
TInt err( KErrNone );
switch( iViewerState )
{
case ELoad:
{
// Async load successful; start async processing if needed
if( NeedAsyncProcess() )
{
err = AsyncProcess();
if( !err )
{
iViewerState = EProcess;
SetActive();
}
break;
}
else
{
iViewerState=EInactive;
// Flowtrough to EProcess if no error
err = SyncProcess();
if( err )
{
break;
}
}
}
case EProcess:
{
// Process successful; finish and check if animation is needed
err = Finish();
if( !err )
{
TInt64 currentTime = User::FastCounter();
if(iAnimDrawStartFastTime > 0)
{
iAnimLastFrameDrawTime=(currentTime-iAnimDrawStartFastTime)*1000000/iTickPeriod;
}
if( iIsPlaying )
{
iViewerState = EAnimation;
SheduleNextFrame(iSource.AnimationFrameDelay( iAnimationIndex ));
}
else
{
iViewerState = EInactive;
}
// Notify client
// NOTE! client may call any API method in this point!
iObserver.ViewerBitmapChangedL();
}
break;
}
case EAnimation:
{
// Check if still playing..
iAnimDrawStartFastTime = User::FastCounter();
if( iIsPlaying )
{
// Change animation frame + 1
UpdateAnimationIndex();
err = AsyncLoad();
if( !err )
{
iViewerState = ELoad;
SetActive();
}
else
{
iViewerState = EInactive;
}
}
else
{
iViewerState = EInactive;
}
break;
}
default:
{
// Internal state error, debug panic only
#ifdef _DEBUG
Panic( KErrGeneral );
#endif
}
}
User::LeaveIfError( err );
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::RunError
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::RunError( TInt aError )
{
switch( iViewerState )
{
case ELoad:
{
// Cleanup cached source if load has been failed
iCacheSource->Reset();
break;
}
case EProcess:
{
// Cleanup cached destination if process has been failed
iCacheDestination->Reset();
break;
}
default:
{
break;
}
}
iViewerState = EInactive;
iObserver.ViewerError( aError );
return KErrNone;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::Set
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::ApplySettings()
{
// Assert source rect and destination, debug build only
#ifdef _DEBUG
AssertSourceRectAndDestinationSize();
#endif
// Cancel current process if any
Cancel();
// Check if cached source bitmap is already valid for processing..
TBool validSourceCache( EFalse );
const CFbsBitmap& cacheSource = iCacheSource->Bitmap();
if( cacheSource.Handle() )
{
const TSize cacheSize( cacheSource.SizeInPixels() );
const TSize minLoadSize( MinimumSourceLoadSize() );
if( cacheSize.iWidth >= minLoadSize.iWidth &&
cacheSize.iHeight >= minLoadSize.iHeight )
{
validSourceCache = ETrue;
}
}
// Start async load/process..
TInt err( KErrNone );
if( validSourceCache )
{
if( NeedAsyncProcess() )
{
err = AsyncProcess();
if( !err )
{
iViewerState = EProcess;
SetActive();
}
}
else
{
err = SyncProcess();
if( !err )
{
iViewerState = EProcess;
SelfComplete();
}
}
}
else
{
err = AsyncLoad();
if( !err )
{
iViewerState = ELoad;
SetActive();
}
}
return err;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::AsyncLoad
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::AsyncLoad()
{
// Internal state check, debug panic only
__ASSERT_DEBUG( !iCacheDestination->Bitmap().Handle(), Panic( KErrGeneral ) );
TInt err( KErrNone );
if( !iSource.IsAnimation() )
{
// Do not reset animation frame because loading
// might be faster if previous frame can be utilized
iCacheSource->Reset();
}
if( !iCacheSource->Bitmap().Handle() )
{
TDisplayMode transparency( iSource.MaskDisplayMode() );
if( transparency )
{
err = iCacheSource->Create( MinimumSourceLoadSize(),
iSource.DisplayMode(),
transparency );
}
else
{
err = iCacheSource->Create( MinimumSourceLoadSize(),
iSource.DisplayMode() );
}
}
// Load source bitmap
if( !err )
{
if( iSource.IsAnimation() )
{
err = iSource.LoadAnimation( iStatus, *iCacheSource, iAnimationIndex );
}
else
{
err = iSource.Load( iStatus, *iCacheSource, iSource.ImageIndex() );
}
}
// Error cleanup if needed
if( err )
{
iCacheSource->Reset();
}
return err;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::NeedAsyncProcess
// -----------------------------------------------------------------------------
TBool CIHLImageViewer::NeedAsyncProcess()
{
// Internal state check, debug panic only
__ASSERT_DEBUG( iCacheSource->Bitmap().Handle(), Panic( KErrGeneral ) );
__ASSERT_DEBUG( !iCacheDestination->Bitmap().Handle(), Panic( KErrGeneral ) );
// First check if destination size differs from source size
// (=scaling needed)
TSize scaledSrcSize( iCacheSource->Bitmap().SizeInPixels() );
if( scaledSrcSize.iWidth != iDestinationSize.iWidth ||
scaledSrcSize.iHeight != iDestinationSize.iHeight )
{
return ETrue;
}
// Then check if source rect and source size differs
// (=clipping needed)
if( iSourceRect.iTl != TPoint( 0,0 ) ||
iSourceRect.iBr != iSource.Size().AsPoint() )
{
return ETrue;
}
// Finally check rotation and mirroring
if( iRotationAngle == ERotationAngle0 &&
!iHorizontalMirroring &&
!iVerticalMirroring )
{
return EFalse;
}
else if( iRotationAngle == ERotationAngle180 &&
iHorizontalMirroring &&
iVerticalMirroring )
{
return EFalse;
}
else
{
return ETrue;
}
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::AsyncProcess
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::AsyncProcess()
{
IHL_DEBUG1( KIHLDebug, "IHL - CIHLImageViewer - AsyncProcess" );
// Internal state check, debug panic only
__ASSERT_DEBUG( iCacheSource->Bitmap().Handle(), Panic( KErrGeneral ) );
__ASSERT_DEBUG( !iCacheDestination->Bitmap().Handle(), Panic( KErrGeneral ) );
const CFbsBitmap& srcBitmap = iCacheSource->Bitmap();
const CFbsBitmap& srcMask = iCacheSource->Mask();
TInt err( KErrNone );
// Create new cached destination
if( srcMask.Handle() )
{
err = iCacheDestination->Create( iDestinationSize,
srcBitmap.DisplayMode(), srcMask.DisplayMode() );
}
else
{
err = iCacheDestination->Create( iDestinationSize, srcBitmap.DisplayMode() );
}
// Scale and clip bitmap from cached source to new cached destination
if( !err )
{
TRect scaledSourceRect(
CalculateProcessSourceRect( srcBitmap.SizeInPixels() ) );
TRect destinationRect( CalculateProcessDestinationRect() );
err = iProcessor->Process( iStatus, *iCacheSource, scaledSourceRect,
*iCacheDestination, destinationRect );
}
// Error cleanup if needed
if( err )
{
iCacheDestination->Reset();
}
return err;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SyncProcess
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::SyncProcess()
{
IHL_DEBUG1( KIHLDebug, "IHL - CIHLImageViewer - SyncProcess" );
// Internal state check, debug panic only
__ASSERT_DEBUG( iCacheSource->Bitmap().Handle(), Panic( KErrGeneral ) );
__ASSERT_DEBUG( !iCacheDestination->Bitmap().Handle(), Panic( KErrGeneral ) );
// Duplicate destination cache to destination bitmap
TInt err( iCacheDestination->Copy( *iCacheSource, EFalse ) );
return err;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::Finish
// -----------------------------------------------------------------------------
TInt CIHLImageViewer::Finish()
{
// Internal state check, debug panic only
__ASSERT_DEBUG( iCacheDestination->Bitmap().Handle(), Panic( KErrGeneral ) );
// Duplicate destination cache to destination bitmap
TInt err( iDestination.Copy( *iCacheDestination, ETrue ) );
iCacheDestination->Reset();
// Error cleanup if needed
if( err )
{
iDestination.Reset();
}
return err;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::UpdateAnimationIndex
// -----------------------------------------------------------------------------
void CIHLImageViewer::UpdateAnimationIndex()
{
// Internal state check, debug panic only
__ASSERT_DEBUG( iIsPlaying, Panic( KErrGeneral ) );
// Check if animation is in last frame
if( iAnimationIndex == iSource.AnimationFrameCount() - 1 )
{
iAnimationIndex = 0;
}
else
{
// Not in last frame; move to next frame
iAnimationIndex++;
}
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::SelfComplete
// -----------------------------------------------------------------------------
void CIHLImageViewer::SelfComplete()
{
SetActive();
iStatus = KRequestPending;
TRequestStatus* status = &iStatus;
User::RequestComplete( status, KErrNone );
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::MinimumSourceLoadSize
// -----------------------------------------------------------------------------
TSize CIHLImageViewer::MinimumSourceLoadSize()
{
TSize minumumLoadSize( iSource.Size() );
if( iSource.IsFullyScaleable() )
{
const TSize originalSize( iSource.Size() );
switch( iRotationAngle )
{
case ERotationAngle0:
case ERotationAngle180:
{
minumumLoadSize.iWidth = originalSize.iWidth * iDestinationSize.iWidth / Abs( iSourceRect.Width() );
minumumLoadSize.iHeight = originalSize.iHeight * iDestinationSize.iHeight / Abs( iSourceRect.Height() );
break;
}
case ERotationAngle90:
case ERotationAngle270:
{
minumumLoadSize.iWidth = originalSize.iWidth * iDestinationSize.iWidth / Abs( iSourceRect.Height() );
minumumLoadSize.iHeight = originalSize.iHeight * iDestinationSize.iHeight / Abs( iSourceRect.Width() );
break;
}
default:
{
// Internal state error, debug panic only
#ifdef _DEBUG
Panic( KErrGeneral );
#endif
}
}
// Limit fully scaleable loadsize to original size
if( minumumLoadSize.iWidth > originalSize.iWidth ||
minumumLoadSize.iHeight > originalSize.iHeight )
{
minumumLoadSize = originalSize;
}
}
else
{
const RArray<TSize>& loadSizeArray = iSource.CustomLoadSizeArray();
const TInt count( loadSizeArray.Count() );
if( count )
{
TRect loadRect;
TSize loadRectSize;
TBool indexFound( EFalse );
for( TInt i( 0 ); ( i < count ) && !indexFound; ++i )
{
loadRect = CalculateProcessSourceRect( loadSizeArray[ i ] );
loadRectSize = loadRect.Size();
loadRectSize.iWidth = Abs( loadRectSize.iWidth );
loadRectSize.iHeight = Abs( loadRectSize.iHeight );
if( iDestinationSize.iWidth <= loadRectSize.iWidth &&
iDestinationSize.iHeight <= loadRectSize.iHeight )
{
minumumLoadSize = loadSizeArray[ i ];
indexFound = ETrue;
}
}
}
}
return minumumLoadSize;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::CalculateProcessSourceRect
// -----------------------------------------------------------------------------
TRect CIHLImageViewer::CalculateProcessSourceRect( const TSize& aLoadSize )
{
const TSize originalSize( iSource.Size() );
TRect loadRect;
if ( aLoadSize.iWidth > KErrNone && aLoadSize.iHeight > KErrNone )
{
loadRect.iTl.iX = iSourceRect.iTl.iX * aLoadSize.iWidth / originalSize.iWidth;
loadRect.iTl.iY = iSourceRect.iTl.iY * aLoadSize.iHeight / originalSize.iHeight;
loadRect.iBr.iX = iSourceRect.iBr.iX * aLoadSize.iWidth / originalSize.iWidth;
loadRect.iBr.iY = iSourceRect.iBr.iY * aLoadSize.iHeight / originalSize.iHeight;
}
else
{
loadRect.SetRect(0,0,0,0);
return loadRect;
}
switch( iRotationAngle )
{
case ERotationAngle0:
{
break;
}
case ERotationAngle90:
{
TInt tmp( loadRect.iTl.iY );
loadRect.iTl.iY = loadRect.iBr.iY;
loadRect.iBr.iY = tmp;
break;
}
case ERotationAngle180:
{
TPoint tmp( loadRect.iTl );
loadRect.iTl = loadRect.iBr;
loadRect.iBr = tmp;
break;
}
case ERotationAngle270:
{
TInt tmp( loadRect.iTl.iX );
loadRect.iTl.iX = loadRect.iBr.iX;
loadRect.iBr.iX = tmp;
break;
}
default:
{
// Internal state error, debug panic only
#ifdef _DEBUG
Panic( KErrGeneral );
#endif
}
}
return loadRect;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::CalculateProcessDestinationRect
// -----------------------------------------------------------------------------
TRect CIHLImageViewer::CalculateProcessDestinationRect()
{
TRect dstRect( iDestinationSize );
switch( iRotationAngle )
{
case ERotationAngle0:
case ERotationAngle180:
{
if( iHorizontalMirroring )
{
TInt tmp( dstRect.iTl.iX );
dstRect.iTl.iX = dstRect.iBr.iX;
dstRect.iBr.iX = tmp;
}
if( iVerticalMirroring )
{
TInt tmp( dstRect.iTl.iY );
dstRect.iTl.iY = dstRect.iBr.iY;
dstRect.iBr.iY = tmp;
}
break;
}
case ERotationAngle90:
case ERotationAngle270:
{
if( iHorizontalMirroring )
{
TInt tmp( dstRect.iTl.iY );
dstRect.iTl.iY = dstRect.iBr.iY;
dstRect.iBr.iY = tmp;
}
if( iVerticalMirroring )
{
TInt tmp( dstRect.iTl.iX );
dstRect.iTl.iX = dstRect.iBr.iX;
dstRect.iBr.iX = tmp;
}
break;
}
default:
{
// Internal state error, debug panic only
#ifdef _DEBUG
Panic( KErrGeneral );
#endif
}
}
return dstRect;
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::CalculateZoomToFitRatio
// -----------------------------------------------------------------------------
void CIHLImageViewer::CalculateZoomToFitRatio()
{
TSize sourceSize( SourceSize() );
TReal widthRatio( TReal( iViewerSize.iWidth ) / TReal( sourceSize.iWidth ) );
TReal heightRatio( TReal( iViewerSize.iHeight ) / TReal( sourceSize.iHeight ) );
if( iOptions & MIHLImageViewer::EOptionIgnoreAspectRatio )
{
iZoomToFitRatio = ( widthRatio > heightRatio ) ? widthRatio : heightRatio;
}
else
{
iZoomToFitRatio = ( widthRatio < heightRatio ) ? widthRatio : heightRatio;
}
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::CalculateSourceRectAndDestinationSize
// -----------------------------------------------------------------------------
void CIHLImageViewer::CalculateSourceRectAndDestinationSize()
{
// Calculate new source rect
TSize oldSourceRectSize( iSourceRect.Size() );
TReal newSourceRectWidth;
TReal newSourceRectHeight;
TReal widthZoomRatio( iZoomRatio );
TReal heightZoomRatio( iZoomRatio );
if( iOptions & MIHLImageViewer::EOptionIgnoreAspectRatio )
{
TReal widthRatio( TReal( iViewerSize.iWidth ) / TReal( iSourceSize.iWidth ) );
TReal heightRatio( TReal( iViewerSize.iHeight ) / TReal( iSourceSize.iHeight ) );
if( widthRatio < heightRatio )
{
widthZoomRatio = widthZoomRatio * widthRatio / heightRatio;
}
else
{
heightZoomRatio = heightZoomRatio * heightRatio / widthRatio;
}
}
if( iRotationAngle == ERotationAngle90 ||
iRotationAngle == ERotationAngle270 )
{
newSourceRectWidth = iSrcBitmapScaleFactor * iViewerSize.iHeight / heightZoomRatio; //widthZoomRatio
newSourceRectHeight = iSrcBitmapScaleFactor * iViewerSize.iWidth / widthZoomRatio; //heightZoomRatio
}
else
{
newSourceRectWidth = iSrcBitmapScaleFactor * iViewerSize.iWidth / widthZoomRatio;
newSourceRectHeight = iSrcBitmapScaleFactor * iViewerSize.iHeight / heightZoomRatio;
}
// Check if source rect is not larger than source area
if( newSourceRectWidth > iSourceSize.iWidth )
{
newSourceRectWidth = iSourceSize.iWidth;
}
if( newSourceRectHeight > iSourceSize.iHeight )
{
newSourceRectHeight = iSourceSize.iHeight;
}
// Rounding the results
TReal roundedWidth;
Math::Round( roundedWidth, newSourceRectWidth, 0 );
TReal roundedHeight;
Math::Round( roundedHeight, newSourceRectHeight, 0 );
iSourceRect.SetWidth( (TInt)roundedWidth );
iSourceRect.SetHeight( (TInt)roundedHeight );
// Calculate actual destination size (always same or smaller than iViewerSize !)
if( iRotationAngle == ERotationAngle90 ||
iRotationAngle == ERotationAngle270 )
{
iDestinationSize.iWidth = (TInt)( newSourceRectHeight * widthZoomRatio ); //heightZoomRatio
iDestinationSize.iHeight = (TInt)( newSourceRectWidth * heightZoomRatio ); //widthZoomRatio
}
else
{
iDestinationSize.iWidth = (TInt)( newSourceRectWidth * widthZoomRatio );
iDestinationSize.iHeight = (TInt)( newSourceRectHeight * heightZoomRatio );
}
// Check that destination size is not rounded to zero
if( iDestinationSize.iWidth == 0 )
{
iDestinationSize.iWidth = 1;
}
if( iDestinationSize.iHeight == 0 )
{
iDestinationSize.iHeight = 1;
}
// Move source rect keeping center point in same location
iSourceRect.Move( ( oldSourceRectSize.iWidth - (TInt)newSourceRectWidth ) / 2,
( oldSourceRectSize.iHeight - (TInt)newSourceRectHeight ) / 2 );
// Move rect if partially out of source area
TPoint moveOffset( 0, 0 );
if( iSourceRect.iTl.iX < 0 )
{
moveOffset.iX = -iSourceRect.iTl.iX;
}
else if( iSourceRect.iBr.iX > iSourceSize.iWidth )
{
moveOffset.iX = iSourceSize.iWidth - iSourceRect.iBr.iX;
}
if( iSourceRect.iTl.iY < 0 ) //lint !e961
{
moveOffset.iY = -iSourceRect.iTl.iY;
}
else if( iSourceRect.iBr.iY > iSourceSize.iHeight )
{
moveOffset.iY = iSourceSize.iHeight - iSourceRect.iBr.iY;
}
iSourceRect.Move( moveOffset ); //lint !e961
// Assert that rectangle is valid, debug build only
#ifdef _DEBUG
AssertSourceRectAndDestinationSize();
#endif
}
// -----------------------------------------------------------------------------
// CIHLImageViewer::AssertSourceRectAndDestinationSize
// Used in debug build only
// -----------------------------------------------------------------------------
#ifdef _DEBUG
void CIHLImageViewer::AssertSourceRectAndDestinationSize()
{
if( iSourceRect.iTl.iX < 0 ||
iSourceRect.iBr.iX > iSourceSize.iWidth ||
iSourceRect.iTl.iY < 0 ||
iSourceRect.iBr.iY > iSourceSize.iHeight ||
iDestinationSize.iWidth <= 0 || iDestinationSize.iHeight <= 0 )
{
Panic( KErrGeneral );
}
}
#endif
// End of File