windowing/windowserver/tauto/tcrpanim.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 27 May 2010 14:13:51 +0300
changeset 85 cdf2f6e5c390
parent 0 5d03bc08d59c
permissions -rw-r--r--
Revision: 201021 Kit: 2010121

// Copyright (c) 2007-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:
// Implements  CTCrpAnim
// Test CRP animations & their interaction with overlapping transparent/non-transparent windows 
// & wserv's underlying redraw-store strategies
// 
//

/**
 @file
 @test
 @internalComponent - Internal Symbian test code
*/

#include <w32stdgraphic.h>
#include "tcrpanim.h"

// RUN_SAMPLE_ON_LEFT allows the demo animation to run in the left-hand window during testing. 
// Used for demonstration purposes only
#define RUN_SAMPLE_ON_LEFT

namespace	//anonymous local scope
	{
	const TInt KAnimationFrameDelayTime = 50000; 				// delay in microseconds between frames
	const TInt KShortDelayLoop = 2*KAnimationFrameDelayTime; 	// delay time in microseconds used in test cases
	const TInt KAnimationTotalFrames = 40;						// total number of frames in a CWsGraphicBitmapAnimation
	const TInt KAnimDimension = 40;								// animation width/height. We're enforcing a square animation here
	const TInt KFrameMissedAnimationsThreshold = 10;			// maximum number of missed frame steps allowed
	const TInt KAnimationTearWidthThreshold = 4; 				// maximum columns permitted between a tear
	const TInt KMinGoodFrameThreshold = 30; 					// percentage threshold for number of good frames detected in a test
	const TInt KMaxXY = 200;									// arbitrary maximum size of square used to invalidate a window
	const TInt KMaxRepeatDraw = 2;								// arbitrary value for DrawLine calls during a Draw
	TUid  KUidTestAnimation2 = {0xBAADF00D};					// unique id. for CWsGraphicBitmapAnimation object
	const TUint32 KWhitePixels = 0xFFFFFFFF;					// 32-bit mask value for rgb white
	const TUint32 KBlackPixels = 0x00000000;					// 32-bit value for rgb black
	const TPoint KPointZero(0,0);								// initial point used for animation creation & manipulation (currently 0,0)
	const TPoint KPointOffsite(1000,1000);						// point used to draw off-screen
	const TDisplayMode KTestDisplayMode = EColor16MU;			// display mode used for testing
	const TInt KFrameStepCalculation = Max(1, KAnimDimension/Max(1, KAnimationTotalFrames)); // determine framestep size in columns
	
	enum TColorDetected
		{
		ECantTell=0,
		EDetRed=1,
		EDetGreen=2,
		EDetBlue=4,
		EDetBlack=0x10,
		EDetGrey=0x20,
		EDetWhite=0x40
		};

	class CCrpAnim;		
	class CAnimRedrawWindow : public CTWin
		{
	public:
		CAnimRedrawWindow(CCrpAnim *aAnimWindow, TBool aIsBase);
		~CAnimRedrawWindow();
		void Draw();
	private:
		CCrpAnim *iAnimWindow;
		TBool iIsBase;
		};
		
	class CCrpAnim : public CBase
		{
		friend class CAnimRedrawWindow;
	public:
		enum TWinType
			{
			ERedraw,
			EBlank,		// note: not currently used in tcrpanim tests
			EBackedUp	// note: not currently used in tcrpanim tests
			};
	public:
		CCrpAnim(TBool aIsBase, TWinType aWinType);
		~CCrpAnim();
		enum 
			{
			ENoTransparency=0x100
			};
		void ConstructL(const TPoint &aPos, const TSize &aSize,const TInt aAlphaValue=ENoTransparency);
		void DoDraw(TBool aBlankIt);
		inline void DoDraw();
		void DoDrawEllipse();
		inline TSize Size() {return iCtWin->Size();};
		inline RWindowBase* BaseWin() const {return iCtWin->BaseWin();};
		inline RWindow* Window() const {return STATIC_CAST(RWindow*, iCtWin->BaseWin());};
		inline CTBaseWin* CtBaseWin() {return iCtWin;};
		inline void Invalidate() {CTUser::Splat(TheClient, TRect(iCtWin->Position(), iCtWin->Size()), KRgbGray);};
		void Invalidate(const TRect &aRect);
		static void SetEllipseDrawMode(CGraphicsContext::TDrawMode aEllipseDrawMode);
		void InvalidateAndRedraw(TBool aUseBlankItMember,TBool aBlankIt,TBool aUseRWindowInvalidate,TRect* aRect=NULL);

		//A bit of an animation interface...
		//I have written this interface to be amenable to playing multiple animations,
		//which I think needs testing,
		//but the underlying implementation assumes one animation at present.
		//Your mission, should you choose to accept it, ....
		
		void SetPosAnimation(const TUid& aUid, const TRect& aRect);
		TRect* GetPosAnimation(const TUid& aUid);
		TWsGraphicAnimation* SetAnimation(TUid);	
		TWsGraphicAnimation* GetAnimation(TUid);	
		TBool RemoveAnimation(TUid);	
		inline void	SetBlankIt(TBool aNewVal) {iBlankIt = aNewVal;};
		inline void SetRepeatDrawMax(TInt aVal) {iRepeatDrawMax = aVal;};
	protected:
		static void Draw(CBitmapContext *aGc, const TSize &aSize, TBool aIsBase,const TRect &aRect, TBool aBlankIt,TInt aRepeat, TInt aAlphaValue);
		static void DrawEllipse(CBitmapContext *aGc, const TRect &aRect, TInt aAlphaValue);
		CTBaseWin *iCtWin;
		TWinType iWinType;
		TBool iIsBase;
		TBool iBlankIt;
		TRect iRect;
		TInt  iRepeatDrawMax;
		static CGraphicsContext::TDrawMode iEllipseDrawMode;
		TUid  iAnimUid;
		TWsGraphicAnimation iAnimData;
		TRect iAnimPos;
		TInt iAlphaValue;
		};

/*	    Using this time delay class in order to allow animations to play in our draw.
		User::Wait does not allow the draw to occur (aparrently)
		Note when using this time-delay class: because other active objects can perform part of their
	    processing whilst we wait, wrapping calls to this in __UHEAP_MARK / __UHEAP_MARKEND
	    is likely to fail.  The data providers and animators are a major cause of this. 
*/
	class CActiveWait : public CActive
		{
	public:
		static CActiveWait* NewL();
		~CActiveWait();
		void Wait(TInt aDelay);
		// From CActive:
		void RunL();
		void DoCancel();
		TInt RunError(TInt aError);
	protected:
		CActiveWait();
		void ConstructL();
	protected:
		RTimer iTimer;
		TTime iFromTime;
		};

	CActiveWait* CActiveWait::NewL()
		{
		CActiveWait* self = new (ELeave) CActiveWait;
		CleanupStack::PushL(self);
		self->ConstructL();
		CleanupStack::Pop(self);
		return self;
		}
		
	void CActiveWait::ConstructL()
		{
		User::LeaveIfError(iTimer.CreateLocal());
		CActiveScheduler::Add(this);
		}
		
	CActiveWait::CActiveWait() : CActive(EPriorityNormal)
		{
		iFromTime.HomeTime();
		}

	CActiveWait::~CActiveWait()
		{
		Cancel();
		iTimer.Close();
		}

	void CActiveWait::DoCancel()
		{
		iTimer.Cancel();
		CActiveScheduler::Stop();
		}

	void CActiveWait::RunL()
		{
		CActiveScheduler::Stop();
		}
		
	TInt CActiveWait::RunError(TInt aError)
		{
		return aError; // exists so a break point can be placed on it.
		}

/*	    Note when using this : because other active objects can perform part of their
	    processing whilst we wait, wrapping calls to this in __UHEAP_MARK / __UHEAP_MARKEND
	    is likely to fail.  The data providers and animators are a major cause of this. 
*/
	void CActiveWait::Wait(TInt aDelay)
		{
		iTimer.After(iStatus, aDelay);
		SetActive();
		CActiveScheduler::Start();
		}
	CGraphicsContext::TDrawMode CCrpAnim::iEllipseDrawMode;

//
	}	//end anonymous local scope
