First public contribution.
1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // Implements CTCrpAnim
15 // Test CRP animations & their interaction with overlapping transparent/non-transparent windows
16 // & wserv's underlying redraw-store strategies
23 @internalComponent - Internal Symbian test code
26 #include <w32stdgraphic.h>
29 // RUN_SAMPLE_ON_LEFT allows the demo animation to run in the left-hand window during testing.
30 // Used for demonstration purposes only
31 #define RUN_SAMPLE_ON_LEFT
33 namespace //anonymous local scope
35 const TInt KAnimationFrameDelayTime = 50000; // delay in microseconds between frames
36 const TInt KShortDelayLoop = 2*KAnimationFrameDelayTime; // delay time in microseconds used in test cases
37 const TInt KAnimationTotalFrames = 40; // total number of frames in a CWsGraphicBitmapAnimation
38 const TInt KAnimDimension = 40; // animation width/height. We're enforcing a square animation here
39 const TInt KFrameMissedAnimationsThreshold = 10; // maximum number of missed frame steps allowed
40 const TInt KAnimationTearWidthThreshold = 4; // maximum columns permitted between a tear
41 const TInt KMinGoodFrameThreshold = 30; // percentage threshold for number of good frames detected in a test
42 const TInt KMaxXY = 200; // arbitrary maximum size of square used to invalidate a window
43 const TInt KMaxRepeatDraw = 2; // arbitrary value for DrawLine calls during a Draw
44 TUid KUidTestAnimation2 = {0xBAADF00D}; // unique id. for CWsGraphicBitmapAnimation object
45 const TUint32 KWhitePixels = 0xFFFFFFFF; // 32-bit mask value for rgb white
46 const TUint32 KBlackPixels = 0x00000000; // 32-bit value for rgb black
47 const TPoint KPointZero(0,0); // initial point used for animation creation & manipulation (currently 0,0)
48 const TPoint KPointOffsite(1000,1000); // point used to draw off-screen
49 const TDisplayMode KTestDisplayMode = EColor16MU; // display mode used for testing
50 const TInt KFrameStepCalculation = Max(1, KAnimDimension/Max(1, KAnimationTotalFrames)); // determine framestep size in columns
64 class CAnimRedrawWindow : public CTWin
67 CAnimRedrawWindow(CCrpAnim *aAnimWindow, TBool aIsBase);
71 CCrpAnim *iAnimWindow;
75 class CCrpAnim : public CBase
77 friend class CAnimRedrawWindow;
82 EBlank, // note: not currently used in tcrpanim tests
83 EBackedUp // note: not currently used in tcrpanim tests
86 CCrpAnim(TBool aIsBase, TWinType aWinType);
92 void ConstructL(const TPoint &aPos, const TSize &aSize,const TInt aAlphaValue=ENoTransparency);
93 void DoDraw(TBool aBlankIt);
96 inline TSize Size() {return iCtWin->Size();};
97 inline RWindowBase* BaseWin() const {return iCtWin->BaseWin();};
98 inline RWindow* Window() const {return STATIC_CAST(RWindow*, iCtWin->BaseWin());};
99 inline CTBaseWin* CtBaseWin() {return iCtWin;};
100 inline void Invalidate() {CTUser::Splat(TheClient, TRect(iCtWin->Position(), iCtWin->Size()), KRgbGray);};
101 void Invalidate(const TRect &aRect);
102 static void SetEllipseDrawMode(CGraphicsContext::TDrawMode aEllipseDrawMode);
103 void InvalidateAndRedraw(TBool aUseBlankItMember,TBool aBlankIt,TBool aUseRWindowInvalidate,TRect* aRect=NULL);
105 //A bit of an animation interface...
106 //I have written this interface to be amenable to playing multiple animations,
107 //which I think needs testing,
108 //but the underlying implementation assumes one animation at present.
109 //Your mission, should you choose to accept it, ....
111 void SetPosAnimation(const TUid& aUid, const TRect& aRect);
112 TRect* GetPosAnimation(const TUid& aUid);
113 TWsGraphicAnimation* SetAnimation(TUid);
114 TWsGraphicAnimation* GetAnimation(TUid);
115 TBool RemoveAnimation(TUid);
116 inline void SetBlankIt(TBool aNewVal) {iBlankIt = aNewVal;};
117 inline void SetRepeatDrawMax(TInt aVal) {iRepeatDrawMax = aVal;};
119 static void Draw(CBitmapContext *aGc, const TSize &aSize, TBool aIsBase,const TRect &aRect, TBool aBlankIt,TInt aRepeat, TInt aAlphaValue);
120 static void DrawEllipse(CBitmapContext *aGc, const TRect &aRect, TInt aAlphaValue);
127 static CGraphicsContext::TDrawMode iEllipseDrawMode;
129 TWsGraphicAnimation iAnimData;
134 /* Using this time delay class in order to allow animations to play in our draw.
135 User::Wait does not allow the draw to occur (aparrently)
136 Note when using this time-delay class: because other active objects can perform part of their
137 processing whilst we wait, wrapping calls to this in __UHEAP_MARK / __UHEAP_MARKEND
138 is likely to fail. The data providers and animators are a major cause of this.
140 class CActiveWait : public CActive
143 static CActiveWait* NewL();
145 void Wait(TInt aDelay);
149 TInt RunError(TInt aError);
158 CActiveWait* CActiveWait::NewL()
160 CActiveWait* self = new (ELeave) CActiveWait;
161 CleanupStack::PushL(self);
163 CleanupStack::Pop(self);
167 void CActiveWait::ConstructL()
169 User::LeaveIfError(iTimer.CreateLocal());
170 CActiveScheduler::Add(this);
173 CActiveWait::CActiveWait() : CActive(EPriorityNormal)
175 iFromTime.HomeTime();
178 CActiveWait::~CActiveWait()
184 void CActiveWait::DoCancel()
187 CActiveScheduler::Stop();
190 void CActiveWait::RunL()
192 CActiveScheduler::Stop();
195 TInt CActiveWait::RunError(TInt aError)
197 return aError; // exists so a break point can be placed on it.
200 /* Note when using this : because other active objects can perform part of their
201 processing whilst we wait, wrapping calls to this in __UHEAP_MARK / __UHEAP_MARKEND
202 is likely to fail. The data providers and animators are a major cause of this.
204 void CActiveWait::Wait(TInt aDelay)
206 iTimer.After(iStatus, aDelay);
208 CActiveScheduler::Start();
210 CGraphicsContext::TDrawMode CCrpAnim::iEllipseDrawMode;
213 } //end anonymous local scope
216 /** This fn allocates an animation frame of the specified dimensions.
217 Not tested outside the current limited parameter set (16/2/2007).
218 Note the use of 32-bit integers for pixel/colour values. Using display mode lower than 24bpp may not produce correct results
219 My attempt to write animation generating code that avoids CIclLoader and Decoder class.
220 @param aDelayUs the display time for the frame
221 @param aImageType Colour format for colour plane. 24MA currently not flagged correctly I expect.
222 @param aMaskType Format for mask. ENone for no mask.
223 @param aImageSize Width/height of bitmap area
224 @param aImageOffset Optional offset for bitmap area
225 @param aTotalSize Optional width/height of whole animation (I think)
226 @return CFrame filled in with allocated bitmaps. The get methods for the bitmaps return const type.
228 static CWsGraphicBitmapAnimation::CFrame* NewFrameLC(TInt aDelayUs,TDisplayMode aImageType,TDisplayMode aMaskType,const TSize& aImageSize,const TPoint& aImageOffset=KPointZero,const TSize& aTotalSize=TSize(0,0))
231 info.iFrameCoordsInPixels = TRect(aImageOffset,aImageSize);
232 info.iFrameSizeInTwips = aImageSize; //this is zero in the gif loader
233 info.iDelay = TTimeIntervalMicroSeconds(aDelayUs);
234 info.iFlags = TFrameInfo::EColor|TFrameInfo::ELeaveInPlace|TFrameInfo::EUsesFrameSizeInPixels;
235 if (aMaskType != ENone)
237 info.iFlags|=TFrameInfo::ETransparencyPossible;
239 if ((aTotalSize.iHeight > 0) && (aTotalSize.iWidth > 0))
241 // restrict the size of the frame to specified size of the animation
242 info.iOverallSizeInPixels = aTotalSize;
246 // assign the size of the frame to the size of the entire bitmap area
247 info.iOverallSizeInPixels = info.iFrameCoordsInPixels.iBr.AsSize();
249 info.iFrameDisplayMode = aImageType;
250 info.iBackgroundColor = KRgbGreen;
252 CWsGraphicBitmapAnimation::CFrame* frame = CWsGraphicBitmapAnimation::CFrame::NewL();
253 CleanupStack::PushL(frame);
254 frame->SetFrameInfo(info);
255 CFbsBitmap* bitmap = new(ELeave) CFbsBitmap;
256 frame->SetBitmap(bitmap); //takes ownership
257 TSize frameInfoSize = info.iFrameCoordsInPixels.Size();
258 User::LeaveIfError(bitmap->Create(frameInfoSize, aImageType));
259 if((TFrameInfo::EAlphaChannel|TFrameInfo::ETransparencyPossible) & info.iFlags)
261 CFbsBitmap* mask = new(ELeave) CFbsBitmap;
262 frame->SetMask(mask); //takes ownership
263 User::LeaveIfError(mask->Create(frameInfoSize, aMaskType));
269 // function called back by TCleanupItem frameListCleanup from within CreateAnimFramesL(..) method
271 void CleanupFrameList(TAny* aPtr)
273 RPointerArray<CWsGraphicBitmapAnimation::CFrame>* ptrArray = STATIC_CAST(RPointerArray<CWsGraphicBitmapAnimation::CFrame>*, aPtr);
274 ptrArray->ResetAndDestroy();
278 /** Helper function for making animation frames.
279 //Called from CreateAnimL(...)
280 @param aDelayUs the delay between frames
281 @param aNumFrames number of frames (approx - image width is a factor)
282 @param aImageType colour format of colour data. This may not work properly for non-32-bit, but I haven't fully understood TBitmapUtil documentation.
283 @param aMaskType format for mask - ENone for no mask.
284 @param aImageSize width/height of animation
285 @param aBgCol background colour for image non-masked areas. Masked areas are black.
286 @param aFgCol foreground colour of animating area
287 @param aFrames frames that the animation is constructed from
289 static void CreateAnimFramesL(TInt aDelayUs,TInt aNumFrames,TDisplayMode aImageType,TDisplayMode aMaskType,TSize aImageSize,TRgb aBgCol,TRgb aFgCol, RPointerArray<CWsGraphicBitmapAnimation::CFrame>& aFrames)
291 const TInt animWH = aImageSize.iWidth;
292 const TInt animStep = Max(1,animWH/Max(1,aNumFrames)); //note this intentionally rounds down to avoid overflows
293 for (TInt ii = 0 ; ii < animWH ; ii += animStep)
295 CWsGraphicBitmapAnimation::CFrame* frame = NewFrameLC(aDelayUs,aImageType,aMaskType,aImageSize,KPointZero,aImageSize);
296 aFrames.AppendL(frame);
297 CleanupStack::Pop(frame);
298 TBitmapUtil utilMask(CONST_CAST(CFbsBitmap*, frame->Mask()));
299 TBitmapUtil utilCol(CONST_CAST(CFbsBitmap*, frame->Bitmap()));
300 utilCol.Begin(KPointZero);
302 // cycle through the frame's actual bitmap & assign each pixel a value identical to the specified colours
303 TUint32 colback=aBgCol.Internal();
304 TUint32 colfront=aFgCol.Internal();
307 for (row = 0 ; row < aImageSize.iHeight ; row++)
309 utilCol.SetPos(TPoint(0, row));
310 for (col = 0 ; col < aImageSize.iWidth ; col++)
312 utilCol.SetPixel(colback);
315 utilCol.SetPos(TPoint(ii, row));
316 for (col = 0 ; col < animStep ; col++) //Note I rely on intentional rounding down here!
318 utilCol.SetPixel(colfront);
325 // cycle through each pixel of the frame's mask & assign a default pixel a colour value
326 utilMask.Begin(KPointZero);
327 for (row = 0 ; row < aImageSize.iHeight ; row++)
329 utilMask.SetPos(TPoint(0,row));
330 for (col = 0 ; col < aImageSize.iWidth ; col++)
332 utilMask.SetPixel(KWhitePixels);
337 const TInt maxmaskWidth = Min(8,Max(animWH/3,2));
339 //cut the corners off the mask
340 for (row = 0 ; row < maxmaskWidth ; row++)
342 TInt currentX = maxmaskWidth - row;
343 TInt xPos = KErrNone;
345 utilCol.SetPos(TPoint(0,row));
346 utilMask.SetPos(TPoint(0,row));
347 for(xPos = currentX ; xPos >= 0 ; xPos--)
349 utilCol.SetPixel(KBlackPixels);
351 utilMask.SetPixel(KBlackPixels);
355 utilCol.SetPos(TPoint(animWH - 1, row));
356 utilMask.SetPos(TPoint(animWH - 1, row));
357 for(xPos = currentX ; xPos >= 0 ; xPos--)
359 utilCol.SetPixel(KBlackPixels);
361 utilMask.SetPixel(KBlackPixels);
365 utilCol.SetPos(TPoint(0, animWH - 1 - row));
366 utilMask.SetPos(TPoint(0, animWH - 1 - row));
367 for(xPos = currentX ; xPos >= 0 ; xPos--)
369 utilCol.SetPixel(KBlackPixels);
371 utilMask.SetPixel(KBlackPixels);
375 utilCol.SetPos(TPoint(animWH - 1, animWH - 1 - row));
376 utilMask.SetPos(TPoint(animWH - 1, animWH - 1 - row));
377 for(xPos = currentX ; xPos >= 0 ; xPos--)
379 utilCol.SetPixel(KBlackPixels);
381 utilMask.SetPixel(KBlackPixels);
391 /** My attempt to write animation generating code that avoids CIclLoader and Decoder class.
392 //It is better if this test class used it's own generated animation
393 //rather than relying on the GIF loader in order to reduce the cross-dependencies.
394 //The animation generated is a simple vertical line moving from left to right.
395 //To prove the masking, I cut the corners off.
396 @param aDelayUs the delay between frames
397 @param aNumFrames number of frames (approx - image width is a factor)
398 @param aImageType colour format of colour data. This may not work properly for non-32-bit, but I haven't fully understood TBitmapUtil documentation.
399 @param aMaskType format for mask - ENone for no mask.
400 @param aImageSize width/height of animation
401 @param aBgCol background colour for image non-masked areas. Masked areas are black.
402 @param aFgCol foreground colour of animating area
403 @param aTUid TUid assigned to animation
404 @return CWsGraphicBitmapAnimation allocated to represent the final animation
406 static CWsGraphicBitmapAnimation* CreateAnimL(TInt aDelayUs,TInt aNumFrames,TDisplayMode aImageType,TDisplayMode aMaskType,TSize aImageSize,TRgb aBgCol,TRgb aFgCol,TUid& aTUid)
408 RPointerArray<CWsGraphicBitmapAnimation::CFrame> frames;
409 TCleanupItem frameListCleanup(CleanupFrameList, &frames);
410 CleanupStack::PushL(frameListCleanup);
412 CreateAnimFramesL(aDelayUs, aNumFrames, aImageType, aMaskType, aImageSize,aBgCol, aFgCol, frames);
414 CWsGraphicBitmapAnimation* anim = CWsGraphicBitmapAnimation::NewL(aTUid,frames.Array());
415 CleanupStack::PopAndDestroy(&frames);
420 // Describes the pure colour of the RGB value. yellow/magenta/cyan set 2 bits. White/grey is seperately flagged.
421 // This method attempts to determine the strongest primary colour present in any given pixel.
422 // Note: The algorithm used is known to work for the current test cases only but requires careful review
423 // for anyone making additional changes to tcrpanim. Given time, improved algorithm should be developed
424 // to replace the current one
426 TUint PredominantColour(TUint aCol)
427 { //I don't like all these ifs, but I don't see an easy alternative
428 //Possibly a bit look-up of the deltas from average would work
429 //(ignoring the bottom 5 bits =32, not 0x30=48. Ignore bottom 4 bits and accept 3-same answers, or divide by delta?)
431 const TInt Kdelta=0x30;
432 TInt red=(aCol&0x00ff0000)>>16;
433 TInt green=(aCol&0x0000ff00)>>8;
434 TInt blue=(aCol&0x000000ff);
435 TInt ave=((red+green+blue)*(65536/3))>>16;
436 TBool rOverA=(red>ave);
437 TBool gOverA=(green>ave);
438 TBool bOverA=(blue>ave);
439 TInt numOverAve=(rOverA?1:0)+(gOverA?1:0)+(bOverA?1:0);
447 if ((green-blue)>-Kdelta && (green-blue)<Kdelta)
456 if (green>ave-Kdelta && blue>ave-Kdelta)
469 if (green>ave+Kdelta)
471 if ((blue-red)>-Kdelta && (blue-red)<Kdelta)
480 if (red>ave-Kdelta && blue>ave-Kdelta)
493 if ((green-red)>-Kdelta && (green-red)<Kdelta)
502 if (red>ave-Kdelta && green>ave-Kdelta)
516 if ((green-blue)>-Kdelta && (green-blue)<Kdelta)
517 return EDetGreen|EDetBlue;
525 if (blue<ave+Kdelta && green<ave+Kdelta)
537 if (green<ave-Kdelta)
539 if ((blue-red)>-Kdelta && (blue-red)<Kdelta)
540 return EDetRed|EDetBlue;
548 if (blue<ave+Kdelta && red<ave+Kdelta)
561 if ((green-red)>-Kdelta && (green-red)<Kdelta)
562 return EDetGreen|EDetRed;
570 if (red<ave+Kdelta && green<ave+Kdelta)
583 Helper fn to ensure I put the anims in the same place each time...
585 void CalcCentredAnimPosition(TRect& aRect,const TSize& aWinSize)
587 aRect.Shrink(aWinSize.iWidth*3/8,aWinSize.iHeight*4/10);
590 CTCrpAnim::CTCrpAnim(CTestStep* aStep) :
591 CTWsGraphicsBase(aStep)
595 void CTCrpAnim::ConstructL()
597 TheClient->iGroup->WinTreeNode()->SetOrdinalPosition(0);
598 iRedrawWin=new(ELeave) CCrpAnim(EFalse, CCrpAnim::ERedraw);
599 iBaseWin=new(ELeave) CCrpAnim(EFalse, CCrpAnim::ERedraw);
600 iOverWin=new(ELeave) CCrpAnim(EFalse, CCrpAnim::ERedraw);
602 TSize screenSize=TheClient->iGroup->Size();
603 TInt winWidth=(screenSize.iWidth/3)-10;
604 TInt winHeight=screenSize.iHeight-10;
605 TSize windowSize(winWidth,winHeight);
607 iRedrawWin->ConstructL(TPoint(screenSize.iWidth/3*2+5,5), windowSize);
608 iBaseWin->ConstructL(TPoint(screenSize.iWidth/3+5,5), windowSize);
610 //Create a transparent window that exactly overlaps the test window
611 //If transparency is not supported the leave causes the window to be destroyed and set to NULL.
612 //There is a test for transparency supported, but that simply creates a temp window to test anyway...
614 //Note that when I originally wrote this test to fix PDEF101991, it generated white areas that I detected.
615 //However, if this transparent window used for extended tests is created over the test window,
616 //that somehow stops the white fill from occurring.
617 //The fault still occurs, but the previous screen contents are left behind.
618 //So now this window is created at an off-screen location.
619 TRAPD(err, iOverWin->ConstructL(KPointOffsite, windowSize, 0x80); iOverWin->SetBlankIt(ETrue); iOverWin->SetRepeatDrawMax(KMaxRepeatDraw););
626 iTestWin = iRedrawWin;
627 iTestWin->SetRepeatDrawMax(KMaxRepeatDraw);
628 iBaseWin->SetRepeatDrawMax(KMaxRepeatDraw);
630 // create animation object & share it with everyone
631 iAnim = CreateAnimL(KAnimationFrameDelayTime,KAnimationTotalFrames,KTestDisplayMode,EGray256,TSize(KAnimDimension, KAnimDimension),KRgbBlue,KRgbRed,KUidTestAnimation2);
634 User::Leave(KErrNoMemory);
636 iAnim->ShareGlobally();
638 // calculate minimum length of the red line
639 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
640 iMinimumCalcRedLine = KAnimDimension - maxmaskHeight*2; // the height of the image minus the two cut corners
642 // create the timer object
643 iWaiter = CActiveWait::NewL();
645 // create screen bitmap object & scanline buffer
646 iScreenBitmap = new (ELeave) CFbsBitmap;
647 User::LeaveIfError(iScreenBitmap->Create(TSize(KAnimDimension, KAnimDimension), KTestDisplayMode));
648 TInt bufLength = iScreenBitmap->ScanLineLength(windowSize.iHeight, KTestDisplayMode);
649 iScanlineBuf = HBufC8::NewL(bufLength);
651 #ifdef RUN_SAMPLE_ON_LEFT
653 // play animation on iBaseWin window
654 iBaseWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
655 TSize subsize1 = iTestWin->BaseWin()->Size();
656 TRect subposition1(subsize1);
657 CalcCentredAnimPosition(subposition1, subsize1);
658 iBaseWin->SetPosAnimation(KUidTestAnimation2, subposition1);
659 iBaseWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
664 CTCrpAnim::~CTCrpAnim()
671 // destroy the animation object
672 iAnim->UnShareGlobally();
679 // destroy the timer object
685 // destroy the screen capture of the animation
686 delete iScreenBitmap;
687 iScreenBitmap = NULL;
691 // destroy the scanline buffer
699 // This method checks the animation contained in the aAnimWin window has progressed. That is
700 // that it's drawn a sufficient number of concurrent frames to screen & the animation is
701 // drawn properly to screen
702 // returns a Bool identifying whether the animation is considered 'good' or not
704 void CTCrpAnim::CheckAnimProgressedL(CAnonAnimWindow* aAnimWin, TInt aAdditionalFrameCount, TBool aCaptureFrameResult)
706 TBool goodAnimation = ETrue;
708 // retrieve the rect from the screen's bitmap that contains the animation
709 CWsScreenDevice* screen = TheClient->iScreen;
710 TRect animPos = *aAnimWin->GetPosAnimation(KUidTestAnimation2);
711 CTBaseWin* bWin = aAnimWin->CtBaseWin();
712 animPos.Move(bWin->Position());
713 User::LeaveIfError(screen->CopyScreenToBitmap(iScreenBitmap, animPos));
715 TInt frameNum = DetermineApproxFrameNum(iScreenBitmap, aCaptureFrameResult); // determines the frame Number & checks quality of animation (no tearing, etc)
716 TBool frameIdentified=(frameNum>=0);
718 if (aCaptureFrameResult)
722 if (iPreviousFrameNum != KErrNotFound)
724 if (iPreviousFrameNum < frameNum)
726 TInt frameStep = KFrameStepCalculation * aAdditionalFrameCount;
727 iPreviousFrameNum += frameStep; // move to our *expected* framenumber
728 if (frameNum > iPreviousFrameNum)
730 // the frame number is ahead of it's expected position
731 // This suggests we've possibly missed animating a frame in wserv
732 // or test code isn't getting a chance to execute as crp animations taking all cpu cycles
733 // If its significantly outside norms, we log the fact (as a performance metric)
734 TInt performance = ((frameNum - iPreviousFrameNum) / frameStep);
735 if (performance > KFrameMissedAnimationsThreshold)
737 iFrameStatus.iFrameSkipped++;
738 goodAnimation = EFalse;
741 // else we're animating above an acceptable threshold
743 else if (iPreviousFrameNum == frameNum) // potentially not animating anymore
745 iFrameStatus.iFrameIdentical++;
746 goodAnimation = EFalse;
748 // else animation is progressing fine
750 // ignore iPreviousFrameNum == KErrNotFound
754 goodAnimation = EFalse; // couldn't id the red line
759 iFrameStatus.iFrameOK++;
762 // else we were only interested in calculating the frameNum
763 iPreviousFrameNum = frameNum;
767 // method to estimate the framenumber based on the location of the thin, red line.
768 // Also checks whether tearing of the animation has occured or the animation
769 // is only partially drawn.
770 // These are known issues with wserv animation performance & so we give some allowance for error
772 TInt CTCrpAnim::DetermineApproxFrameNum(CFbsBitmap* aBitmap, TBool aCaptureFrameResult)
774 TInt colFirstTear = KErrNotFound; // column id'ing the first tear in the vertical line
775 TPtr8 des = iScanlineBuf->Des(); // ptr to the scanline buffer
777 // locate the thin, red line in the bitmap
778 for (TInt xPos = 0 ; xPos < aBitmap->SizeInPixels().iWidth ; xPos++)
780 aBitmap->GetVerticalScanLine(des, xPos, EColor16MA);
781 TUint32* pixel = (TUint32*) des.Ptr();
782 TInt colour = KErrNone;
784 for (TInt ii = 0 ; ii < aBitmap->SizeInPixels().iHeight ; ii++)
786 colour = PredominantColour(*pixel);
787 if (colour & EDetRed)
789 if (colFirstTear < 0)
791 // check the length of the red line is a good length
792 pixel += (iMinimumCalcRedLine - 1); // minus the one pixel to position on last pixel in red line
793 colour = PredominantColour(*pixel);
794 if (colour & EDetRed)
799 else // we've detected first part of a torn line
806 // located second part of torn line
807 if ((xPos - colFirstTear) > KAnimationTearWidthThreshold)
809 if (aCaptureFrameResult)
811 iFrameStatus.iFrameTearing++;
822 if (aCaptureFrameResult)
824 if (colFirstTear < 0)
826 iFrameStatus.iFrameEmpty++; // we never located any red line at all
830 iFrameStatus.iFramePartial++; // we only located a single, small part of the red line
836 /** This internal loop tests that the animation and the foreground interact correctly
837 The primary test is that the outline of the animation
838 intersects the lines drawn on the foreground correctly, compared to a reference version.
839 The iBaseWin is already showing this reference anim.
840 If the animation is not drawn, or the foreground is wiped, then this test will fail.
842 void CTCrpAnim::TestSpriteLoopL(TBool aAnimForeground,TBool aDrawForeground)
844 _LIT(KForegroundInfo,"TestSpriteLoop animForeground [%d] drawForeground [%d]");
845 INFO_PRINTF3(KForegroundInfo, aAnimForeground, aDrawForeground);
847 if (!iOverWin && (aAnimForeground || aDrawForeground))
849 User::Leave(KErrGeneral); // unable to run this test without iOverWin
852 ResetFrameCounters();
853 iTestWin->RemoveAnimation(KUidTestAnimation2);
854 iTestWin->SetBlankIt(ETrue);
857 iOverWin->RemoveAnimation(KUidTestAnimation2);
858 iOverWin->SetBlankIt(ETrue);
861 // determine which window holds the animation, & which will be invalidated with progressively larger rects
862 CCrpAnim* animWin=aAnimForeground?iOverWin:iTestWin;
863 CCrpAnim* paintWin=aDrawForeground?iOverWin:iTestWin;
864 paintWin->SetBlankIt(EFalse);
866 // set & play the animation on the specified window (animWin)
867 animWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
868 TSize subsize1 = paintWin->BaseWin()->Size();
869 TRect subposition1(subsize1);
870 CalcCentredAnimPosition(subposition1, subsize1);
871 animWin->SetPosAnimation(KUidTestAnimation2, subposition1);
873 #ifdef RUN_SAMPLE_ON_LEFT
874 // play the demo animation in the left-hand window also
875 iBaseWin->InvalidateAndRedraw(ETrue, EFalse, ETrue);
878 iTestWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
881 iOverWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
884 // invalidate increasingly larger squares on paintWin
885 // note, some fully overlap the animation, some partially overlap, and some don't overlap at all
886 TInt invalidateWaitTime=KAnimationFrameDelayTime*3/4; // microseconds
887 TInt temp = KErrNotFound;
888 for (TInt step=30;step<KMaxXY;step+=30)
890 for (TInt xx=0;xx<KMaxXY;xx+=step)
892 for (TInt yy=10;yy<KMaxXY;yy+=step)
894 // calculate rectangle & invalidate paintWin with it
895 TRect invalidRect(xx,yy,xx+step,yy+step);
896 paintWin->InvalidateAndRedraw(ETrue,EFalse,ETrue,&invalidRect);
898 // calculate any additional frames that may be drawn by above. Note intentionally ignore frame result
899 temp = iPreviousFrameNum;
900 CheckAnimProgressedL(animWin, 1, EFalse);
902 //new defect DEF101896: Test runs faster with this line removed, but there is evident tearing
903 iWaiter->Wait(invalidateWaitTime); //DEF101896 search string: //interrupt_foreground_draw
905 if (temp == iPreviousFrameNum)
907 // give wserv more time to animate the frame
908 iWaiter->Wait(invalidateWaitTime);
910 CheckAnimProgressedL(animWin, 1); // calculate the frame drawn. Capture frame result
915 // determine whether the animation was successful (ie: enough Good frames were detected) or not
916 // Note KMinGoodFrameThreshold is essentially an arbitrary number. This can be adjusted to accommodate
917 // performance requirements as needed
919 TInt quality = 100*iFrameStatus.iFrameOK/temp;
920 TEST(quality > KMinGoodFrameThreshold);
922 ResetFrameCounters();
924 iTestWin->RemoveAnimation(KUidTestAnimation2);
925 iTestWin->SetBlankIt(ETrue);
928 iOverWin->RemoveAnimation(KUidTestAnimation2);
929 iOverWin->SetBlankIt(ETrue);
934 // resets the frame trackers to intial values
936 void CTCrpAnim::ResetFrameCounters()
938 iPreviousFrameNum = KErrNotFound;
939 iFrameStatus.iFrameOK = 0;
940 iFrameStatus.iFramePartial = 0;
941 iFrameStatus.iFrameIdentical = 0;
942 iFrameStatus.iFrameEmpty = 0;
943 iFrameStatus.iFrameTearing = 0;
944 iFrameStatus.iFrameSkipped = 0;
948 // Log the current frame results & return the total number of frame calculations
950 // Calculated : the total number of frame-checks run
951 // Good: the frame was successfully drawn to screen & within specified tolerances for tearing, expected position & colour
952 // Partial: the frame was only partially drawn to screen. Specifcally the animated red line was only partially drawn
953 // Identical: the frame was in the same position as the last frame
954 // Empty: no redline was detected at all in the frame
955 // Skipped: the position of the frame was beyond the expected position
957 // There is a dependency on the timing as to when the frame is animated hence tolerances are used to allow
960 TInt CTCrpAnim::LogResults()
962 TInt result = iFrameStatus.iFrameOK + iFrameStatus.iFramePartial + iFrameStatus.iFrameIdentical +
963 iFrameStatus.iFrameEmpty + iFrameStatus.iFrameTearing + iFrameStatus.iFrameSkipped;
964 INFO_PRINTF4(_L("\tAnimation results: Calculated[%d], Good[%d], Partial[%d]"), result, iFrameStatus.iFrameOK, iFrameStatus.iFramePartial);
965 INFO_PRINTF5(_L("\tAnimation results: Identical[%d], Empty[%d], Tearing[%d], Skipped[%d]"), iFrameStatus.iFrameIdentical, iFrameStatus.iFrameEmpty, iFrameStatus.iFrameTearing, iFrameStatus.iFrameSkipped);
969 /** This test tests the result of drawing an animation and main draw to two windows that overlap.
970 The two windows are placed in exactly the same location, so the result of splitting the drawing across them should be "identical".
971 Note that when the anim and the draw are on different screens the lines are seen merged over the anim.
973 void CTCrpAnim::TestOverlappingWindowsL()
977 INFO_PRINTF1(_L("- Test skipped - transparency not supported"));
981 // setup necessary params
982 // Note we place the overlapping transparent window (iOverWin) directly on top of the test window (iTestWin)
983 iOverWin->BaseWin()->SetPosition(iTestWin->BaseWin()->Position());
992 EFirstMode=EAllBackground,
995 // test the various permutations of overlapping vs animated windows
996 for (TInt mode = EFirstMode ; mode < ECountModes ; mode++)
998 INFO_PRINTF2(_L("TestOverlappingWindowsL [%d]"), mode);
999 TestSpriteLoopL((mode&EForegroundAnim)!=0,(mode&EForegroundDraw)!=0);
1004 This method demonstrates clipping of an animation running behind a transparent window.
1005 No main window redraw takes place here.
1007 void CTCrpAnim::DemoClippingWindowsL()
1011 INFO_PRINTF1(_L("- Test skipped - transparency not supported"));
1015 // setup test case params. Note we calculate three different positions for the overlapping window
1016 RWindow* win = iTestWin->Window();
1018 TPoint screenPos= win->Position();
1019 TSize screenSize = win->Size();
1020 TRect subposition1(screenSize);
1021 CalcCentredAnimPosition(subposition1, screenSize);
1023 TPoint testPositions[]=
1025 //first test: window clips corner of anim
1026 TPoint(screenPos.iX+screenSize.iWidth/2-10,screenPos.iY+screenSize.iHeight/2-10),
1027 //test: window clips all of anim
1028 TPoint(screenPos.iX+screenSize.iWidth/3,screenPos.iY+screenSize.iHeight/3),
1029 //test: window clips none of anim
1030 TPoint(screenPos.iX+screenSize.iWidth*2/3,screenPos.iY+screenSize.iHeight*2/3),
1033 // calculate roughly number of frames we expect to have drawn
1034 TInt loopWaitTime = KShortDelayLoop; // time given to allow animation to progress (arbitrary number)
1035 float expectedFrameCount = 1;
1036 if (loopWaitTime > KAnimationFrameDelayTime)
1038 expectedFrameCount = loopWaitTime/KAnimationFrameDelayTime;
1041 for (TInt ii = 0; ii < ((sizeof testPositions)/(sizeof testPositions[0])) ; ii++)
1043 // initialise test windows to known state with no active animations
1044 ResetFrameCounters();
1045 iTestWin->RemoveAnimation(KUidTestAnimation2);
1046 iTestWin->SetBlankIt(EFalse);
1047 iOverWin->SetBlankIt(ETrue);
1048 iOverWin->RemoveAnimation(KUidTestAnimation2);
1050 // position animation windows
1051 iTestWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
1052 iTestWin->SetPosAnimation(KUidTestAnimation2, subposition1);
1053 iOverWin->BaseWin()->SetPosition(testPositions[ii]); // positions the transparent overlapping window
1055 // redraw both test windows
1056 iTestWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
1057 iOverWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
1059 // run the animation for an arbitrary period
1060 for (TInt loopit = 0 ; loopit < 20 ; loopit++)
1062 iWaiter->Wait(loopWaitTime);
1063 CheckAnimProgressedL(iTestWin,static_cast<TInt>(expectedFrameCount)); // log the frame result
1066 // calculate & log frame results. Test an acceptable number of frames were successfully animated
1067 TInt total = LogResults();
1068 TInt qA = 100*iFrameStatus.iFrameOK/total;
1069 TEST(qA > KMinGoodFrameThreshold);
1073 /** In this version, the background window is updated in patches.
1074 If the animation intersects the transparent window then the whole transparent window is redrawn.
1076 void CTCrpAnim::TestClippingWindowsL()
1080 INFO_PRINTF1(_L("- Test skipped - transparency not supported"));
1083 // setup test case params. Note we calculate three different positions for the overlapping window
1084 RWindow* win = iTestWin->Window();
1085 TPoint screenPos= win->Position();
1086 TSize screenSize = win->Size();
1088 TPoint testPositions[]=
1090 //first test: window clips corner of anim
1091 TPoint(screenPos.iX+screenSize.iWidth/2-10,screenPos.iY+screenSize.iHeight/2-10),
1092 //test: window clips all of anim
1093 TPoint(screenPos.iX+screenSize.iWidth/3,screenPos.iY+screenSize.iHeight/3),
1094 //test: window clips none of anim
1095 TPoint(screenPos.iX+screenSize.iWidth*2/3,screenPos.iY+screenSize.iHeight*2/3),
1098 for (TInt loopIt = 0; loopIt < ((sizeof testPositions)/(sizeof testPositions[0])) ; loopIt++)
1100 iOverWin->BaseWin()->SetPosition(testPositions[loopIt]); // position the overlapping window
1101 TestSpriteLoopL(EFalse,EFalse);
1105 /** This just demonstrates that an animation plays - for about 1 second.
1107 void CTCrpAnim::BasicCRPDemo()
1109 // draw the animation in two positions
1110 TSize subsize1 = iTestWin->BaseWin()->Size();
1111 TRect subposition1(subsize1);
1112 CalcCentredAnimPosition(subposition1, subsize1);
1116 iOverWin->BaseWin()->SetPosition(KPointOffsite); //way away!
1117 iOverWin->InvalidateAndRedraw(EFalse,EFalse,ETrue);
1120 CCrpAnim *animWin= iTestWin;
1121 animWin->SetAnimation(KUidTestAnimation2)->Play(ETrue);
1122 animWin->SetPosAnimation(KUidTestAnimation2, subposition1);
1123 iTestWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
1124 iBaseWin->InvalidateAndRedraw(ETrue,EFalse,ETrue);
1126 // allow the animation to play for ~1 second. Purpose is to demonstrate animation to an observer
1127 iWaiter->Wait(KShortDelayLoop);
1129 ResetFrameCounters();
1131 iTestWin->RemoveAnimation(KUidTestAnimation2);
1135 @SYMTestCaseID GRAPHICS-WSERV-CRP01-0001
1139 @SYMTestCaseDesc CRP animation test for redraw storing interrupting main draw
1141 @SYMTestPriority High
1143 @SYMTestStatus Implemented
1145 @SYMTestActions Creates a CRP animation and runs it on the server scheduler
1146 while also running redraws of the window.
1148 With Redraw storing this has been known to cause problems
1149 sharing and resetting the window iDisplayRegion.
1150 This is evidenced by white areas.
1153 @SYMTestExpectedResults
1154 The LHS window shows what the animation should look like just animating,
1155 while the RHS window demonstrates the simultanious animation and redraw.
1156 No White patches should be in evidence, and no missing fragments of animation.
1157 The TEST should detect white patches.
1159 void CTCrpAnim::TestSpriteInterruptsForegroundL()
1161 // setup test params
1162 TSize subsize1(iTestWin->BaseWin()->Size());
1163 TRect subposition1(subsize1);
1164 CalcCentredAnimPosition(subposition1, subsize1);
1167 iOverWin->BaseWin()->SetPosition(KPointOffsite); // ensure overlapping transparent window DOESN'T overlap the test window
1170 // execute test loop
1171 TestSpriteLoopL(EFalse,EFalse);
1174 void CTCrpAnim::RunTestCaseL(TInt /*aCurTestCase*/)
1176 _LIT(KTest1,"1: Basic CRP demo");
1177 _LIT(KTest2,"2: sprite anim interrupts foreground");
1178 _LIT(KTest3,"3: translucent windows");
1179 _LIT(KTest4,"4: CRP clipping windows");
1180 _LIT(KTest5,"5: CRP & redraw clipping windows");
1181 _LIT(KTest6,"6: CRP Invalidation");
1183 ((CTCrpAnimStep*)iStep)->SetTestStepID(KUnknownSYMTestCaseIDName);
1184 switch(++iTest->iState)
1188 @SYMTestCaseID GRAPHICS-WSERV-CRP01-0002
1190 ((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0002"));
1191 iTest->LogSubTest(KTest1);
1195 ((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0001"));
1196 iTest->LogSubTest(KTest2);
1197 TestSpriteInterruptsForegroundL();
1201 @SYMTestCaseID GRAPHICS-WSERV-CRP01-0003
1203 ((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0003"));
1204 iTest->LogSubTest(KTest3);
1205 TestOverlappingWindowsL();
1209 @SYMTestCaseID GRAPHICS-WSERV-CRP01-0004
1211 ((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0004"));
1212 iTest->LogSubTest(KTest4);
1213 DemoClippingWindowsL();
1217 @SYMTestCaseID GRAPHICS-WSERV-CRP01-0005
1219 ((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0005"));
1220 iTest->LogSubTest(KTest5);
1221 TestClippingWindowsL();
1225 @SYMTestCaseID GRAPHICS-WSERV-CRP01-0006
1227 ((CTCrpAnimStep*)iStep)->SetTestStepID(_L("GRAPHICS-WSERV-CRP01-0006"));
1228 iTest->LogSubTest(KTest6);
1229 //this testcase is removed, because invalidation is removed from CWsGraphicDrawer destructor (due to flickering)
1232 ((CTCrpAnimStep*)iStep)->SetTestStepID(KNotATestSYMTestCaseIDName);
1233 ((CTCrpAnimStep*)iStep)->CloseTMSGraphicsStep();
1236 ((CTCrpAnimStep*)iStep)->RecordTestResultL();
1240 namespace //anonymous namespace
1243 CAnimRedrawWindow::CAnimRedrawWindow(CCrpAnim *aAnimWindow, TBool aIsBase) : CTWin(),
1244 iAnimWindow(aAnimWindow),
1249 CAnimRedrawWindow::~CAnimRedrawWindow()
1253 void CAnimRedrawWindow::Draw()
1255 CCrpAnim::Draw(Gc(),Size(),iIsBase,iAnimWindow->iRect,iAnimWindow->iBlankIt,iAnimWindow->iRepeatDrawMax,iAnimWindow->iAlphaValue);
1256 if (iAnimWindow->iAnimUid!=TUid::Null())
1259 Gc()->DrawWsGraphic(iAnimWindow->iAnimUid,iAnimWindow->iAnimPos,iAnimWindow->iAnimData.Pckg());
1266 CCrpAnim::CCrpAnim(TBool aIsBase, TWinType aWinType)
1267 : iWinType(aWinType),
1271 iAnimUid(TUid::Null()),
1272 iAlphaValue(ENoTransparency)
1276 CCrpAnim::~CCrpAnim()
1281 void CCrpAnim::ConstructL(const TPoint &aPos, const TSize &aSize, TInt aAlphaValue)
1283 TDisplayMode reqMode = EColor16MA; //for transparency we need 16ma or 16map mode
1284 TDisplayMode *pReqMode=&reqMode;
1288 iCtWin = new(ELeave) CAnimRedrawWindow(this, iIsBase);
1291 iCtWin = new(ELeave) CTBlankWindow();
1294 iCtWin = new(ELeave) CTBackedUpWin(EColor64K);
1298 iCtWin->SetUpL(aPos, aSize, TheClient->iGroup, *TheClient->iGc, pReqMode, ETrue);
1299 if (aAlphaValue != ENoTransparency)
1301 User::LeaveIfError(Window()->SetTransparencyAlphaChannel());
1302 //the window itself should be completely transparent, the draw commands will use the alpha value
1303 Window()->SetBackgroundColor(TRgb(0, 0, 0, 0));
1304 iAlphaValue = aAlphaValue;
1308 void CCrpAnim::SetEllipseDrawMode(CGraphicsContext::TDrawMode aEllipseDrawMode)
1310 iEllipseDrawMode = aEllipseDrawMode;
1313 void CCrpAnim::DrawEllipse(CBitmapContext *aGc, const TRect &aRect, TInt aAlphaValue)
1315 if(aAlphaValue != ENoTransparency)
1317 aGc->SetBrushColor(TRgb(85,85,85, aAlphaValue));
1318 aGc->SetPenColor(TRgb(170,170,170, aAlphaValue));
1322 aGc->SetBrushColor(TRgb(85,85,85));
1323 aGc->SetPenColor(TRgb(170,170,170));
1325 aGc->SetDrawMode(iEllipseDrawMode);
1326 aGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
1327 aGc->DrawEllipse(aRect);
1330 void CCrpAnim::Draw(CBitmapContext *aGc, const TSize &aSize, TBool aIsBase, const TRect &aRect, TBool aBlankIt,TInt aRepeat, TInt aAlphaValue)
1332 static TInt sGrey=0;
1337 if(aAlphaValue != ENoTransparency)
1339 aGc->SetBrushColor(TRgb(sGrey, sGrey, sGrey, aAlphaValue));
1340 aGc->SetPenColor(TRgb(KRgbGreen.Value(), aAlphaValue));
1344 aGc->SetBrushColor(TRgb::Gray256(sGrey));
1345 aGc->SetPenColor(KRgbGreen);
1348 TInt xPos=aSize.iHeight,yPos=aSize.iWidth;
1350 // The test windows are created relative to screen size. The
1351 // number of green lines generated needs to be tied into the
1352 // window size to prevent green becoming the dominant colour
1353 // when blended with the second animation, which would
1354 // prevent the PredominantColour() algorithm from discovering
1356 TInt yStep = aSize.iHeight/14;
1357 TInt xStep = aSize.iWidth/6;
1359 //This paint is intentionally complex and slow so that the animation timer is likely to interrupt it.
1361 for (TInt nn = 0 ; nn < aRepeat ; nn++)
1362 for(yPos=0 ; yPos < aSize.iHeight ; yPos += yStep)
1363 for(xPos=0 ; xPos < aSize.iWidth ; xPos += xStep)
1364 aGc->DrawLine(aRect.Center(),TPoint(xPos,yPos));
1366 DrawEllipse(aGc, aRect, aAlphaValue);
1369 //This simple API may need replacing by a list and search if multiple anims are played together
1370 TWsGraphicAnimation* CCrpAnim::SetAnimation(TUid aUid)
1371 { //currently only have 1 animation - it gets replaced. It could get refiused
1376 TWsGraphicAnimation* CCrpAnim::GetAnimation(TUid aUid)
1377 { //currently only have 1 animation
1384 void CCrpAnim::SetPosAnimation(const TUid& aUid, const TRect& aRect)
1385 { //currently only have 1 animation
1390 TRect* CCrpAnim::GetPosAnimation(const TUid& aUid)
1391 { //currently only have 1 animation
1398 TBool CCrpAnim::RemoveAnimation(TUid)
1400 iAnimUid=TUid::Null();
1401 iAnimData.Stop(EFalse);
1405 void CCrpAnim::DoDraw()
1410 inline void CCrpAnim::DoDraw(TBool aBlankIt)
1412 __ASSERT_ALWAYS(iWinType!=EBlank,AutoPanic(EAutoPanicWindowType));
1413 iCtWin->Gc()->Activate(*Window());
1414 Draw(iCtWin->Gc(),Size(),iIsBase,iRect,aBlankIt,iRepeatDrawMax,iAlphaValue);
1415 if (iAnimUid!=TUid::Null())
1416 iCtWin->Gc()->DrawWsGraphic(iAnimUid,iAnimPos,iAnimData.Pckg());
1417 iCtWin->Gc()->Deactivate();
1420 void CCrpAnim::DoDrawEllipse()
1422 __ASSERT_ALWAYS(iWinType!=EBlank,AutoPanic(EAutoPanicWindowType));
1423 iCtWin->Gc()->Activate(*Window());
1424 DrawEllipse(iCtWin->Gc(),iRect,iAlphaValue);
1425 iCtWin->Gc()->Deactivate();
1428 void CCrpAnim::InvalidateAndRedraw(TBool /*aUseBlankItMember*/,TBool /*aBlankIt*/,TBool aUseRWindowInvalidate,TRect* aRect)
1430 RWindow& win = *Window();
1433 if (aUseRWindowInvalidate)
1434 win.Invalidate(*aRect);
1440 if (aUseRWindowInvalidate)
1446 win.BeginRedraw(*aRect);
1454 void CCrpAnim::Invalidate(const TRect &aRect)
1457 rect.Move(iCtWin->Position());
1458 CTUser::Splat(TheClient,rect,TRgb::Gray256(0));
1462 } //end anonymous namespace
1464 __WS_CONSTRUCT_STEP__(CrpAnim)