videditor/VideoEditorCommon/src/VeiImageClipGenerator.cpp
changeset 0 951a5db380a0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/videditor/VideoEditorCommon/src/VeiImageClipGenerator.cpp	Fri Jan 29 14:08:33 2010 +0200
@@ -0,0 +1,999 @@
+/*
+* Copyright (c) 2010 Ixonos Plc.
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the "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:
+* Ixonos Plc
+*
+* Description:  
+*
+*/
+
+
+
+#include "VeiImageClipGenerator.h"
+
+#include <VedMovie.h>
+#include <fbs.h>
+#include <bitdev.h>
+#include <gdi.h>
+#include <aknutils.h>
+#include <ImageConversion.h>
+#include <BitmapTransforms.h>
+
+#define KMiddleFrameDuration TTimeIntervalMicroSeconds(1000000)
+
+const TInt KNumberOfTransitionFrames = 10;
+
+EXPORT_C CVeiImageClipGenerator* CVeiImageClipGenerator::NewL(const TDesC& aFilename,
+												     const TSize& aMaxResolution,
+													 const TTimeIntervalMicroSeconds& aDuration, 
+													 const TRgb& aBackgroundColor,
+													 TDisplayMode aMaxDisplayMode,
+													 RFs& aFs,
+													 MVeiImageClipGeneratorObserver& aObserver)
+	{
+	CVeiImageClipGenerator* self = 
+		CVeiImageClipGenerator::NewLC(aFilename, aMaxResolution, aDuration, aBackgroundColor, aMaxDisplayMode, aFs, aObserver);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+
+EXPORT_C CVeiImageClipGenerator* CVeiImageClipGenerator::NewLC(const TDesC& aFilename,
+													  const TSize& aMaxResolution,
+													  const TTimeIntervalMicroSeconds& aDuration,
+													  const TRgb& aBackgroundColor,
+													  TDisplayMode aMaxDisplayMode,
+													  RFs& aFs,
+													  MVeiImageClipGeneratorObserver& aObserver)
+	{
+	CVeiImageClipGenerator* self = new (ELeave) CVeiImageClipGenerator(aDuration, aBackgroundColor, aMaxResolution);
+	CleanupStack::PushL(self);
+	self->ConstructL(aFilename, aObserver, aMaxDisplayMode, aFs);
+	return self;
+	}
+
+
+CVeiImageClipGenerator::CVeiImageClipGenerator(const TTimeIntervalMicroSeconds& aDuration, 
+											   const TRgb& aBackgroundColor,
+											   const TSize& aMaxResolution)
+   : iReady(EFalse), iMaxResolution(aMaxResolution), iBackgroundColor(aBackgroundColor), iInitializing(ETrue)
+	{
+	__ASSERT_ALWAYS(iMaxResolution.iHeight >= 0, TVedPanic::Panic(TVedPanic::EImageClipGeneratorIllegalMaxResolution));
+	__ASSERT_ALWAYS(iMaxResolution.iWidth >= 0, TVedPanic::Panic(TVedPanic::EImageClipGeneratorIllegalMaxResolution));
+
+	SetDuration(aDuration);
+
+	iInitializing = EFalse;
+	}
+
+
+void CVeiImageClipGenerator::ConstructL(const TDesC& aFilename, 
+										MVeiImageClipGeneratorObserver& aObserver, 
+										TDisplayMode aMaxDisplayMode, RFs& aFs)
+	{
+	iDecodeOperation = CVeiImageClipDecodeOperation::NewL(*this, aFilename, aObserver, aFs);
+	iFrameOperation = CVeiImageClipFrameOperation::NewL(*this);
+	
+	iFilename = aFilename.AllocL();
+
+	TParse parse;
+	parse.Set(aFilename, 0, 0);
+	iDescriptiveName = parse.Name().AllocL();
+
+	iDecodeOperation->StartOperationL(iMaxResolution, aMaxDisplayMode);
+	}
+
+
+EXPORT_C CVeiImageClipGenerator::~CVeiImageClipGenerator()
+	{
+	delete iDecodeOperation;
+	delete iFrameOperation;
+	delete iDescriptiveName;
+	delete iBitmap;
+	delete iMask;
+	delete iFilename;
+	}
+
+
+EXPORT_C TPtrC CVeiImageClipGenerator::DescriptiveName() const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+
+	return *iDescriptiveName;
+	}
+
+
+EXPORT_C TUid CVeiImageClipGenerator::Uid() const
+	{
+	return KUidImageClipGenerator;
+	}
+
+EXPORT_C TTimeIntervalMicroSeconds CVeiImageClipGenerator::Duration() const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+
+	return iDuration;
+	}
+
+
+EXPORT_C TInt CVeiImageClipGenerator::VideoFrameCount() const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+
+	TInt frameCount = 0;
+
+	TInt maxFramerate = 10;
+	if (IsInserted()) 
+		{
+		maxFramerate = Movie()->MaximumFramerate();
+		}
+
+	TTimeIntervalMicroSeconds frameDuration(TInt64(1000000 / maxFramerate));
+	if (iDuration.Int64() < (TInt64(KNumberOfTransitionFrames * 2 + 1) * frameDuration.Int64()))
+		{
+		frameCount = (static_cast<TInt>(iDuration.Int64() / frameDuration.Int64()));
+		if ((iDuration.Int64() % frameDuration.Int64()) != 0)
+			{
+			frameCount++;
+			}
+		}
+	else
+		{
+		frameCount = KNumberOfTransitionFrames * 2; 
+		TTimeIntervalMicroSeconds middleTime(iDuration.Int64() - (TInt64(KNumberOfTransitionFrames * 2) * frameDuration.Int64()));
+		frameCount += (static_cast<TInt32>(middleTime.Int64() / KMiddleFrameDuration.Int64()));
+		if ((middleTime.Int64() % KMiddleFrameDuration.Int64()) != 0)
+			{
+			frameCount++;
+			}
+		}
+	return frameCount;
+	}
+
+
+EXPORT_C TTimeIntervalMicroSeconds CVeiImageClipGenerator::VideoFrameStartTime(TInt aIndex) const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < VideoFrameCount(), TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalVideoFrameIndex));
+	
+	TInt maxFramerate = 10;
+	if (IsInserted()) 
+		{
+		maxFramerate = Movie()->MaximumFramerate();
+		}
+
+	TTimeIntervalMicroSeconds frameDuration(TInt64(1000000 / maxFramerate));
+	TTimeIntervalMicroSeconds finalThreshold(iDuration.Int64() - TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+	TTimeIntervalMicroSeconds startThreshold(TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+	TTimeIntervalMicroSeconds startTime(-1);
+	TInt frameCount = VideoFrameCount();
+
+
+	if (frameCount < (KNumberOfTransitionFrames * 2 + 1))
+		{		
+		// Special case: less than KNumberOfTransitionFrames frames in the movie
+		startTime = TTimeIntervalMicroSeconds(TInt64(aIndex) * frameDuration.Int64());
+		}
+	else if (aIndex < KNumberOfTransitionFrames) 
+		{
+		// Start frames
+		startTime = TTimeIntervalMicroSeconds(TInt64(aIndex) * frameDuration.Int64());
+		}
+	else if (aIndex >= (frameCount - KNumberOfTransitionFrames))
+		{
+		// End frames
+		startTime = TTimeIntervalMicroSeconds(
+			static_cast<TInt64>((aIndex - frameCount) + KNumberOfTransitionFrames) 
+			* frameDuration.Int64() + finalThreshold.Int64() );
+		}
+	else  
+		{
+		// Middle frames
+		startTime = TTimeIntervalMicroSeconds(startThreshold.Int64() 
+			+ TInt64(aIndex- KNumberOfTransitionFrames) * KMiddleFrameDuration.Int64());
+		}
+
+	return startTime;
+	}
+
+
+EXPORT_C TTimeIntervalMicroSeconds CVeiImageClipGenerator::VideoFrameEndTime(TInt aIndex) const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < VideoFrameCount(), TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalVideoFrameIndex));
+	
+	if (aIndex == VideoFrameCount() - 1) 
+		{
+		return iDuration;
+		}
+
+	TInt maxFramerate = 10;
+	if (IsInserted()) 
+		{
+		maxFramerate = Movie()->MaximumFramerate();
+		}
+
+	TTimeIntervalMicroSeconds frameDuration(TInt64(1000000 / maxFramerate));
+	TTimeIntervalMicroSeconds finalThreshold(iDuration.Int64() - TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+	TTimeIntervalMicroSeconds startThreshold(TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+	TTimeIntervalMicroSeconds endTime(-1);
+
+
+	TInt frameCount = VideoFrameCount();
+
+	if (frameCount < (KNumberOfTransitionFrames * 2 + 1))
+		{
+		// Special case: less than KNumberOfTransitionFrames frames in the movie
+		endTime = TTimeIntervalMicroSeconds(TInt64(aIndex + 1) * frameDuration.Int64());
+		}
+	else if (aIndex < KNumberOfTransitionFrames) 
+		{
+		// start frames
+		endTime = TTimeIntervalMicroSeconds(TInt64(aIndex + 1) * frameDuration.Int64());
+		}
+	else if (aIndex > (frameCount - KNumberOfTransitionFrames))
+		{
+		// end frames
+		endTime = TTimeIntervalMicroSeconds(TInt64((aIndex - frameCount) 
+			+ KNumberOfTransitionFrames) * frameDuration.Int64() 
+			+ finalThreshold.Int64());
+		}
+	else  
+		{
+		// middle frames
+		endTime = TTimeIntervalMicroSeconds(startThreshold.Int64() 
+			+ TInt64(aIndex - (KNumberOfTransitionFrames - 1)) 
+			* KMiddleFrameDuration.Int64());
+
+		if (endTime.Int64() >= finalThreshold.Int64())
+			{
+			// last of the middle frames may be shorter than normal
+			endTime = finalThreshold;
+			}
+		}
+	return endTime;
+	}
+
+
+EXPORT_C TTimeIntervalMicroSeconds CVeiImageClipGenerator::VideoFrameDuration(TInt aIndex) const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < VideoFrameCount(), TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalVideoFrameIndex));
+
+
+	// check maximum framerate
+	TInt maxFramerate = 10;
+	if (IsInserted()) 
+		{
+		maxFramerate = Movie()->MaximumFramerate();
+		}
+
+	// calculate some timing values. 
+	TTimeIntervalMicroSeconds frameDuration(TInt64(1000000 / maxFramerate));
+	TTimeIntervalMicroSeconds finalThreshold(iDuration.Int64() - TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+	TTimeIntervalMicroSeconds startThreshold(TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+
+
+	TInt frameCount = VideoFrameCount();
+	TInt finalThresholdIndex = GetVideoFrameIndex(finalThreshold);
+
+	if ((frameCount < (KNumberOfTransitionFrames * 2 + 1)) && (aIndex == (frameCount - 1)))
+		{
+		// Special case: short clip with only frames that have max framerate 
+		// - all of the frames are of equal duration (frameDuration) except 
+		// the last one.
+		frameDuration = TTimeIntervalMicroSeconds(iDuration.Int64() - (TInt64(frameCount - 1) * frameDuration.Int64()));
+		}
+	else if (aIndex >= KNumberOfTransitionFrames && aIndex < finalThresholdIndex) 
+		{
+		if (aIndex == (finalThresholdIndex - 1)) 
+			{
+			// Last one of the middle frames
+			frameDuration = TTimeIntervalMicroSeconds(finalThreshold.Int64() - VideoFrameStartTime(aIndex).Int64());
+			}
+		else
+			{
+			// Ordinary middle frame
+			frameDuration = KMiddleFrameDuration;
+			}
+		}
+	return frameDuration;
+	}
+
+EXPORT_C TBool CVeiImageClipGenerator::VideoFrameIsIntra(TInt aIndex) const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	if (aIndex == 0) 
+		{
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+EXPORT_C TInt CVeiImageClipGenerator::VideoFirstFrameComplexityFactor() const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	return iFirstFrameComplexityFactor;
+	}
+
+EXPORT_C TInt CVeiImageClipGenerator::VideoFrameDifferenceFactor(TInt aIndex) const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < VideoFrameCount(), TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalVideoFrameIndex));
+
+	if (aIndex == 0) 
+		{
+		return 1000;
+		}
+	else 
+		{
+		return 0;
+		}
+	}
+
+
+EXPORT_C TInt CVeiImageClipGenerator::GetVideoFrameIndex(TTimeIntervalMicroSeconds aTime) const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	__ASSERT_ALWAYS(aTime.Int64() >= 0, TVedPanic::Panic(TVedPanic::EVideoClipInfoIllegalVideoFrameTime));
+	__ASSERT_ALWAYS(aTime.Int64() <= iDuration.Int64(), TVedPanic::Panic(TVedPanic::EVideoClipInfoIllegalVideoFrameTime));
+
+	TInt index = -1;
+
+	TInt maxFramerate = 10;
+	if (IsInserted()) 
+		{
+		maxFramerate = Movie()->MaximumFramerate();
+		}
+
+	TTimeIntervalMicroSeconds frameDuration(TInt64(1000000 / maxFramerate));
+	TTimeIntervalMicroSeconds finalThreshold(
+		iDuration.Int64() - TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+	TTimeIntervalMicroSeconds startThreshold(
+		TInt64(KNumberOfTransitionFrames) * frameDuration.Int64());
+
+	if (iDuration <= (TInt64(KNumberOfTransitionFrames * 2) * frameDuration.Int64())) 
+		{
+		index = static_cast<TInt>(aTime.Int64() / frameDuration.Int64());
+		}
+	else if (aTime < startThreshold) 
+		{
+		index = static_cast<TInt>(aTime.Int64() / frameDuration.Int64());
+		}
+	else if (aTime >= finalThreshold) 
+		{
+		TTimeIntervalMicroSeconds middleDuration(finalThreshold.Int64() - startThreshold.Int64());
+		TInt numberOfMiddleFrames = 
+		    static_cast<TInt32>(middleDuration.Int64() / KMiddleFrameDuration.Int64());
+		if (middleDuration.Int64() % KMiddleFrameDuration.Int64() != 0) 
+			{
+			numberOfMiddleFrames++;
+			}
+
+		index = KNumberOfTransitionFrames + numberOfMiddleFrames 
+			+ static_cast<TInt>((aTime.Int64() - finalThreshold.Int64()) / frameDuration.Int64()); 
+		}
+	else  
+		{
+		index = KNumberOfTransitionFrames 
+			+ static_cast<TInt>((aTime.Int64() - startThreshold.Int64()) / KMiddleFrameDuration.Int64());
+		}
+	
+	return index;
+	}
+
+
+EXPORT_C void CVeiImageClipGenerator::GetFrameL(MVedVideoClipGeneratorFrameObserver& aObserver,
+								  TInt aIndex, TSize* const aResolution,
+								  TDisplayMode aDisplayMode, TBool aEnhance,
+								  TInt aPriority)
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));
+	__ASSERT_ALWAYS((aIndex >= 0  && aIndex < VideoFrameCount()) || 
+					 aIndex == KFrameIndexBestThumb, 
+					TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalVideoFrameIndex));
+	__ASSERT_ALWAYS((aResolution->iHeight <= iMaxResolution.iHeight &&
+					 aResolution->iWidth  <= iMaxResolution.iWidth), 
+					TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalFrameResolution));
+	__ASSERT_ALWAYS((aResolution->iHeight >= 0 && aResolution->iWidth  >= 0), 
+					TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalFrameResolution));
+	
+
+    TDisplayMode displayMode = aDisplayMode;
+
+    // check validity of thumbnail and associated operation
+    if(aEnhance)	// for saving to file
+        {
+        if(displayMode == ENone)					// if no preference
+			{
+            displayMode = EColor16M;				// 24-bit color image for enhancement
+			}
+        else if(displayMode != EColor16M)	// invalid combination
+			{
+            User::Leave(KErrNotSupported);
+			}
+        }
+    else								// for screen display
+        {
+        if(displayMode == ENone)					// if no preference
+			{
+            displayMode = EColor64K;				// 16-bit image 
+			}
+        }
+    
+    CFbsBitmap* destBitmap = new (ELeave) CFbsBitmap;
+	CleanupStack::PushL(destBitmap);
+	User::LeaveIfError(destBitmap->Create(*aResolution, displayMode));
+	CleanupStack::Pop(destBitmap);
+
+	iFrameOperation->StartOperationL(&aObserver, aIndex, aEnhance, iBitmap,
+		destBitmap, iMask, aPriority);
+	}
+
+
+EXPORT_C void CVeiImageClipGenerator::CancelFrame()
+	{
+	iFrameOperation->Cancel();
+	}
+
+	
+EXPORT_C void CVeiImageClipGenerator::SetDuration(const TTimeIntervalMicroSeconds& aDuration)
+	{
+	__ASSERT_ALWAYS(iReady || iInitializing,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));	
+	__ASSERT_ALWAYS(aDuration.Int64() > 0, 
+		TVedPanic::Panic(TVedPanic::EVideoClipGeneratorIllegalDuration));
+
+	iDuration = aDuration;
+
+	if (!iInitializing) 
+		{
+		ReportDurationChanged();
+		}
+	}
+
+
+EXPORT_C void CVeiImageClipGenerator::SetBackgroundColor(const TRgb& aBackgroundColor)
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));	
+
+	iBackgroundColor = aBackgroundColor;
+	ReportSettingsChanged();
+	}
+
+EXPORT_C const TRgb& CVeiImageClipGenerator::BackgroundColor() const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));	
+
+	return iBackgroundColor;
+	}
+
+EXPORT_C TPtrC CVeiImageClipGenerator::ImageFilename() const
+	{
+	__ASSERT_ALWAYS(iReady,
+		TVedPanic::Panic(TVedPanic::EImageClipGeneratorNotReady));	
+	return *iFilename;
+	}
+
+void CVeiImageClipGenerator::UpdateFirstFrameComplexityFactorL()
+	{
+	iFirstFrameComplexityFactor = CalculateFrameComplexityFactor(iBitmap);
+	}
+
+//////////////////////////////////////////////////////////////////////////
+//  Decode operation
+//////////////////////////////////////////////////////////////////////////
+
+
+CVeiImageClipDecodeOperation* CVeiImageClipDecodeOperation::NewL(CVeiImageClipGenerator& aGenerator, 
+																 const TDesC& aFilename, 
+																 MVeiImageClipGeneratorObserver& aObserver,
+																 RFs& aFs,
+																 TInt aPriority)
+	{
+    CVeiImageClipDecodeOperation* self = 
+		new (ELeave) CVeiImageClipDecodeOperation(aGenerator, aObserver, aPriority);
+    CleanupStack::PushL(self);
+    self->ConstructL(aFilename, aFs);
+    CleanupStack::Pop(self);
+    return self;	
+	}
+
+
+CVeiImageClipDecodeOperation::CVeiImageClipDecodeOperation(CVeiImageClipGenerator& aGenerator, 
+														   MVeiImageClipGeneratorObserver& aObserver,
+														   TInt aPriority)
+  : CActive(aPriority), iGenerator(aGenerator), iObserver(aObserver)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+
+void CVeiImageClipDecodeOperation::ConstructL(const TDesC& aFilename, RFs& aFs)
+	{
+	iDecoder = CImageDecoder::FileNewL(aFs, aFilename);
+	}
+
+
+CVeiImageClipDecodeOperation::~CVeiImageClipDecodeOperation()
+	{
+	Cancel();
+
+	delete iDecoder;
+	iDecoder = 0;
+	delete iBitmap;
+	iBitmap = 0;
+	delete iMask;
+	iMask = 0;
+	}
+
+
+void CVeiImageClipDecodeOperation::DoCancel()
+	{
+	if (iDecoder) 
+		{
+		iDecoder->Cancel();
+		}
+
+	delete iDecoder;
+	iDecoder = 0;
+
+	delete iBitmap;
+	iBitmap = 0;
+
+	delete iMask;
+	iMask = 0;
+
+	iObserver.NotifyImageClipGeneratorInitializationComplete(iGenerator, KErrCancel);
+	}
+
+
+void CVeiImageClipDecodeOperation::RunL()
+	{
+	/* Transfer ownership of iBitmap to generator. */
+	iGenerator.iBitmap = iBitmap;
+	iBitmap = 0;
+	iGenerator.iMask = iMask;
+	iMask = 0;
+	iGenerator.iReady = ETrue;
+	iGenerator.UpdateFirstFrameComplexityFactorL();
+
+	/* Notify observer. */
+	iObserver.NotifyImageClipGeneratorInitializationComplete(iGenerator, KErrNone);
+	delete iDecoder;
+	iDecoder = 0;
+	}
+
+TInt CVeiImageClipDecodeOperation::RunError(TInt aError)
+	{
+	if (iDecoder) 
+		{
+		iDecoder->Cancel();
+		}
+	delete iDecoder;
+	iDecoder = 0;
+	delete iBitmap;
+	iBitmap = 0;
+	delete iMask;
+	iMask = 0;
+
+	iObserver.NotifyImageClipGeneratorInitializationComplete(iGenerator, aError);
+	return KErrNone;
+	}
+
+
+
+void CVeiImageClipDecodeOperation::StartOperationL(const TSize& aMaxResolution, TDisplayMode aDisplayMode)
+	{
+	__ASSERT_ALWAYS(!IsActive(), TVedPanic::Panic(TVedPanic::EInternal));
+
+	const TFrameInfo& info = iDecoder->FrameInfo();
+	TSize targetResolution(0, 0);
+	const TSize sourceResolution(info.iOverallSizeInPixels);
+
+	/* Calculate resolution. */
+
+	if ((sourceResolution.iWidth <= aMaxResolution.iWidth) 
+		&& (sourceResolution.iHeight <= aMaxResolution.iHeight))
+		{
+		targetResolution.iWidth = sourceResolution.iWidth;
+		targetResolution.iHeight = sourceResolution.iHeight;
+		}
+	else if (info.iFlags & TFrameInfo::EFullyScaleable) 
+		{
+		if ((sourceResolution.iWidth * aMaxResolution.iHeight) > 
+			(sourceResolution.iHeight * aMaxResolution.iWidth))
+			{
+			targetResolution.iWidth = aMaxResolution.iWidth;
+			targetResolution.iHeight = 
+				(targetResolution.iWidth * sourceResolution.iHeight) / sourceResolution.iWidth;
+			}
+		else
+			{
+			targetResolution.iHeight = aMaxResolution.iHeight;
+			targetResolution.iWidth = 
+				(targetResolution.iHeight * sourceResolution.iWidth) / sourceResolution.iHeight;
+			}
+		}
+	else 
+		{
+		targetResolution.iWidth = (sourceResolution.iWidth / 8) + 1;
+		targetResolution.iHeight = (sourceResolution.iHeight / 8) + 1;
+		
+		if ((targetResolution.iWidth < aMaxResolution.iWidth) 
+			&& (targetResolution.iHeight < aMaxResolution.iHeight))
+			{
+			targetResolution.iWidth = (sourceResolution.iWidth / 4) + 1;
+			targetResolution.iHeight = (sourceResolution.iHeight / 4) + 1;
+			}
+
+		if ((targetResolution.iWidth < aMaxResolution.iWidth) 
+			&& (targetResolution.iHeight < aMaxResolution.iHeight))
+			{
+			targetResolution.iWidth = (sourceResolution.iWidth / 2) + 1;
+			targetResolution.iHeight = (sourceResolution.iHeight / 2) + 1;
+			}
+
+		if ((targetResolution.iWidth < aMaxResolution.iWidth) 
+			&& (targetResolution.iHeight < aMaxResolution.iHeight))
+			{
+			targetResolution.iWidth = (sourceResolution.iWidth);
+			targetResolution.iHeight = (sourceResolution.iHeight);
+			}
+		}
+
+
+	iBitmap = new (ELeave) CFbsBitmap;
+	TInt err = iBitmap->Create(targetResolution, aDisplayMode);
+
+	if (err != KErrNone) 
+		{
+		delete iBitmap;
+		iBitmap = 0;
+		iObserver.NotifyImageClipGeneratorInitializationComplete(iGenerator, err);
+		return;
+		}
+
+	if (info.iFlags & TFrameInfo::ETransparencyPossible)
+		{
+		iMask = new (ELeave) CFbsBitmap;
+		if (info.iFlags & TFrameInfo::EAlphaChannel) 
+			{
+			err = iMask->Create(targetResolution, EGray256);
+			}
+		else 
+			{
+			err = iMask->Create(targetResolution, EGray2);
+			}
+		}
+	
+	if (err != KErrNone) 
+		{
+		delete iBitmap;
+		iBitmap = 0;
+		delete iMask;
+		iMask = 0;
+		iObserver.NotifyImageClipGeneratorInitializationComplete(iGenerator, err);
+		return;
+		}
+
+	if (iMask != 0)
+		{
+		iDecoder->Convert(&iStatus, *iBitmap, *iMask);
+		}
+	else
+		{
+		iDecoder->Convert(&iStatus, *iBitmap);
+		}
+
+	SetActive();
+	}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Frame operation
+//////////////////////////////////////////////////////////////////////////
+
+CVeiImageClipFrameOperation* CVeiImageClipFrameOperation::NewL(CVeiImageClipGenerator& aGenerator)
+    {
+    CVeiImageClipFrameOperation* self = 
+		new (ELeave) CVeiImageClipFrameOperation(aGenerator);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+
+CVeiImageClipFrameOperation::CVeiImageClipFrameOperation(CVeiImageClipGenerator& aGenerator)
+		: CActive(EPriorityStandard), iGenerator(aGenerator)
+    {
+	CActiveScheduler::Add(this);
+	}
+
+
+void CVeiImageClipFrameOperation::ConstructL()
+    {
+	}
+
+
+CVeiImageClipFrameOperation::~CVeiImageClipFrameOperation()
+    {
+	Cancel();
+	delete iScaler;
+	iScaler = 0;
+	delete iDestBitmap;
+	iDestBitmap = 0;
+	delete iScaledBitmap;
+	iScaledBitmap = 0;
+	delete iScaledMask;
+	iScaledMask = 0;
+
+	iSourceBitmap = 0;
+	iSourceMask = 0;
+	iObserver = 0;
+    }
+
+
+void CVeiImageClipFrameOperation::StartOperationL(MVedVideoClipGeneratorFrameObserver* aObserver,
+												 TInt aIndex, TBool aEnhance, 
+												 CFbsBitmap* aSourceBitmap, CFbsBitmap* aDestBitmap, 
+												 CFbsBitmap* aSourceMask, TInt aPriority)
+	{
+	__ASSERT_ALWAYS(!IsActive(), TVedPanic::Panic(TVedPanic::EImageClipGeneratorFrameOperationAlreadyRunning));
+
+	iObserver = aObserver;
+	iSourceBitmap = aSourceBitmap;
+	iDestBitmap = aDestBitmap;
+	iSourceMask = aSourceMask;
+	iIndex = aIndex;
+	iEnhance = aEnhance;
+
+	SetPriority(aPriority);
+
+
+	TSize sourceRes = iSourceBitmap->SizeInPixels();
+	TSize destRes = iDestBitmap->SizeInPixels();
+	TSize movieRes = iGenerator.Movie()->Resolution();
+
+	TSize imageResInMovie(0,0);
+	if ((sourceRes.iWidth >= movieRes.iWidth) || (sourceRes.iHeight >= movieRes.iHeight)) 
+		{
+		// Downscaling
+		if ((sourceRes.iWidth * movieRes.iHeight) > 
+			(sourceRes.iHeight * movieRes.iWidth))
+			{
+			imageResInMovie.iWidth = movieRes.iWidth;
+			imageResInMovie.iHeight =
+				(movieRes.iWidth * sourceRes.iHeight) / sourceRes.iWidth;
+			}
+		else 
+			{
+			imageResInMovie.iHeight = movieRes.iHeight;
+			imageResInMovie.iWidth = 
+				(movieRes.iHeight * sourceRes.iWidth) / sourceRes.iHeight;
+			}
+		}
+	else
+		{
+		// Upscaling - limit to a factor of two
+		if ((sourceRes.iWidth * movieRes.iHeight) > 
+			(sourceRes.iHeight * movieRes.iWidth))
+			{
+			imageResInMovie.iWidth = Min(movieRes.iWidth, (sourceRes.iWidth * 2));
+			imageResInMovie.iHeight = (imageResInMovie.iWidth * sourceRes.iHeight) / sourceRes.iWidth;
+			}
+		else 
+			{
+			imageResInMovie.iHeight = Min((sourceRes.iHeight * 2), movieRes.iHeight);
+			imageResInMovie.iWidth = (imageResInMovie.iHeight * sourceRes.iWidth) / sourceRes.iHeight;
+			}
+		}
+
+	TSize movieResInDestBitmap(-1,-1);
+	if ((movieRes.iWidth * destRes.iHeight) > 
+		(movieRes.iHeight * destRes.iWidth))
+		{
+		movieResInDestBitmap.iWidth = destRes.iWidth;
+		movieResInDestBitmap.iHeight =
+			(movieResInDestBitmap.iWidth * movieRes.iHeight) / movieRes.iWidth;
+		}
+	else 
+		{
+		movieResInDestBitmap.iHeight = destRes.iHeight;
+		movieResInDestBitmap.iWidth = 
+			(movieResInDestBitmap.iHeight * movieRes.iWidth) / movieRes.iHeight;
+		}
+	
+
+	TSize targetRes(imageResInMovie);
+	targetRes.iWidth = imageResInMovie.iWidth * movieResInDestBitmap.iWidth / movieRes.iWidth;
+	targetRes.iHeight = imageResInMovie.iHeight * movieResInDestBitmap.iHeight / movieRes.iHeight;
+
+	TSize cachedRes(-1, -1);
+	if (iScaledBitmap) 
+		{
+		cachedRes = iScaledBitmap->SizeInPixels();
+		}
+
+	/* Check if we already have scaled this bitmap.*/
+	if ((cachedRes.iWidth == targetRes.iWidth) || 
+		(cachedRes.iHeight == targetRes.iHeight))
+		{
+		SetActive();
+		TRequestStatus* status = &iStatus;
+		User::RequestComplete(status, KErrNone);
+		return;
+		}
+	else if (iScaledBitmap) 
+		{
+		delete iScaledBitmap;
+		iScaledBitmap = 0;
+		delete iScaledMask;
+		iScaledMask = 0;
+		}
+
+	delete iScaler;
+	iScaler = NULL;
+	iScaler = CBitmapScaler::NewL();
+
+	iScaledBitmap = new (ELeave) CFbsBitmap;
+	User::LeaveIfError(iScaledBitmap->Create(targetRes, iDestBitmap->DisplayMode()));
+	iScaler->Scale(&iStatus, *iSourceBitmap, *iScaledBitmap, ETrue);
+	SetActive();
+	}
+
+
+
+void CVeiImageClipFrameOperation::RunL()
+	{
+	if (!iNoScaling && iSourceMask && !iScaledMask) 
+		{
+		/* Scale the mask. */
+		iScaledMask = new (ELeave) CFbsBitmap;
+		User::LeaveIfError(iScaledMask->Create(iScaledBitmap->SizeInPixels(), iSourceMask->DisplayMode()));
+		iScaler->Scale(&iStatus, *iSourceMask, *iScaledMask, ETrue);
+		SetActive();
+		return;
+		}
+
+	/* Select source. */
+	CFbsBitmap* bitmap = 0;
+	CFbsBitmap* mask = 0;
+
+	if (iScaledBitmap) 
+		{
+		bitmap = iScaledBitmap;
+		}
+	else
+		{
+		bitmap = iSourceBitmap;
+		}
+
+	if (iScaledMask) 
+		{
+		mask = iScaledMask;
+		}
+	else
+		{
+		mask = iSourceMask;
+		}
+
+
+	/* Initialize context. */
+	CFbsDevice* device = CFbsBitmapDevice::NewL(iDestBitmap);
+	CleanupStack::PushL(device);
+	CFbsBitGc* gc = NULL;
+	User::LeaveIfError(device->CreateContext(gc));
+
+	/* Calculate source point. */
+	TSize destRes = iDestBitmap->SizeInPixels();
+	TSize sourceRes = bitmap->SizeInPixels();	
+	TPoint sourcePoint((destRes.iWidth - sourceRes.iWidth) / 2,
+						(destRes.iHeight - sourceRes.iHeight) / 2);
+
+	/* Draw background (this is relevant for scaled images and transparency). */
+	gc->SetBrushColor(iGenerator.BackgroundColor());
+	gc->SetBrushStyle(CGraphicsContext::ESolidBrush);
+	gc->DrawRect(TRect(TPoint(0, 0), destRes));
+
+	if (mask) 
+		{
+		TRect sourceRect(bitmap->SizeInPixels());
+		gc->BitBltMasked(sourcePoint, bitmap, sourceRect, mask, EFalse);
+		}
+	else
+		{
+		gc->BitBlt(sourcePoint, bitmap);
+		}
+
+	delete gc;
+	CleanupStack::PopAndDestroy(device);
+
+	/* This transfers the bitmap ownership to the observer. */
+	iObserver->NotifyVideoClipGeneratorFrameCompleted(iGenerator, KErrNone, iDestBitmap);
+
+	delete iScaler;
+	iScaler = 0;
+
+	iSourceBitmap = 0;
+	iSourceMask = 0;
+	iDestBitmap = 0;
+	iObserver = 0;
+	iIndex = -1;
+	iNoScaling = EFalse;
+	}
+
+TInt CVeiImageClipFrameOperation::RunError(TInt aError)
+	{
+	iObserver->NotifyVideoClipGeneratorFrameCompleted(iGenerator, aError, NULL);
+
+	if (iScaler) 
+		{
+		iScaler->Cancel();
+		}
+	
+	delete iScaler;
+	iScaler = 0;
+	delete iScaledBitmap;
+	iScaledBitmap = 0;
+	delete iScaledMask;
+	iScaledMask = 0;
+	delete iDestBitmap;
+	iDestBitmap = 0;
+	iSourceBitmap = 0;
+	iSourceMask = 0;
+	iObserver = 0;
+	iIndex = -1;
+
+	return KErrNone;
+	}
+
+
+void CVeiImageClipFrameOperation::DoCancel()
+	{
+	iObserver->NotifyVideoClipGeneratorFrameCompleted(iGenerator, KErrCancel, 0);
+
+	if (iScaler) 
+		{
+		iScaler->Cancel();
+		}
+	
+	delete iScaler;
+	iScaler = 0;
+	delete iScaledBitmap;
+	iScaledBitmap = 0;
+	delete iScaledMask;
+	iScaledMask = 0;
+
+	delete iDestBitmap;
+	iDestBitmap = 0;
+	iSourceBitmap = 0;
+	iSourceMask = 0;
+	iObserver = 0;
+	iIndex = -1;
+	}
+// End of File
+