//

/** This fn allocates an animation frame of the specified dimensions.
	Not tested outside the current limited parameter set (16/2/2007).
	Note the use of 32-bit integers for pixel/colour values. Using display mode lower than 24bpp may not produce correct results
	My attempt to write animation generating code that avoids CIclLoader and Decoder class.
	@param aDelayUs	 	the display time for the frame
	@param aImageType	Colour format for colour plane. 24MA currently not flagged correctly I expect.
	@param aMaskType	Format for mask. ENone for no mask.
	@param aImageSize	Width/height of bitmap area
	@param aImageOffset	Optional offset for bitmap area
	@param aTotalSize	Optional width/height of whole animation (I think)
	@return CFrame		filled in with allocated bitmaps. The get methods for the bitmaps return const type.
**/
static CWsGraphicBitmapAnimation::CFrame* NewFrameLC(TInt aDelayUs,TDisplayMode aImageType,TDisplayMode aMaskType,const TSize& aImageSize,const TPoint& aImageOffset=KPointZero,const TSize& aTotalSize=TSize(0,0))
	{
	TFrameInfo info;
	info.iFrameCoordsInPixels = TRect(aImageOffset,aImageSize);
	info.iFrameSizeInTwips = aImageSize;	  //this is zero in the gif loader
	info.iDelay = TTimeIntervalMicroSeconds(aDelayUs);
	info.iFlags = TFrameInfo::EColor|TFrameInfo::ELeaveInPlace|TFrameInfo::EUsesFrameSizeInPixels;
	if (aMaskType != ENone)
		{
		info.iFlags|=TFrameInfo::ETransparencyPossible;
		}
	if ((aTotalSize.iHeight > 0) && (aTotalSize.iWidth > 0))
		{
		// restrict the size of the frame to specified size of the animation
		info.iOverallSizeInPixels = aTotalSize;
		}
	else
		{
		// assign the size of the frame to the size of the entire bitmap area
		info.iOverallSizeInPixels = info.iFrameCoordsInPixels.iBr.AsSize();
		}
	info.iFrameDisplayMode = aImageType;
	info.iBackgroundColor = KRgbGreen;

	CWsGraphicBitmapAnimation::CFrame* frame = CWsGraphicBitmapAnimation::CFrame::NewL();
	CleanupStack::PushL(frame);
	frame->SetFrameInfo(info);
	CFbsBitmap* bitmap = new(ELeave) CFbsBitmap;
	frame->SetBitmap(bitmap); //takes ownership
	TSize frameInfoSize = info.iFrameCoordsInPixels.Size();
	User::LeaveIfError(bitmap->Create(frameInfoSize, aImageType));
	if((TFrameInfo::EAlphaChannel|TFrameInfo::ETransparencyPossible) & info.iFlags)
		{
		CFbsBitmap* mask = new(ELeave) CFbsBitmap;
		frame->SetMask(mask); //takes ownership
		User::LeaveIfError(mask->Create(frameInfoSize, aMaskType));
		}
	return frame;
	}

//
// function called back by TCleanupItem frameListCleanup from within CreateAnimFramesL(..) method
//
void CleanupFrameList(TAny* aPtr)
	{
	RPointerArray<CWsGraphicBitmapAnimation::CFrame>* ptrArray = STATIC_CAST(RPointerArray<CWsGraphicBitmapAnimation::CFrame>*, aPtr);
	ptrArray->ResetAndDestroy();
	ptrArray->Close();
	}

