imaging/imagingfws/ImageDisplay/plugins/IclWrapper/GenericIclWrapper.cpp
changeset 0 5752a19fdefe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imaging/imagingfws/ImageDisplay/plugins/IclWrapper/GenericIclWrapper.cpp	Wed Aug 25 12:29:52 2010 +0300
@@ -0,0 +1,714 @@
+// Copyright (c) 2005-2009 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:
+//
+
+/**
+ @file
+ @internalComponent 
+*/
+
+
+#include "GenericIclWrapper.h"
+#include <icl/imagedisplaypaniccodes.h>
+#include "MiscUtils.h"
+
+#include "ExifImageDisplayPanic.h"
+
+#define KZeroDelay TTimeIntervalMicroSeconds(0)
+_LIT(KGenIclWrapperPanicCategory, "GenIclWrapperImgDisplay");
+
+CGenericImageDisplayPlugin* CGenericImageDisplayPlugin::NewL()
+	{
+	CGenericImageDisplayPlugin* self = new(ELeave) CGenericImageDisplayPlugin(KGenIclWrapperPanicCategory);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CGenericImageDisplayPlugin::ConstructL()
+	{
+	User::LeaveIfError( iFs.Connect() );
+	iPlayer		= new (ELeave) CImagePlayer(*this);
+	iDelayedCb	= CDelayedCallback::NewL(*this);
+	}
+
+CGenericImageDisplayPlugin::~CGenericImageDisplayPlugin()
+	{
+	if (iTransformer != NULL)
+		{
+		iTransformer->Cancel();
+		delete iTransformer;
+		}
+	if (iMaskTransformer != NULL)
+		{
+		iMaskTransformer->Cancel();
+		delete iMaskTransformer;
+		}
+	
+	delete iDelayedCb;
+	delete iPlayer;
+	delete iDecoder;
+	iFs.Close();
+	}
+
+CGenericImageDisplayPlugin::CGenericImageDisplayPlugin(const TDesC& aPanicCategoryStr):
+							iPanicCategory(aPanicCategoryStr),
+							iMaxReductionFactor(KMaxReductionFactor),
+							iMaxUnscaledSize(KMaxTInt,KMaxTInt),
+							iExtHandler(*this),
+							iScaleQuality(-1),
+							iPluginImgStatus(CImageDisplay::EImageTypeUnknown)
+	{
+	}
+
+void CGenericImageDisplayPlugin::Panic(TInt aPanicNumber) const
+	{
+	User::Panic(iPanicCategory, aPanicNumber);
+	}
+
+void CGenericImageDisplayPlugin::OpenL()
+	{
+	iValidBitmap = EFalse;
+	const TUint option=(Options() & (CImageDisplay::EOptionThumbnail|CImageDisplay::EOptionMainImage));
+	if (option==CImageDisplay::EOptionsUndefined || 
+			option == (CImageDisplay::EOptionThumbnail|CImageDisplay::EOptionMainImage))
+		{
+		User::Leave(KErrNotSupported);
+		}
+
+	switch ( SourceType() )
+		{
+		case CImageDisplayPlugin::EImgSrcFileName:
+			{
+			
+			TMMFileSource src(SourceFilename(), SourceDataId(), SourceDataIntent());
+			iDecoder = CImageDecoder::FileNewL(iFs, src, CImageDecoder::EAllowGeneratedMask, KNullUid, KNullUid, RequiredImageClass());
+			}
+			break;
+
+		case CImageDisplayPlugin::EImgSrcFileHandle:
+			{
+			TMMFileHandleSource src(SourceFileHandle(), SourceDataId(), SourceDataIntent());
+			iDecoder = CImageDecoder::FileNewL(iFs, src, CImageDecoder::EAllowGeneratedMask, KNullUid, KNullUid, RequiredImageClass());
+			}
+			break;
+
+		case CImageDisplayPlugin::EImgSrcDescriptor:
+			iDecoder = CImageDecoder::DataNewL(iFs, SourceData(), CImageDecoder::EAllowGeneratedMask, KNullUid, KNullUid, RequiredImageClass());
+			break;
+
+		default:
+			ASSERT(EFalse);
+			User::Leave(KErrArgument);
+		}
+	iValidBitmap		= EFalse;
+	iImageIsThumbnail	= (option==CImageDisplay::EOptionThumbnail);
+
+	if (iImageIsThumbnail)
+		{
+		iDecoder->SetImageTypeL(CImageDecoder::EImageTypeThumbnail ); // leaves if there is no thumbnail
+		}
+
+	AfterOpenL(); // allow derived plug-in to perform some initialization
+	iRotationOptions = EffectiveRotation();
+	FillRecommendedSizesL();
+	CacheImageStatus();
+	}
+
+void CGenericImageDisplayPlugin::Play()
+	{
+	iIsPaused = EFalse;
+	if (iCallBackIsPending)
+		{
+		iDelayedCb->CallAfter(KZeroDelay);
+		iCallBackIsPending = EFalse;
+		return;
+		}
+	if (IsStatusSet(EStatusNoMoreToDecode))
+		{
+		ASSERT(IsStatusSet(EStatusPaused)==EFalse);
+		if (iState < EInitCompleted)
+			{
+			Panic(EPanicNotInitialized);
+			}
+		if (iValidBitmap && iLastError==KErrNone)
+			{
+			ClearStatusFlag(EStatusPaused|EStatusBusy);
+			iDelayedCb->CallAfter(KZeroDelay);
+			return;
+			}
+		ClearStatusFlag(EStatusNoMoreToDecode);
+		iState = EInitCompleted;
+		}
+	else if (IsStatusSet(EStatusPaused)==EFalse && iState>=EInitCompleted)
+		{
+		if (iState==EInitCompleted && IsStatusSet(EStatusBusy)==EFalse)
+			{
+			iPlayer->Play(*iDecoder, EffectiveDispMode(), (iImageIsThumbnail? 1 : CImagePlayer::KNoFrameLimit) );
+			iState = EProcessing;
+			}
+		return;
+		}
+
+	ClearStatusFlag(EStatusPaused|EStatusNoMoreToDecode|EStatusBusy);
+	switch (iState)
+		{
+		case EIdle:
+			TRAPD(err,	
+						CompleteInitL();
+				);
+			iOutputRect=TRect(0,0,DestinationSizeInPixels().iWidth, DestinationSizeInPixels().iHeight);
+			if (err != KErrNone)
+				{
+				AsyncCallbackImageReady(NULL, EStatusNoMoreToDecode, iOutputRect, err);
+				iState = EInitFailed;
+				return;
+				}
+			iPlayer->Play(*iDecoder, EffectiveDispMode(), (iImageIsThumbnail? 1 : CImagePlayer::KNoFrameLimit) );
+			iState = EProcessing;
+			break;
+
+		case EInitFailed:
+			Panic(EPanicInitFailed);
+			break;
+
+		case EInitCompleted:
+		case EProcessing:
+			iPlayer->Resume();
+			iState = EProcessing;
+			break;
+
+		case ETransforming:
+			iTransformer->Restart();
+			break;
+
+		case ETransformingMask:
+			iMaskTransformer->Restart();
+			break;
+
+		default:
+			ASSERT(EFalse);
+		}
+	
+	SetStatusFlag(EStatusBusy);
+	}
+
+void CGenericImageDisplayPlugin::Pause()
+	{
+	if (iIsPaused)
+		{
+		return;
+		}
+	if ( CallbackIsRunning() )
+		{
+		CancelCallback();
+		// actually the framework may not be able to cancel the callback
+		// if the execution point has already passed to the client code and it yielded to AS somehow
+		// so we check if the callback was actually cancelled by trying it after cancel attempt
+		iCallBackIsPending = !CallbackIsRunning(); 
+		}
+	else
+		{
+		if (iTransformer != NULL && iState == ETransforming)
+			{
+			iTransformer->Cancel();
+			}
+		else if (iMaskTransformer != NULL  && iState == ETransformingMask)
+			{
+			iMaskTransformer->Cancel();
+			}
+		else
+			{
+			iPlayer->Pause();
+			}
+		if (!IsStatusSet(EStatusNoMoreToDecode))
+			{
+			SetStatusFlag(EStatusPaused);
+			iIsPaused = ETrue;
+			}			
+		}			
+	ClearStatusFlag(EStatusBusy);
+	}
+
+void CGenericImageDisplayPlugin::StopPlay()
+	{
+	CancelCallback();
+	iNextFrameDelay	= 0;
+	iIsPaused		= EFalse;
+	iValidBitmap	= EFalse;
+	iCallBackIsPending = EFalse;
+	if (iState >= EInitCompleted)
+		{
+		iState = EInitCompleted;
+		}
+	if (iTransformer != NULL)
+		{
+		iTransformer->Cancel();
+		}
+	if (iMaskTransformer != NULL)
+		{
+		iMaskTransformer->Cancel();
+		}
+	if (iPlayer != NULL)
+		{
+		iPlayer->Cancel();
+		}
+	if (iDelayedCb != NULL)
+		{
+		iDelayedCb->Cancel();
+		}
+	ClearStatusFlag(EStatusBusy|EStatusPaused);
+	}
+
+void CGenericImageDisplayPlugin::GetBitmap(const CFbsBitmap*& aBitmap, const CFbsBitmap*& aMask) const
+	{
+	if (iState < EInitCompleted)
+		{
+		Panic(EPanicNotInitialized);
+		}
+	if (ValidBitmap())
+		{
+		aBitmap = iCurrentFrame;
+		aMask	= iCurrentMask;
+		}
+	else
+		{
+		aBitmap = NULL;
+		aMask	= NULL;
+		}
+	}
+	
+const CImageDisplay::RImageSizeArray& CGenericImageDisplayPlugin::RecommendedImageSizes() const
+	{
+	return iImageSizes;
+	}
+
+TInt CGenericImageDisplayPlugin::NumFrames(TInt& aNumFrames) const
+	{
+	if (iDecoder->IsImageHeaderProcessingComplete())
+		{
+		aNumFrames = iDecoder->FrameCount();
+		return KErrNone;
+		}
+	aNumFrames = -1;
+	return KErrNotReady;
+	}
+
+TUint CGenericImageDisplayPlugin::ImageStatus() const
+	{
+	return iPluginImgStatus;
+	}
+
+TBool CGenericImageDisplayPlugin::ValidBitmap() const
+	{
+	return iValidBitmap;
+	}
+
+void CGenericImageDisplayPlugin::CacheImageStatus()
+	{
+	TInt numFrames;
+	iPluginImgStatus=(NumFrames(numFrames)==KErrNone && numFrames > 1)? CImageDisplay::EImageMultiFrame : CImageDisplay::EImageSingleFrame;
+	for (TInt i=0; i<numFrames; ++i)
+		{
+		if (iDecoder->FrameInfo(i).iDelay.Int64() != 0)
+			{
+			iPluginImgStatus|= CImageDisplay::EImageAnimated;
+			if (iImageHasFloatingSubImgs)
+				{
+				break;
+				}
+			}
+		if (iDecoder->FrameInfo(i).iFrameCoordsInPixels != iDecoder->FrameInfo(0).iFrameCoordsInPixels)
+			{
+			iImageHasFloatingSubImgs = ETrue;
+			if (iPluginImgStatus & CImageDisplay::EImageAnimated)
+				{
+				break;
+				}
+			}
+		}
+	iPluginImgStatus|=((iDecoder->FrameInfo().iFlags&TFrameInfo::ETransparencyPossible) ? CImageDisplay::EImageMasked : 0);
+	iPluginImgStatus|=(ThumbnailExists()?CImageDisplay::EImageHasThumbnail : 0);
+	iPluginImgStatus|=((iDecoder->FrameInfo().iFlags&TFrameInfo::EFullyScaleable)?CImageDisplay::EImageIsFullyScalable : 0);
+	}
+
+void CGenericImageDisplayPlugin::OnPlayEvent(TInt aErrorCode, TUint aEventFlags, CFbsBitmap* aFrame, CFbsBitmap* aMask)
+	{
+	ASSERT(IsStatusSet(EStatusPaused)==EFalse);
+	iLastError = aErrorCode;
+	if (aErrorCode == KErrNone)
+		{
+		iValidBitmap	= EFalse;
+		iPluginStatus	= aEventFlags;
+		iCurrentMask	= aMask;
+		if (iTransformer != NULL ) 
+			{
+			SetStatusFlag(EStatusBusy);
+			iState = ETransforming;
+			iTransformer->Singleton().SetTrueSrcSize( iPlayer->OriginalFrameSize() );
+			iTransformer->Transform(aFrame, MaintainAspectRatio());
+			}
+		else
+			{
+			iValidBitmap	= ETrue;
+			iCurrentFrame	= aFrame;
+			ClearStatusFlag(EStatusBusy);
+			if ((aEventFlags&EStatusNoMoreToDecode)==0)
+				{
+				SetStatusFlag(EStatusPaused);	
+				}
+			const TTimeIntervalMicroSeconds delay(iNextFrameDelay);
+			iNextFrameDelay = iPlayer->CurrentFrameInfo().iDelay;
+			iDelayedCb->CallAfter(delay);
+			}
+		}
+	else	
+		{
+		iState = EInitFailed;
+		iPluginStatus = EStatusNoMoreToDecode;
+		iCurrentFrame = NULL;
+// we've got error, so there is no need to go via delayed callback
+		AsyncCallbackImageReady(iCurrentFrame, iPluginStatus, iOutputRect, aErrorCode);
+		}
+	}
+
+void CGenericImageDisplayPlugin::OnTransformDone(TInt aError)
+	{
+	TTimeIntervalMicroSeconds delay(0);
+	if (aError == KErrNone)
+		{
+		if (iCurrentMask != NULL && iState == ETransforming)
+			{
+			iMaskTransformer->Transform(iCurrentMask, MaintainAspectRatio());
+			iState = ETransformingMask;
+			return;
+			}
+		SetStatusFlag(EStatusFrameReady);
+		if (!IsStatusSet(EStatusNoMoreToDecode))
+			{
+			SetStatusFlag(EStatusPaused);
+			}
+		iTransformer->GetBitmap(iCurrentFrame);
+		if (iCurrentMask)
+			{
+			iMaskTransformer->GetBitmap(iCurrentMask);
+			}
+		if (iCurrentFrame != NULL)
+			{
+			iValidBitmap= ETrue;
+			delay		= iNextFrameDelay;
+			}
+		iNextFrameDelay = iPlayer->CurrentFrameInfo().iDelay;
+		iState = EProcessing;
+		}
+	else
+		{
+		iState			= EInitFailed;
+		iValidBitmap	= EFalse;
+		iCurrentFrame	= NULL;
+		SetStatusFlag(EStatusNoMoreToDecode);
+		ClearStatusFlag(EStatusBusy|EStatusPaused);
+		}
+	
+	ClearStatusFlag(EStatusBusy);
+	iLastError = aError;
+	iDelayedCb->CallAfter(delay);
+	}
+
+void CGenericImageDisplayPlugin::FillRecommendedSizesL()
+	{
+	ASSERT( iDecoder->IsImageHeaderProcessingComplete() );
+	TRect clipRect;
+	const TBool clipDefined=SourceRect(clipRect);
+	const TFrameInfo& FrameInfo=iDecoder->FrameInfo();
+
+	TSize imageSize(clipDefined? clipRect.Size() : FrameInfo.iFrameCoordsInPixels.Size() );
+	RotateSize(imageSize, iRotationOptions);
+	GenRecommendedSizesL(iImageSizes, imageSize, KMaxReductionFactor);
+	}
+
+void CGenericImageDisplayPlugin::GenRecommendedSizesL(RSizeArray& aArray,const TSize& aOriginalSize,TInt aMaxReduction )
+	{
+	aArray.Reset();
+	TSize reducedSize(aOriginalSize);
+	TInt i=0;
+	do 
+		{
+		User::LeaveIfError( aArray.Append(reducedSize) );
+		if (KErrNone != iDecoder->ReducedSize(aOriginalSize, ++i, reducedSize))
+			{
+			break;
+			}
+
+		} while ( i<=aMaxReduction &&
+					reducedSize.iWidth <= MaxUnscaledSize().iWidth && reducedSize.iHeight <= MaxUnscaledSize().iHeight);
+	}
+
+TBool CGenericImageDisplayPlugin::SetupPlayerGetScalingNeeded(const TSize& aImageSize, const TSize& aTrueImgSize)
+	{
+	TSize effectiveDestSize( DestinationSizeInPixels() );
+	RotateSize(effectiveDestSize, iRotationOptions);
+
+	const TSize askSize( effectiveDestSize );
+	// in case of clipping and upscaling  - decode to original size, clip then upscale
+	// in case of clipping and downscaling- decode to downscaled, then clip
+	const TSize destSize( aImageSize == aTrueImgSize ? askSize : // no clipping so decode to dest size
+			(IsSize2Exceeds1(aTrueImgSize, askSize)?aTrueImgSize: 
+													ScaleSize(effectiveDestSize, aImageSize, aTrueImgSize) )
+						);
+	if ((iDecoder->FrameInfo().iFlags&TFrameInfo::EFullyScaleable))
+		{
+		iPlayer->Setup( destSize, KZeroReductionFactor, KZeroReductionFactor);
+		return EFalse;
+		}
+
+	TBool scalingNeeded=EFalse;
+	TInt neededReduction=iDecoder->ReductionFactor(aTrueImgSize, destSize);
+	TSize reducedSize(-1,-1);
+	// if given reduction factor is not supported we'd go using scaler
+	iDecoder->ReducedSize(aTrueImgSize, neededReduction, reducedSize);
+	// animated images suffer from "1 pixel difference" problems when they are composed of reduced parts
+	// so always use scaling for such images
+	const TBool KNoReductionAllowed=(iImageHasFloatingSubImgs && (iPluginImgStatus & CImageDisplay::EImageAnimated));
+	if (neededReduction>=0 && neededReduction<=KMaxReductionFactor && reducedSize==destSize 
+			&& !KNoReductionAllowed)
+		{
+		// destination size is achivable through reduction factor
+		TInt maxReduction=(IsSize2Exceeds1(iMaxUnscaledSize,destSize)? Max(iMaxReductionFactor,neededReduction) : neededReduction);
+		iPlayer->Setup(reducedSize, maxReduction ,neededReduction);
+		scalingNeeded=(maxReduction != neededReduction);
+		}
+	else
+		{
+		scalingNeeded=ETrue;
+		if (IsSize2Exceeds1(aTrueImgSize, destSize) || KNoReductionAllowed)// we've got upscaling, or no reduction is allowed
+			{
+			iPlayer->Setup(aTrueImgSize, KZeroReductionFactor, KZeroReductionFactor); 
+			}
+		else
+			{
+			// we do not apply reduction for sizes smaller than iMaxUnscaledSize
+			TInt reduction=iDecoder->ReductionFactor(destSize, iMaxUnscaledSize);
+			reduction=Max(0, Min(iMaxReductionFactor, reduction));
+			iPlayer->Setup(aTrueImgSize, reduction, KZeroReductionFactor); 
+			// and we have to use further downscaling to achieve final target size
+			}
+		}
+	return scalingNeeded;
+	}
+
+void CGenericImageDisplayPlugin::CompleteInitL()
+	{
+	ASSERT( iDecoder->IsImageHeaderProcessingComplete() );
+	iNumberOfFrames = iDecoder->FrameCount();
+	TBool needsMask=EFalse;
+
+	for (TInt i=0; i<iNumberOfFrames && !needsMask; ++i)
+		{
+		needsMask = ( (iDecoder->FrameInfo(i).iFlags & TFrameInfo::ETransparencyPossible) != 0 );
+		}
+	const TFrameInfo& frameInfo=iDecoder->FrameInfo();
+	TRect frameRect(frameInfo.iFrameCoordsInPixels);
+	const TSize imageSize(frameRect.Size());
+	TRect clipRect;
+	const TBool clipDefined=SourceRect(clipRect);
+	if (!clipDefined)
+		{
+		clipRect=frameRect;
+		}
+	else
+		{
+		TRect clipTest(clipRect);
+		clipTest.Intersection(frameRect);
+		if (clipTest != clipRect ) // clipping rect is outside the image rect
+			{
+			User::Leave( KErrArgument );
+			}
+		}
+	
+	TBool scalingNeeded=SetupPlayerGetScalingNeeded( clipDefined?clipRect.Size():imageSize, imageSize);
+
+	if (iRotationOptions || clipDefined || scalingNeeded )
+		{
+		CTransformerSharedData* singleton=CTransformerSharedData::NewL();
+		CleanupReleasePushL(*singleton);
+		singleton->SetTransformOptions(iRotationOptions);
+		singleton->SetTrueSrcSize(imageSize);
+		if (clipDefined)
+			{
+			singleton->SetClipRect(clipRect);
+			}
+		iTransformer= CAsyncTransformer::NewL(*this, *singleton, DestinationSizeInPixels());
+
+		if (needsMask)
+			{
+			iMaskTransformer= CAsyncTransformer::NewL(*this, *singleton, DestinationSizeInPixels());
+			}
+		CleanupStack::PopAndDestroy(singleton);
+
+		if (iScaleQuality!=-1)
+			{
+			User::LeaveIfError( iTransformer->SetScaleQuality(iScaleQuality) );
+			if (iMaskTransformer)
+				{
+				User::LeaveIfError( iMaskTransformer->SetScaleQuality(iScaleQuality) );
+				}
+			}
+		}
+	iState = EInitCompleted;
+	}
+
+TInt CGenericImageDisplayPlugin::ExtensionInterface(TUid aIFaceUid, TAny*& aIFacePtr)
+	{
+	aIFacePtr = NULL;
+	if (aIFaceUid == KUidGenIclImageDisplayPluginExtUid)
+		{
+		aIFacePtr = static_cast<MGenIclImageDisplayExtension*>(&iExtHandler);
+		return KErrNone;
+		}
+	return KErrNotSupported;
+	}
+
+TBool CGenericImageDisplayPlugin::ThumbnailExists() const
+	{
+	return EFalse;
+	}
+
+TUint CGenericImageDisplayPlugin::EffectiveRotation() const
+	{
+	return Options() & ( CImageDisplay::EOptionMirrorVertical |
+			(CImageDisplay::EOptionMirrorVertical - CImageDisplay::EOptionRotateCw90) );
+	}
+
+TInt CGenericImageDisplayPlugin::DoSetScaleQuality(TInt aQualityValue)
+	{
+	iScaleQuality = aQualityValue;
+	TInt err=KErrNone;
+	if (iTransformer != NULL)
+		{
+		err=iTransformer->SetScaleQuality(aQualityValue);
+		}
+	if (err==KErrNone && iMaskTransformer != NULL)
+		{
+		err=iMaskTransformer->SetScaleQuality(aQualityValue);
+		}
+
+	return err;
+	}
+
+TInt CGenericImageDisplayPlugin::DoSetMaximumReductionFactor(TInt aMaxReductionFactor)
+	{
+	if (aMaxReductionFactor<0 || aMaxReductionFactor>KMaxReductionFactor)
+		{
+		return KErrArgument;
+		}
+	iMaxReductionFactor = aMaxReductionFactor;
+	return KErrNone;	
+	}
+
+TInt CGenericImageDisplayPlugin::SetMaxUnscaledSize(const TSize& aSize)
+	{
+	if (aSize.iWidth < 1 || aSize.iHeight < 1)
+		{
+		return KErrArgument;
+		}
+	iMaxUnscaledSize = aSize;
+	return KErrNone;
+	}
+
+void CGenericImageDisplayPlugin::AfterOpenL()
+	{
+	}
+
+TUid CGenericImageDisplayPlugin::RequiredImageClass() const
+	{
+	return KNullUid;
+	}
+
+void CGenericImageDisplayPlugin::OnCallback()
+	{
+	if (iIsPaused)
+		{
+		iCallBackIsPending = ETrue;
+		return;
+		}
+	AsyncCallbackImageReady(iCurrentFrame, iPluginStatus, iOutputRect, iLastError);
+	}
+
+/*static*/
+CDelayedCallback* CDelayedCallback::NewL(MCallbackClient& aClient)
+	{
+	CDelayedCallback* self=new (ELeave) CDelayedCallback(aClient);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CDelayedCallback::CallAfter(const TTimeIntervalMicroSeconds& aAfter)
+	{
+	TTime now;	
+	now.HomeTime();
+	const TInt64 KTimerTreshold	=8000; // 8 ms, we don't go trought timer for delays less than that
+	const TInt64 KDelayDelta	=4000; // 4 ms, make delay 4 ms less to account time before we reach client
+	const TInt64 requiredDelay = aAfter.Int64() - (now.Int64() - iLatestCallbackAt.Int64());
+	if ( requiredDelay <= KTimerTreshold )
+		{
+		iLatestCallbackAt = now;
+		iClient.OnCallback();
+		}
+	else
+		{
+		CTimer::After( TTimeIntervalMicroSeconds32( I64INT(requiredDelay-KDelayDelta) ) );
+		}
+	}
+
+void CDelayedCallback::Cancel()
+	{
+	CTimer::Cancel();
+	}
+
+void CDelayedCallback::RunL()
+	{
+	iLatestCallbackAt.HomeTime();
+	iClient.OnCallback();
+	}
+
+const TUid TExtTie::Uid() const
+	{
+	return KUidGenIclImageDisplayPluginExtUid;
+	}
+
+void TExtTie::Release()
+	{
+	}
+
+TInt TExtTie::SetScaleQuality(TInt aQualityLevel)
+	{
+	return iImplementor.DoSetScaleQuality(aQualityLevel);
+	}
+
+TInt TExtTie::SetMaximumReductionFactor(TInt aMaxReductionFactor)
+	{
+	return iImplementor.DoSetMaximumReductionFactor(aMaxReductionFactor);
+	}
+	
+TInt TExtTie::SetMaximumNonReducedSize(const TSize& aSize)
+	{
+	return iImplementor.SetMaxUnscaledSize(aSize);
+	}
+
+
+