/** Helper function for making animation frames.
	//Called from CreateAnimL(...)
	@param aDelayUs		the delay between frames
	@param aNumFrames	number of frames (approx - image width is a factor)
	@param aImageType	colour format of colour data. This may not work properly for non-32-bit, but I haven't fully understood TBitmapUtil documentation.
	@param aMaskType	format for mask - ENone for no mask.
	@param aImageSize	width/height of animation
	@param aBgCol		background colour for image non-masked areas. Masked areas are black.
	@param aFgCol		foreground colour of animating area
	@param aFrames		frames that the animation is constructed from
**/
static void CreateAnimFramesL(TInt aDelayUs,TInt aNumFrames,TDisplayMode aImageType,TDisplayMode aMaskType,TSize aImageSize,TRgb aBgCol,TRgb aFgCol, RPointerArray<CWsGraphicBitmapAnimation::CFrame>& aFrames)
	{
	const TInt 	animWH = aImageSize.iWidth;
	const TInt	animStep = Max(1,animWH/Max(1,aNumFrames));	//note this intentionally rounds down to avoid overflows
	for (TInt ii = 0 ; ii < animWH ; ii += animStep)
		{
		CWsGraphicBitmapAnimation::CFrame* frame = NewFrameLC(aDelayUs,aImageType,aMaskType,aImageSize,KPointZero,aImageSize);
		aFrames.AppendL(frame);
		CleanupStack::Pop(frame);
		TBitmapUtil utilMask(CONST_CAST(CFbsBitmap*, frame->Mask()));
		TBitmapUtil utilCol(CONST_CAST(CFbsBitmap*, frame->Bitmap()));
		utilCol.Begin(KPointZero);
		
		// cycle through the frame's actual bitmap & assign each pixel a value identical to the specified colours
		TUint32 colback=aBgCol.Internal();
		TUint32 colfront=aFgCol.Internal();
		TInt row = KErrNone;
		TInt col = KErrNone;
		for (row = 0 ; row < aImageSize.iHeight ; row++)
			{
			utilCol.SetPos(TPoint(0, row));
			for (col = 0 ; col < aImageSize.iWidth ; col++)
				{
				utilCol.SetPixel(colback);	
				utilCol.IncXPos();
				}
			utilCol.SetPos(TPoint(ii, row));
			for (col = 0 ; col < animStep ; col++)	  //Note I rely on intentional rounding down here!
				{
				utilCol.SetPixel(colfront);	
				utilCol.IncXPos();
				}			
			}
		
		if (aMaskType)
			{
			// cycle through each pixel of the frame's mask & assign a default pixel a colour value
			utilMask.Begin(KPointZero);
			for (row = 0 ; row < aImageSize.iHeight ; row++)
				{
				utilMask.SetPos(TPoint(0,row));
				for (col = 0 ; col < aImageSize.iWidth ; col++)
					{
					utilMask.SetPixel(KWhitePixels);
					utilMask.IncXPos();
					}
				}
			
			const TInt maxmaskWidth = Min(8,Max(animWH/3,2));

			//cut the corners off the mask
			for (row = 0 ; row < maxmaskWidth ; row++)
				{
				TInt currentX = maxmaskWidth - row;
				TInt xPos = KErrNone;

				utilCol.SetPos(TPoint(0,row));
				utilMask.SetPos(TPoint(0,row));
				for(xPos = currentX ; xPos >= 0 ; xPos--)
					{
					utilCol.SetPixel(KBlackPixels);	
					utilCol.IncXPos();
					utilMask.SetPixel(KBlackPixels);	
					utilMask.IncXPos();
					}

				utilCol.SetPos(TPoint(animWH - 1, row));
				utilMask.SetPos(TPoint(animWH - 1, row));
				for(xPos = currentX ; xPos >= 0 ; xPos--)
					{
					utilCol.SetPixel(KBlackPixels);	
					utilCol.DecXPos();
					utilMask.SetPixel(KBlackPixels);	
					utilMask.DecXPos();
					}

				utilCol.SetPos(TPoint(0, animWH - 1 - row));
				utilMask.SetPos(TPoint(0, animWH - 1 - row));
				for(xPos = currentX ; xPos >= 0 ; xPos--)
					{
					utilCol.SetPixel(KBlackPixels);	
					utilCol.IncXPos();
					utilMask.SetPixel(KBlackPixels);	
					utilMask.IncXPos();
					}

				utilCol.SetPos(TPoint(animWH - 1, animWH - 1 - row));
				utilMask.SetPos(TPoint(animWH - 1, animWH - 1 - row));
				for(xPos = currentX ; xPos >= 0 ; xPos--)
					{
					utilCol.SetPixel(KBlackPixels);	
					utilCol.DecXPos();
					utilMask.SetPixel(KBlackPixels);	
					utilMask.DecXPos();
					}
				}
			utilMask.End();
			}
		utilCol.End();
		}
	}

/** My attempt to write animation generating code that avoids CIclLoader and Decoder class.
	//It is better if this test class used it's own generated animation
	//rather than relying on the GIF loader in order to reduce the cross-dependencies.
	//The animation generated is a simple vertical line moving from left to right.
	//To prove the masking, I cut the corners off.
	@param aDelayUs		the delay between frames
	@param aNumFrames	number of frames (approx - image width is a factor)
	@param aImageType	colour format of colour data. This may not work properly for non-32-bit, but I haven't fully understood TBitmapUtil documentation.
	@param aMaskType	format for mask - ENone for no mask.
	@param aImageSize	width/height of animation
	@param aBgCol		background colour for image non-masked areas. Masked areas are black.
	@param aFgCol		foreground colour of animating area
	@param aTUid		TUid assigned to animation
	@return CWsGraphicBitmapAnimation allocated to represent the final animation	
**/
static CWsGraphicBitmapAnimation* CreateAnimL(TInt aDelayUs,TInt aNumFrames,TDisplayMode aImageType,TDisplayMode aMaskType,TSize aImageSize,TRgb aBgCol,TRgb aFgCol,TUid& aTUid)
	{
	RPointerArray<CWsGraphicBitmapAnimation::CFrame> frames;
	TCleanupItem frameListCleanup(CleanupFrameList, &frames);
	CleanupStack::PushL(frameListCleanup);
	
	CreateAnimFramesL(aDelayUs, aNumFrames, aImageType, aMaskType, aImageSize,aBgCol, aFgCol, frames);
	
	CWsGraphicBitmapAnimation* anim = CWsGraphicBitmapAnimation::NewL(aTUid,frames.Array());
	CleanupStack::PopAndDestroy(&frames);
	return anim;
	}

//
// Describes the pure colour of the RGB value. yellow/magenta/cyan set 2 bits. White/grey is seperately flagged.
// This method attempts to determine the strongest primary colour present in any given pixel. 
// Note: The algorithm used is known to work for the current test cases only but requires careful review
// for anyone making additional changes to tcrpanim. Given time, improved algorithm should be developed
// to replace the current one
//
TUint PredominantColour(TUint aCol)
	{	 //I don't like all these ifs, but I don't see an easy alternative
		 //Possibly a bit look-up of the deltas from average would work 
		 //(ignoring the bottom 5 bits =32, not 0x30=48. Ignore bottom 4 bits and accept 3-same answers, or divide by delta?)
		 //
	const TInt Kdelta=0x30;
	TInt red=(aCol&0x00ff0000)>>16;
	TInt green=(aCol&0x0000ff00)>>8;
	TInt blue=(aCol&0x000000ff);
	TInt ave=((red+green+blue)*(65536/3))>>16;
	TBool rOverA=(red>ave);
	TBool gOverA=(green>ave);
	TBool bOverA=(blue>ave);
	TInt numOverAve=(rOverA?1:0)+(gOverA?1:0)+(bOverA?1:0);

	if (numOverAve==1)
		{
		if (rOverA)
			{
			if (red>ave+Kdelta)
				{
				if ((green-blue)>-Kdelta && (green-blue)<Kdelta)
					return  EDetRed;
				}
			else
				{
				if (ave<Kdelta)
					return EDetBlack;
				else
					{
					if (green>ave-Kdelta && blue>ave-Kdelta)
						{
						if (ave>256-Kdelta)
							return EDetWhite;
						else
							return EDetGrey;
						}
					}
				}
			}
				
		if (gOverA)
			{
			if (green>ave+Kdelta)
				{
				if ((blue-red)>-Kdelta && (blue-red)<Kdelta)
					return  EDetGreen;
				}
			else
				{
				if (ave<Kdelta)
					return EDetBlack;
				else
					{
					if (red>ave-Kdelta && blue>ave-Kdelta)
						if (ave>256-Kdelta)
							return EDetWhite;
						else
							return EDetGrey;
					}
				}
			}

		if (bOverA)
			{
			if (blue>ave+Kdelta)
				{
				if ((green-red)>-Kdelta && (green-red)<Kdelta)
					return  EDetBlue;
				}
			else
				{
				if (ave<Kdelta)
					return EDetBlack;
				else
					{
					if (red>ave-Kdelta && green>ave-Kdelta)
						if (ave>256-Kdelta)
							return EDetWhite;
						else
							return EDetGrey;
					}
				}
			}
		}
	else	
		{
		if (!rOverA)
			 if (red<ave-Kdelta)
			 	{
			 	if ((green-blue)>-Kdelta && (green-blue)<Kdelta)
			 		return EDetGreen|EDetBlue;
			 	}
			 else
				{
				if (ave>256-Kdelta)
					return EDetWhite;
				else
					{
					if (blue<ave+Kdelta && green<ave+Kdelta)
						{
						if (ave<Kdelta)
							return EDetBlack;
						else
							return EDetGrey;
						}
					}
				}

		if (!gOverA)
			{
			if (green<ave-Kdelta)
				{
				if ((blue-red)>-Kdelta && (blue-red)<Kdelta)
					return  EDetRed|EDetBlue;
				}
			 else
				{
				if (ave>256-Kdelta)
					return EDetWhite;
				else
					{
					if (blue<ave+Kdelta && red<ave+Kdelta)
						if (ave<Kdelta)
							return EDetBlack;
						else
							return EDetGrey;
					}
				}
			}

		if (!bOverA)
			{
			if (blue<ave-Kdelta)
				{
				if ((green-red)>-Kdelta && (green-red)<Kdelta)
					return  EDetGreen|EDetRed;
				}
			 else
				{
				if (ave>256-Kdelta)
					return EDetWhite;
				else
					{
					if (red<ave+Kdelta && green<ave+Kdelta)
						if (ave<Kdelta)
							return EDetBlack;
						else
							return EDetGrey;
					}
				}
			}
		}
	return ECantTell;
	}

/**
	Helper fn to ensure I put the anims in the same place each time...
**/
void CalcCentredAnimPosition(TRect& aRect,const TSize& aWinSize)
	{
	aRect.Shrink(aWinSize.iWidth*3/8,aWinSize.iHeight*4/10);
	}

CTCrpAnim::CTCrpAnim(CTestStep* aStep) : 
	CTWsGraphicsBase(aStep)
	{
	}

void CTCrpAnim::ConstructL()
	{
	TheClient->iGroup->WinTreeNode()->SetOrdinalPosition(0);
	iRedrawWin=new(ELeave) CCrpAnim(EFalse, CCrpAnim::ERedraw);
	iBaseWin=new(ELeave) CCrpAnim(EFalse, CCrpAnim::ERedraw);
	iOverWin=new(ELeave) CCrpAnim(EFalse, CCrpAnim::ERedraw);

	TSize screenSize=TheClient->iGroup->Size();
	TInt winWidth=(screenSize.iWidth/3)-10;
	TInt winHeight=screenSize.iHeight-10;
	TSize windowSize(winWidth,winHeight);

	iRedrawWin->ConstructL(TPoint(screenSize.iWidth/3*2+5,5), windowSize);
	iBaseWin->ConstructL(TPoint(screenSize.iWidth/3+5,5), windowSize);
	
	//Create a transparent window that exactly overlaps the test window
	//If transparency is not supported the leave causes the window to be destroyed and set to NULL.
	//There is a test for transparency supported, but that simply creates a temp window to test anyway... 
	
	//Note that when I originally wrote this test to fix PDEF101991, it generated white areas that I detected.
	//However, if this transparent window used for extended tests is created over the test window,
	//that somehow stops the white fill from occurring.
	//The fault still occurs, but the previous screen contents are left behind.
	//So now this window is created at an off-screen location.
	TRAPD(err, iOverWin->ConstructL(KPointOffsite, windowSize, 0x80); iOverWin->SetBlankIt(ETrue); iOverWin->SetRepeatDrawMax(KMaxRepeatDraw););
	if (err)
		{
		delete iOverWin;
		iOverWin = NULL;
		}
	
	iTestWin = iRedrawWin;
	iTestWin->SetRepeatDrawMax(KMaxRepeatDraw);
	iBaseWin->SetRepeatDrawMax(KMaxRepeatDraw);

	// create animation object & share it with everyone
	iAnim = CreateAnimL(KAnimationFrameDelayTime,KAnimationTotalFrames,KTestDisplayMode,EGray256,TSize(KAnimDimension, KAnimDimension),KRgbBlue,KRgbRed,KUidTestAnimation2);
	if (!iAnim)
		{
		User::Leave(KErrNoMemory);
		}
	iAnim->ShareGlobally();
	
	// calculate minimum length of the red line
	const TInt maxmaskHeight = Min(8, Max(KAnimDimension/3,2)); // note this calculation mimics that for the size of the corners cut from the mask in CreateAnimL above
	iMinimumCalcRedLine = KAnimDimension - maxmaskHeight*2; // the height of the image minus the two cut corners

	// create the timer object
	iWaiter = CActiveWait::NewL();

	// create screen bitmap object & scanline buffer
	iScreenBitmap = new (ELeave) CFbsBitmap;
	User::LeaveIfError(iScreenBitmap->Create(TSize(KAnimDimension, KAnimDimension), KTestDisplayMode));
	TInt bufLength = iScreenBitmap->ScanLineLength(windowSize.iHeight, KTestDisplayMode);
	iScanlineBuf = HBufC8::NewL(bufLength);
	
	#ifdef RUN_SAMPLE_ON_LEFT
    	{
		// play animation on iBaseWin window
		iBaseWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
		TSize subsize1 = iTestWin->BaseWin()->Size();
		TRect subposition1(subsize1);
		CalcCentredAnimPosition(subposition1, subsize1);
		iBaseWin->SetPosAnimation(KUidTestAnimation2, subposition1);
		iBaseWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
    	}
	#endif
	}

CTCrpAnim::~CTCrpAnim()
	{
	delete iRedrawWin;
	delete iBaseWin;
	delete iOverWin;
	if (iAnim)
		{
		// destroy the animation object
		iAnim->UnShareGlobally();
		iAnim->Destroy();
		delete iAnim;				
		iAnim = NULL;
		}
	if (iWaiter)
		{
		// destroy the timer object
		delete iWaiter;
		iWaiter = NULL;
		}
	if (iScreenBitmap)
		{
		// destroy the screen capture of the animation
		delete iScreenBitmap;
		iScreenBitmap = NULL;
		}
	if (iScanlineBuf)
		{
		// destroy the scanline buffer
		delete iScanlineBuf;
		iScanlineBuf = NULL;
		}
	User::After(200000);
	}

//
// This method checks the animation contained in the aAnimWin window has progressed. That is 
// that it's drawn a sufficient number of concurrent frames to screen & the animation is 
// drawn properly to screen
// returns a Bool identifying whether the animation is considered 'good' or not
//  
void CTCrpAnim::CheckAnimProgressedL(CAnonAnimWindow* aAnimWin, TInt aAdditionalFrameCount, TBool aCaptureFrameResult)
	{
	TBool goodAnimation = ETrue;

	// retrieve the rect from the screen's bitmap that contains the animation
	CWsScreenDevice* screen = TheClient->iScreen;
	TRect animPos = *aAnimWin->GetPosAnimation(KUidTestAnimation2);
	CTBaseWin* bWin = aAnimWin->CtBaseWin();
	animPos.Move(bWin->Position());
	User::LeaveIfError(screen->CopyScreenToBitmap(iScreenBitmap, animPos));
	
	TInt frameNum = DetermineApproxFrameNum(iScreenBitmap, aCaptureFrameResult); // determines the frame Number & checks quality of animation (no tearing, etc)
	TBool frameIdentified=(frameNum>=0);
	
	if (aCaptureFrameResult)
		{
		if (frameIdentified)
			{
			if (iPreviousFrameNum != KErrNotFound)
			 	{
				if (iPreviousFrameNum < frameNum)
					{
					TInt frameStep = KFrameStepCalculation * aAdditionalFrameCount;
					iPreviousFrameNum += frameStep; // move to our *expected* framenumber
					if (frameNum > iPreviousFrameNum)
						{
						// the frame number is ahead of it's expected position
						// This suggests we've possibly missed animating a frame in wserv
						// or test code isn't getting a chance to execute as crp animations taking all cpu cycles
						// If its significantly outside norms, we log the fact (as a performance metric)
						TInt performance = ((frameNum - iPreviousFrameNum) / frameStep);
						if (performance > KFrameMissedAnimationsThreshold)
							{
							iFrameStatus.iFrameSkipped++;
							goodAnimation = EFalse;	
							}	
						}
					// else we're animating above an acceptable threshold
					}
				else if (iPreviousFrameNum == frameNum) // potentially not animating anymore
					{
					iFrameStatus.iFrameIdentical++;
					goodAnimation = EFalse;
					}
				// else animation is progressing fine
				}
			// ignore iPreviousFrameNum == KErrNotFound
			}
		else
			{
			goodAnimation = EFalse; // couldn't id the red line	
			}

		if (goodAnimation)
			{
			iFrameStatus.iFrameOK++;
			}
		}
	// else we were only interested in calculating the frameNum
	iPreviousFrameNum = frameNum;
	}

//
// method to estimate the framenumber based on the location of the thin, red line. 
// Also checks whether tearing of the animation has occured or the animation
// is only partially drawn. 
// These are known issues with wserv animation performance & so we give some allowance for error
// 
TInt CTCrpAnim::DetermineApproxFrameNum(CFbsBitmap* aBitmap, TBool aCaptureFrameResult)
	{
	TInt colFirstTear = KErrNotFound; 	// column id'ing the first tear in the vertical line
	TPtr8 des = iScanlineBuf->Des();	// ptr to the scanline buffer

	// locate the thin, red line in the bitmap
	for (TInt xPos = 0 ; xPos < aBitmap->SizeInPixels().iWidth ; xPos++)
		{
		aBitmap->GetVerticalScanLine(des, xPos, EColor16MA);
		TUint32* pixel = (TUint32*) des.Ptr();
		TInt colour = KErrNone;
		
		for (TInt ii = 0 ; ii < aBitmap->SizeInPixels().iHeight ; ii++)
			{
			colour = PredominantColour(*pixel);
			if (colour & EDetRed)
				{
				if (colFirstTear < 0)
					{
					// check the length of the red line is a good length
					pixel += (iMinimumCalcRedLine - 1); // minus the one pixel to position on last pixel in red line
					colour = PredominantColour(*pixel);
					if (colour & EDetRed)
						{
						// good line
						return xPos;
						}
					else // we've detected first part of a torn line
						{
						colFirstTear = xPos; 
						}
					}
				else
					{
					// located second part of torn line
					if ((xPos - colFirstTear) > KAnimationTearWidthThreshold)
						{
						if (aCaptureFrameResult)
							{
							iFrameStatus.iFrameTearing++;	
							}
						xPos = KErrNotFound;
						}
					return xPos;
					}	
				break;
				}
			pixel++;
			}
		}
	if (aCaptureFrameResult)
		{
		if (colFirstTear < 0)
			{
			iFrameStatus.iFrameEmpty++; // we never located any red line at all
			}
		else
			{
			iFrameStatus.iFramePartial++; // we only located a single, small part of the red line
			}
		}
	return KErrNotFound;
	}

/**	This internal loop tests that the animation and the foreground interact correctly
	The primary test is that the outline of the animation 
	intersects the lines drawn on the foreground correctly, compared to a reference version.
	The iBaseWin is already showing this reference anim.
	If the animation is not drawn, or the foreground is wiped, then this test will fail.
**/
void CTCrpAnim::TestSpriteLoopL(TBool aAnimForeground,TBool aDrawForeground)
	{
	_LIT(KForegroundInfo,"TestSpriteLoop animForeground [%d] drawForeground [%d]");
	INFO_PRINTF3(KForegroundInfo, aAnimForeground, aDrawForeground);
	
	if (!iOverWin && (aAnimForeground || aDrawForeground))
		{
		User::Leave(KErrGeneral); // unable to run this test without iOverWin
		}

	ResetFrameCounters();
	iTestWin->RemoveAnimation(KUidTestAnimation2);
	iTestWin->SetBlankIt(ETrue);		
	if (iOverWin)
		{
		iOverWin->RemoveAnimation(KUidTestAnimation2);
		iOverWin->SetBlankIt(ETrue);
		}

	// determine which window holds the animation, & which will be invalidated with progressively larger rects
	CCrpAnim* animWin=aAnimForeground?iOverWin:iTestWin;
	CCrpAnim* paintWin=aDrawForeground?iOverWin:iTestWin;
	paintWin->SetBlankIt(EFalse);
	
	// set & play the animation on the specified window (animWin)
	animWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
	TSize subsize1 = paintWin->BaseWin()->Size();
	TRect subposition1(subsize1);
	CalcCentredAnimPosition(subposition1, subsize1);
	animWin->SetPosAnimation(KUidTestAnimation2, subposition1);
	
	#ifdef RUN_SAMPLE_ON_LEFT
		// play the demo animation in the left-hand window also
		iBaseWin->InvalidateAndRedraw(ETrue, EFalse, ETrue);
	#endif

	iTestWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
	if (iOverWin)
		{
		iOverWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
		}
	
	// invalidate increasingly larger squares on paintWin 
	// note, some fully overlap the animation, some partially overlap, and some don't overlap at all
	TInt invalidateWaitTime=KAnimationFrameDelayTime*3/4; // microseconds
	TInt temp = KErrNotFound;
	for (TInt step=30;step<KMaxXY;step+=30)
		{
		for (TInt xx=0;xx<KMaxXY;xx+=step)
			{
			for (TInt yy=10;yy<KMaxXY;yy+=step)
	    		{
				// calculate rectangle & invalidate paintWin with it
	    		TRect invalidRect(xx,yy,xx+step,yy+step);
	    		paintWin->InvalidateAndRedraw(ETrue,EFalse,ETrue,&invalidRect);
				
				// calculate any additional frames that may be drawn by above. Note intentionally ignore frame result
				temp = iPreviousFrameNum;	
				CheckAnimProgressedL(animWin, 1, EFalse);
				
				//new defect DEF101896: Test runs faster with this line removed, but there is evident tearing
				iWaiter->Wait(invalidateWaitTime);			//DEF101896 search string: //interrupt_foreground_draw
				
				if (temp == iPreviousFrameNum)
					{
					// give wserv more time to animate the frame
					iWaiter->Wait(invalidateWaitTime);
					}
				CheckAnimProgressedL(animWin, 1); // calculate the frame drawn. Capture frame result
				}
			}
		}

	// determine whether the animation was successful (ie: enough Good frames were detected) or not
	// Note KMinGoodFrameThreshold is essentially an arbitrary number. This can be adjusted to accommodate
	// performance requirements as needed
	temp = LogResults();
	TInt quality = 100*iFrameStatus.iFrameOK/temp;
	TEST(quality > KMinGoodFrameThreshold);
	
	ResetFrameCounters();
	iWaiter->Cancel();
	iTestWin->RemoveAnimation(KUidTestAnimation2);
	iTestWin->SetBlankIt(ETrue);		
	if (iOverWin)
		{
		iOverWin->RemoveAnimation(KUidTestAnimation2);
		iOverWin->SetBlankIt(ETrue);
		}
	}

//
// resets the frame trackers to intial values
//
void CTCrpAnim::ResetFrameCounters()
	{
	iPreviousFrameNum = KErrNotFound;
	iFrameStatus.iFrameOK = 0;
	iFrameStatus.iFramePartial = 0;
	iFrameStatus.iFrameIdentical = 0;
	iFrameStatus.iFrameEmpty = 0;
	iFrameStatus.iFrameTearing = 0;
	iFrameStatus.iFrameSkipped = 0;
	}

//
// Log the current frame results & return the total number of frame calculations
//
// Calculated : the total number of frame-checks run
// Good: 		the frame was successfully drawn to screen & within specified tolerances for tearing, expected position & colour
// Partial: 	the frame was only partially drawn to screen. Specifcally the animated red line was only partially drawn
// Identical: 	the frame was in the same position as the last frame
// Empty: 		no redline was detected at all in the frame
// Skipped: 	the position of the frame was beyond the expected position
//
// There is a dependency on the timing as to when the frame is animated hence tolerances are used to allow
// for this. 
// 
TInt CTCrpAnim::LogResults()
	{
	TInt result = iFrameStatus.iFrameOK + iFrameStatus.iFramePartial + iFrameStatus.iFrameIdentical + 
				iFrameStatus.iFrameEmpty + iFrameStatus.iFrameTearing + iFrameStatus.iFrameSkipped;
	INFO_PRINTF4(_L("\tAnimation results:  Calculated[%d], Good[%d], Partial[%d]"), result, iFrameStatus.iFrameOK, iFrameStatus.iFramePartial);
	INFO_PRINTF5(_L("\tAnimation results:  Identical[%d], Empty[%d], Tearing[%d], Skipped[%d]"), iFrameStatus.iFrameIdentical, iFrameStatus.iFrameEmpty, iFrameStatus.iFrameTearing, iFrameStatus.iFrameSkipped);
	return result;
	}

/** This test tests the result of drawing an animation and main draw to two windows that overlap.
	The two windows are placed in exactly the same location, so the result of splitting the drawing across them should be "identical".
	Note that when the anim and the draw are on different screens the lines are seen merged over the anim.
**/
void CTCrpAnim::TestOverlappingWindowsL()
	{
	if (!iOverWin)
		{
		INFO_PRINTF1(_L("- Test skipped - transparency not supported"));
		return;
		}
		
	// setup necessary params
	// Note we place the overlapping transparent window (iOverWin) directly on top of the test window (iTestWin)
	iOverWin->BaseWin()->SetPosition(iTestWin->BaseWin()->Position());
		
	enum 
		{
		 EAllBackground=0,
		 EForegroundDraw=1,
		 EForegroundAnim=2,
		 EAllForeGround=3,
		 ECountModes,
		 EFirstMode=EAllBackground,
		};
	
	// test the various permutations of overlapping vs animated windows
	for (TInt mode = EFirstMode ; mode < ECountModes ; mode++)	    
		{
		INFO_PRINTF2(_L("TestOverlappingWindowsL [%d]"), mode);
		TestSpriteLoopL((mode&EForegroundAnim)!=0,(mode&EForegroundDraw)!=0);
		}
	}
	
/**
	This method demonstrates clipping of an animation running behind a transparent window.
	No main window redraw takes place here.
**/	
void CTCrpAnim::DemoClippingWindowsL()
	{
	if (!iOverWin)
		{
		INFO_PRINTF1(_L("- Test skipped - transparency not supported"));
		return;
		}

	// setup test case params. Note we calculate three different positions for the overlapping window
	RWindow* win = iTestWin->Window();
	
	TPoint	screenPos= win->Position();
	TSize screenSize = win->Size();	
	TRect subposition1(screenSize);
	CalcCentredAnimPosition(subposition1, screenSize);

	TPoint testPositions[]=
		{	
		//first test: window clips corner of anim
			TPoint(screenPos.iX+screenSize.iWidth/2-10,screenPos.iY+screenSize.iHeight/2-10),
		//test: window clips all of anim
			TPoint(screenPos.iX+screenSize.iWidth/3,screenPos.iY+screenSize.iHeight/3),
		//test: window clips none of anim
			TPoint(screenPos.iX+screenSize.iWidth*2/3,screenPos.iY+screenSize.iHeight*2/3),
		};

	// calculate roughly number of frames we expect to have drawn
	TInt loopWaitTime = KShortDelayLoop; // time given to allow animation to progress (arbitrary number)
	float expectedFrameCount = 1;
	if (loopWaitTime > KAnimationFrameDelayTime)
		{
		expectedFrameCount = loopWaitTime/KAnimationFrameDelayTime;
		}

	for (TInt ii = 0; ii < ((sizeof testPositions)/(sizeof testPositions[0])) ; ii++)
		{
		// initialise test windows to known state with no active animations
		ResetFrameCounters();
		iTestWin->RemoveAnimation(KUidTestAnimation2);
		iTestWin->SetBlankIt(EFalse);		
		iOverWin->SetBlankIt(ETrue);		
		iOverWin->RemoveAnimation(KUidTestAnimation2);

		// position animation windows
		iTestWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
		iTestWin->SetPosAnimation(KUidTestAnimation2, subposition1);
		iOverWin->BaseWin()->SetPosition(testPositions[ii]); // positions the transparent overlapping window

		// redraw both test windows
		iTestWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
		iOverWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);

		// run the animation for an arbitrary period
		for (TInt loopit = 0 ; loopit < 20 ; loopit++)
			{
			iWaiter->Wait(loopWaitTime);	
			CheckAnimProgressedL(iTestWin,static_cast<TInt>(expectedFrameCount)); // log the frame result
			}

		// calculate & log frame results. Test an acceptable number of frames were successfully animated
		TInt total = LogResults();
		TInt qA = 100*iFrameStatus.iFrameOK/total;
		TEST(qA > KMinGoodFrameThreshold);
		}
	}

/**	In this version, the background window is updated in patches. 
	If the animation intersects the transparent window then the whole transparent window is redrawn.
**/	
void CTCrpAnim::TestClippingWindowsL()
	{
	if (!iOverWin)
		{
		INFO_PRINTF1(_L("- Test skipped - transparency not supported"));
		return;
		}
	// setup test case params. Note we calculate three different positions for the overlapping window
	RWindow* win = iTestWin->Window();
	TPoint screenPos= win->Position();
	TSize screenSize = win->Size();	
	
	TPoint testPositions[]=
		{	
		//first test: window clips corner of anim
			TPoint(screenPos.iX+screenSize.iWidth/2-10,screenPos.iY+screenSize.iHeight/2-10),
		//test: window clips all of anim
			TPoint(screenPos.iX+screenSize.iWidth/3,screenPos.iY+screenSize.iHeight/3),
		//test: window clips none of anim
			TPoint(screenPos.iX+screenSize.iWidth*2/3,screenPos.iY+screenSize.iHeight*2/3),
		};

	for (TInt loopIt = 0; loopIt < ((sizeof testPositions)/(sizeof testPositions[0])) ; loopIt++)
		{
	    iOverWin->BaseWin()->SetPosition(testPositions[loopIt]); // position the overlapping window
	    TestSpriteLoopL(EFalse,EFalse);
		}
	}

/**	This just demonstrates that an animation plays - for about 1 second.
**/
void CTCrpAnim::BasicCRPDemo()
	{
	// draw the animation in two positions
	TSize subsize1 = iTestWin->BaseWin()->Size();
	TRect subposition1(subsize1);
	CalcCentredAnimPosition(subposition1, subsize1);
	
	if (iOverWin)
		{
		iOverWin->BaseWin()->SetPosition(KPointOffsite);	//way away!
		iOverWin->InvalidateAndRedraw(EFalse,EFalse,ETrue);
		}
		
	CCrpAnim *animWin= iTestWin;
	animWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
	animWin->SetPosAnimation(KUidTestAnimation2, subposition1);
	iTestWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
	iBaseWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);

	// allow the animation to play for ~1 second. Purpose is to demonstrate animation to an observer	
	iWaiter->Wait(KShortDelayLoop);
	
	ResetFrameCounters();
	iWaiter->Cancel();
	iTestWin->RemoveAnimation(KUidTestAnimation2);
	}

/**
@SYMTestCaseID		GRAPHICS-WSERV-CRP01-0001

@SYMDEF             DEF100356

@SYMTestCaseDesc    CRP animation test for redraw storing interrupting main draw

@SYMTestPriority    High

@SYMTestStatus      Implemented

@SYMTestActions     Creates a CRP animation and runs it on the server scheduler 
					while also running redraws of the window.
					
					With Redraw storing this has been known to cause problems 
					sharing and resetting the window iDisplayRegion.
					This is evidenced by white areas.


@SYMTestExpectedResults 
					The LHS window shows what the animation should look like just animating,
					while the RHS window demonstrates the simultanious animation and redraw.
					No White patches should be in evidence, and no missing fragments of animation.
					The TEST should detect white patches.
*/
void CTCrpAnim::TestSpriteInterruptsForegroundL()
	{	
	// setup test params
	TSize subsize1(iTestWin->BaseWin()->Size());
	TRect subposition1(subsize1);
	CalcCentredAnimPosition(subposition1, subsize1);
	if (iOverWin)
		{
		iOverWin->BaseWin()->SetPosition(KPointOffsite);	// ensure overlapping transparent window DOESN'T overlap the test window
		}

	// execute test loop
	TestSpriteLoopL(EFalse,EFalse);
	}

void CTCrpAnim::RunTestCaseL(TInt /*aCurTestCase*/)
	{
	_LIT(KTest1,"1: Basic CRP demo");
	_LIT(KTest2,"2: sprite anim interrupts foreground");
	_LIT(KTest3,"3: translucent windows");
	_LIT(KTest4,"4: CRP clipping windows");
	_LIT(KTest5,"5: CRP & redraw clipping windows");
	_LIT(KTest6,"6: CRP Invalidation");

	((CTCrpAnimStep*)iStep)->SetTestStepID(KUnknownSYMTestCaseIDName);
	switch(++iTest->iState)
		{
		case 1:
/**
@SYMTestCaseID		GRAPHICS-WSERV-CRP01-0002
*/
			((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0002"));
			iTest->LogSubTest(KTest1);
			BasicCRPDemo();
			break;
		case 2:
			((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0001"));
			iTest->LogSubTest(KTest2);
			TestSpriteInterruptsForegroundL();
			break;
		case 3:
/**
@SYMTestCaseID		GRAPHICS-WSERV-CRP01-0003
*/
			((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0003"));
			iTest->LogSubTest(KTest3);
			TestOverlappingWindowsL();
			break;
		case 4:
/**
@SYMTestCaseID		GRAPHICS-WSERV-CRP01-0004
*/
			((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0004"));
			iTest->LogSubTest(KTest4);
			DemoClippingWindowsL();
			break;
		case 5:
/**
@SYMTestCaseID		GRAPHICS-WSERV-CRP01-0005
*/
			((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0005"));
			iTest->LogSubTest(KTest5);
			TestClippingWindowsL();
			break;
		case 6:
/**
@SYMTestCaseID		GRAPHICS-WSERV-CRP01-0006
*/
			((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0006"));
			iTest->LogSubTest(KTest6);
			//this testcase is removed, because invalidation is removed from CWsGraphicDrawer destructor (due to flickering)
			break;
		default:
			((CTCrpAnimStep*)iStep)->SetTestStepID(KNotATestSYMTestCaseIDName);
			((CTCrpAnimStep*)iStep)->CloseTMSGraphicsStep();
			TestComplete();
		}
	((CTCrpAnimStep*)iStep)->RecordTestResultL();
	}

//
namespace	//anonymous namespace
	{
//
	CAnimRedrawWindow::CAnimRedrawWindow(CCrpAnim *aAnimWindow, TBool aIsBase) : CTWin(),
		iAnimWindow(aAnimWindow),
		iIsBase(aIsBase)
		{
		}

	CAnimRedrawWindow::~CAnimRedrawWindow()
		{
		}

	void CAnimRedrawWindow::Draw()
		{
		CCrpAnim::Draw(Gc(),Size(),iIsBase,iAnimWindow->iRect,iAnimWindow->iBlankIt,iAnimWindow->iRepeatDrawMax,iAnimWindow->iAlphaValue);
		if (iAnimWindow->iAnimUid!=TUid::Null())
			{
			TheClient->Flush();
			Gc()->DrawWsGraphic(iAnimWindow->iAnimUid,iAnimWindow->iAnimPos,iAnimWindow->iAnimData.Pckg());
			TheClient->Flush();
			}
		}

	//

	CCrpAnim::CCrpAnim(TBool aIsBase, TWinType aWinType) 
	:	iWinType(aWinType), 
		iIsBase(aIsBase),
		iBlankIt(EFalse),
		iRepeatDrawMax(1),
		iAnimUid(TUid::Null()),
		iAlphaValue(ENoTransparency)
		{
		}

	CCrpAnim::~CCrpAnim()
		{
		delete iCtWin;
		}

	void CCrpAnim::ConstructL(const TPoint &aPos, const TSize &aSize, TInt aAlphaValue)
		{
		TDisplayMode reqMode = EColor16MA; //for transparency we need 16ma or 16map mode
		TDisplayMode *pReqMode=&reqMode;	
		switch(iWinType)
			{
			case ERedraw:
				iCtWin = new(ELeave) CAnimRedrawWindow(this, iIsBase);
				break;
			case EBlank:
				iCtWin = new(ELeave) CTBlankWindow();
				break;
			case EBackedUp:
				iCtWin = new(ELeave) CTBackedUpWin(EColor64K);
				pReqMode = NULL;
				break;
			}
		iCtWin->SetUpL(aPos, aSize, TheClient->iGroup, *TheClient->iGc, pReqMode, ETrue);
		if (aAlphaValue != ENoTransparency)
			{
			User::LeaveIfError(Window()->SetTransparencyAlphaChannel());
			//the window itself should be completely transparent, the draw commands will use the alpha value
			Window()->SetBackgroundColor(TRgb(0, 0, 0, 0));
			iAlphaValue = aAlphaValue;
			}
		}

	void CCrpAnim::SetEllipseDrawMode(CGraphicsContext::TDrawMode aEllipseDrawMode)
		{
		iEllipseDrawMode = aEllipseDrawMode;
		}

	void CCrpAnim::DrawEllipse(CBitmapContext *aGc, const TRect &aRect, TInt aAlphaValue)
		{
		if(aAlphaValue != ENoTransparency)
			{
			aGc->SetBrushColor(TRgb(85,85,85, aAlphaValue));
			aGc->SetPenColor(TRgb(170,170,170, aAlphaValue));
			}
		else
			{
			aGc->SetBrushColor(TRgb(85,85,85));
			aGc->SetPenColor(TRgb(170,170,170));
			}
		aGc->SetDrawMode(iEllipseDrawMode);
		aGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
		aGc->DrawEllipse(aRect);
		}

	void CCrpAnim::Draw(CBitmapContext *aGc, const TSize &aSize, TBool aIsBase, const TRect &aRect, TBool aBlankIt,TInt aRepeat, TInt aAlphaValue)
		{
		static TInt sGrey=0;
		sGrey+=3;
		if (sGrey>0x40)
			sGrey-=0x40;		
		sGrey=sGrey^0x20;
		if(aAlphaValue != ENoTransparency)
			{
			aGc->SetBrushColor(TRgb(sGrey, sGrey, sGrey, aAlphaValue));
		    aGc->SetPenColor(TRgb(KRgbGreen.Value(), aAlphaValue));
			}
		else
			{		
			aGc->SetBrushColor(TRgb::Gray256(sGrey));
		    aGc->SetPenColor(KRgbGreen);
			}
		aGc->Clear();
	    TInt xPos=aSize.iHeight,yPos=aSize.iWidth;
	    
	    // The test windows are created relative to screen size. The
	    // number of green lines generated needs to be tied into the
	    // window size to prevent green becoming the dominant colour
	    // when blended with the second animation, which would
	    // prevent the PredominantColour() algorithm from discovering
	    // the red line.
	    TInt yStep = aSize.iHeight/14;
	    TInt xStep = aSize.iWidth/6;
	    
	    //This paint is intentionally complex and slow so that the animation timer is likely to interrupt it.
		if (!aBlankIt)
		 for (TInt nn = 0 ; nn < aRepeat ; nn++)
			for(yPos=0 ; yPos < aSize.iHeight ; yPos += yStep)
				for(xPos=0 ; xPos < aSize.iWidth ; xPos += xStep)
					aGc->DrawLine(aRect.Center(),TPoint(xPos,yPos));
		if (aIsBase)
			DrawEllipse(aGc, aRect, aAlphaValue);			
		}

	//This simple API may need replacing by a list and search if multiple anims are played together
	TWsGraphicAnimation* CCrpAnim::SetAnimation(TUid aUid)
		{	//currently only have 1 animation - it gets replaced. It could get refiused
		iAnimUid=aUid;
		return &iAnimData;	
		}

	TWsGraphicAnimation* CCrpAnim::GetAnimation(TUid aUid)
		{   //currently only have 1 animation
		if (iAnimUid==aUid)
			return &iAnimData;	
		else
			return NULL;
		}
		
	void CCrpAnim::SetPosAnimation(const TUid& aUid, const TRect& aRect)
		{   //currently only have 1 animation
		if (iAnimUid==aUid)
			iAnimPos = aRect;
		}
	
	TRect* CCrpAnim::GetPosAnimation(const TUid& aUid)
		{   //currently only have 1 animation
		if (iAnimUid==aUid)
			return &iAnimPos;	
		else
			return NULL;
		}
	
	TBool CCrpAnim::RemoveAnimation(TUid)
		{
		iAnimUid=TUid::Null();
		iAnimData.Stop(EFalse);		
		return ETrue;
		}	

	void CCrpAnim::DoDraw()
		{
		DoDraw(iBlankIt);
		}

	inline void CCrpAnim::DoDraw(TBool aBlankIt)
		{
		__ASSERT_ALWAYS(iWinType!=EBlank,AutoPanic(EAutoPanicWindowType));
		iCtWin->Gc()->Activate(*Window());
		Draw(iCtWin->Gc(),Size(),iIsBase,iRect,aBlankIt,iRepeatDrawMax,iAlphaValue);
		if (iAnimUid!=TUid::Null())
			iCtWin->Gc()->DrawWsGraphic(iAnimUid,iAnimPos,iAnimData.Pckg());
		iCtWin->Gc()->Deactivate();
		}
		
	void CCrpAnim::DoDrawEllipse()
		{
		__ASSERT_ALWAYS(iWinType!=EBlank,AutoPanic(EAutoPanicWindowType));
		iCtWin->Gc()->Activate(*Window());
		DrawEllipse(iCtWin->Gc(),iRect,iAlphaValue);
		iCtWin->Gc()->Deactivate();
		}

	void CCrpAnim::InvalidateAndRedraw(TBool /*aUseBlankItMember*/,TBool /*aBlankIt*/,TBool aUseRWindowInvalidate,TRect* aRect)
		{
		RWindow& win = *Window();
		if (aRect)
			{
			if (aUseRWindowInvalidate)
				win.Invalidate(*aRect);
			else
				Invalidate(*aRect);
			}
		else
			{
			if (aUseRWindowInvalidate)
				win.Invalidate();
			else
				Invalidate();
			}
		if (aRect)
			win.BeginRedraw(*aRect);
		else
			win.BeginRedraw();
		DoDraw();
		win.EndRedraw();
		TheClient->Flush();
		}
		
	void CCrpAnim::Invalidate(const TRect &aRect)
		{
		TRect rect(aRect);
		rect.Move(iCtWin->Position());
		CTUser::Splat(TheClient,rect,TRgb::Gray256(0));
		}

//
	}	//end anonymous namespace
//
__WS_CONSTRUCT_STEP__(CrpAnim)