windowing/windowserver/nonnga/SERVER/playbackgc.cpp
author Faisal Memon <faisal.memon@nokia.com>
Thu, 06 May 2010 11:31:11 +0100
branchNewGraphicsArchitecture
changeset 47 48b924ae7197
parent 0 5d03bc08d59c
permissions -rw-r--r--
Applied patch 1, to provide a syborg specific minigui oby file. Need to compare this with the "stripped" version currently in the tree. This supplied version applies for Nokia builds, but need to repeat the test for SF builds to see if pruning is needed, or if the file needs to be device-specific.

// Copyright (c) 1994-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:
// GC.CPP
// GC and Graphics functions
// 
//

#include "playbackgc.h"

#include <e32std.h>
#include <s32mem.h> 

#include "backedupwindow.h"
#include "panics.h"
#include "ScrDev.H"
#include "windowgroup.h"
#include "wsfont.h"
#include "wstop.h"
#include "Graphics/WSGRAPHICDRAWER.H"

#if defined(__WINS__) && defined(_DEBUG)
#   include "offscreenbitmap.h"
#	define DEBUGOSB { CWsOffScreenBitmap * ofb = iWin->Screen()->OffScreenBitmap(); if (ofb) ofb->Update(); }
#else
#	define DEBUGOSB
#endif

CPlaybackGc * CPlaybackGc::iSelf=NULL;

GLREF_C RWsRegion* InternalizeRegionL(RReadStream& aReadStream);

/*CPlaybackGc*/

void CPlaybackGc::InitStaticsL()
	{
	iSelf=new(ELeave) CPlaybackGc();
	iSelf->ConstructL();
	}

void CPlaybackGc::DeleteStatics()
	{
	delete iSelf;
	iSelf = 0;
	}

CPlaybackGc::CPlaybackGc()
	{
	}

void CPlaybackGc::ConstructL()
	{
	iSelf->iScratchBitmap=new(ELeave) CFbsBitmap();
	iSelf->iScratchMaskBitmap=new(ELeave) CFbsBitmap();
	iGcBuf = CBufSeg::NewL(512);
	}

CPlaybackGc::~CPlaybackGc()
	{
	delete iPolyPoints;
	delete iGcBuf;
	iCurrentClippingRegion = NULL;
	delete iScratchBitmap;
	delete iScratchMaskBitmap;
	}

void CPlaybackGc::Activate(CWsClientWindow * aWin, CFbsBitGc * aGc, const TRegion * aRegion)
	{
	iWin = aWin;
	iGc = aGc;
	iTargetRegion = aRegion;
	
	iDrawRegion = iTargetRegion;
	iMasterOrigin = iWin->Origin();
	iOrigin.SetXY(0,0);
	iGc->SetBrushColor(iWin->BackColor());
	ResetClippingRect();
	}
	
void CPlaybackGc::Deactivate()
	{
	iWin = 0;
	iGc = 0;
	iTargetRegion = 0;
	iDrawRegion = 0;
	CancelUserClippingRegion();
	}
	
void CPlaybackGc::CancelUserClippingRegion()
	{
	if (iUserDefinedClippingRegion)
		{
		iUserDefinedClippingRegion->Destroy();
		iUserDefinedClippingRegion = 0;
		iDrawRegion = iTargetRegion;
		}
	}
	
void CPlaybackGc::SetClippingRect(const TRect &aRect)
	{
	iClippingRect=aRect;
	iClippingRect.Move(iOrigin);
	iClippingRectSet=ETrue;
	}

void CPlaybackGc::ResetClippingRect()
	{
	iClippingRectSet=EFalse;
	}

void CPlaybackGc::CheckPolyData(const TAny* aDataPtr, TInt aHeaderSize, TInt aNumPoints)
	{
	TInt maxDataLen;
	if (CWsClient::iCurrentCommand.iOpcode>0)
		{
		maxDataLen=CWsClient::EndOfCommandBuffer()-static_cast<const TUint8*>(aDataPtr);
		}
	else
		{
		maxDataLen=CWsClient::iCurrentCommand.iCmdLength;
		}
	const TInt dataSize=aHeaderSize+aNumPoints*sizeof(TPoint);
	if (dataSize>maxDataLen)
		GcOwnerPanic(EWservPanicBadPolyData);
	}

void CPlaybackGc::DoDrawPolygon(const TWsGcCmdDrawPolygon *aDrawPolygon)
	{
	CheckPolyData(aDrawPolygon, sizeof(TWsGcCmdDrawPolygon), aDrawPolygon->numPoints);
	iGc->DrawPolygon((TPoint *)(aDrawPolygon+1),aDrawPolygon->numPoints,aDrawPolygon->fillRule);
	}

void CPlaybackGc::StartSegmentedDrawPolygonL(const TWsGcCmdStartSegmentedDrawPolygon *aDrawPolygon)
	{
	// In case a Playback have been done before all the segment is in the RedrawStore
	// (This allocation is deleted only thanks to the EWsGcOpDrawSegmentedPolygon opcode
	//  which arrive after all the segments)
	delete iPolyPoints;
	iPolyPoints=NULL;	

	iPolyPoints=(TPoint *)User::AllocL(aDrawPolygon->totalNumPoints*sizeof(TPoint));
	iPolyPointListSize=aDrawPolygon->totalNumPoints;
	}

void CPlaybackGc::SegmentedDrawPolygonData(const TWsGcCmdSegmentedDrawPolygonData *aDrawPolygon)
	{
	if (aDrawPolygon->index<0 || (aDrawPolygon->index + aDrawPolygon->numPoints) > iPolyPointListSize)
		GcOwnerPanic(EWservPanicBadPolyData);
	Mem::Copy(iPolyPoints+aDrawPolygon->index,aDrawPolygon+1,aDrawPolygon->numPoints*sizeof(TPoint));
	}

void CPlaybackGc::EndSegmentedPolygon()
	{
	delete iPolyPoints;
	iPolyPoints=NULL;
	}

void CPlaybackGc::DoDrawPolyLine(const TWsGcCmdDrawPolyLine *aDrawPolyLine, TBool aContinued)
	{
	TInt numPoints=aDrawPolyLine->numPoints;
	CheckPolyData(aDrawPolyLine, sizeof(TWsGcCmdDrawPolyLine), numPoints);
	const TPoint *points=(TPoint *)(aDrawPolyLine+1);
	if (aContinued)
		{
		numPoints++;
		points=&aDrawPolyLine->last;
		}
	if (aDrawPolyLine->more)	// more to come so don't draw the end point
		iGc->DrawPolyLineNoEndPoint(points,numPoints);
	else
		iGc->DrawPolyLine(points,numPoints);
	}

void CPlaybackGc::GcOwnerPanic(TClientPanic aPanic)
	{
	iGc->SetClippingRegion(NULL);
	iCurrentClippingRegion = NULL;
	EndSegmentedPolygon();
	iWin->WsOwner()->PPanic(aPanic);
	}

// implementing MWsGc

MWsClient& CPlaybackGc::Client()
	{
	return *(iWin->WsOwner());
	}

MWsScreen& CPlaybackGc::Screen()
	{
	return *(iWin->Screen());
	}

TPoint CPlaybackGc::GcOrigin() const
	{
	return (iMasterOrigin + iOrigin);
	}

const TRegion& CPlaybackGc::ClippingRegion()
	{
	WS_ASSERT_ALWAYS(iCurrentClippingRegion,EWsPanicDrawCommandsInvalidState);
	return* iCurrentClippingRegion;
	}

CFbsBitGc& CPlaybackGc::BitGc()
	{
	return *iGc;
	}
	
TInt CPlaybackGc::PushBitGcSettings()
	{
	// the buf format is len+data where data is written by the GC's ExternalizeL()
	CBufBase& buf = *iGcBuf;
	const TInt start = buf.Size();
	RBufWriteStream out(buf,start);
	TRAPD(err,out.WriteInt32L(0));
	if(!err)
		{
		TRAP(err,iGc->ExternalizeL(out));
		}
	if(err) //rollback addition
		{
		buf.Delete(start,buf.Size()-start);
		}
	else //fixup len
		{
		TRAP_IGNORE(out.CommitL();) // can't see this failing
		TPckgBuf<TInt32> pckg(buf.Size()-sizeof(TInt32)-start);
		buf.Write(start,pckg);
		}
	return err;
	}

void CPlaybackGc::PopBitGcSettings()
	{
	CBufBase& buf = *iGcBuf;
	TInt ofs = 0;
	FOREVER
		{
		TInt chunk = 0;
		RBufReadStream in(buf,ofs);
		TRAPD(err,chunk = in.ReadInt32L());
		if(err)
			{
			WS_ASSERT_DEBUG(err != 0, EWsPanicWsGraphic);
			return;
			}
		if(ofs+sizeof(TInt32)+chunk >= buf.Size()) // the last chunk?
			{
			TRAP_IGNORE(iGc->InternalizeL(in));
			buf.Delete(ofs,buf.Size()-ofs);
			return;
			}
		ofs += chunk + sizeof(TInt32);
		}
	}

const TTime& CPlaybackGc::Now() const
	{
	return iWin->Screen()->Now();
	}
	
void CPlaybackGc::ScheduleAnimation(const TRect& aRect,const TTimeIntervalMicroSeconds& aFromNow)
	{
	ScheduleAnimation(aRect,aFromNow,0,0);
	}

void CPlaybackGc::ScheduleAnimation(const TRect& aRect,const TTimeIntervalMicroSeconds& aFromNow,const TTimeIntervalMicroSeconds& aFreq,const TTimeIntervalMicroSeconds& aStop)
	{
	// convert window rect to screen rect
	TRect rect(aRect);
	rect.Move(GcOrigin());
	// clip rect to window extent
	rect.Intersection(iWin->Abs());
	if (!rect.IsEmpty())
		{
		// and schedule it
		iWin->Screen()->ScheduleAnimation(rect,aFromNow,aFreq,aStop);
		}
	}

void CPlaybackGc::SetGcOrigin(const TPoint& aOrigin) 
	{ 
	iOrigin = aOrigin - iMasterOrigin; 
	} 


void CPlaybackGc::RemoteReadDataAndDrawL(const CWsGraphicDrawer* aGraphic, CWsClient* aOwner, const TWsGcCmdUnion &aData)
	{
	TPtrC8 data;
	HBufC8* dataBuf = NULL;
	const TInt len = aData.WsGraphic->iDataLen;
	
	if ((len >= KMaxTInt / 4) || (len < 0))
		{
		aOwner->PPanic(EWservPanicBuffer);	
		}	
	dataBuf = HBufC8::NewLC(len);
	TPtr8 des = dataBuf->Des();
	aOwner->RemoteRead(des, 0);

	if(des.Size() != len)
		{
		aOwner->PPanic(EWservPanicBuffer);
		}
	data.Set(des);																
	aGraphic->Draw(*this, aData.WsGraphic->iRect, data);
	CleanupStack::PopAndDestroy(dataBuf);		
	}

TPtrC CPlaybackGc::BufferTPtr(TText* aStart,TInt aLen, const TDesC8& aCmdData)
	{
	if ((reinterpret_cast<TUint8*>(aStart) < aCmdData.Ptr()
									|| reinterpret_cast<TUint8*>(aStart+aLen) > (aCmdData.Ptr() + aCmdData.Size()) ))
		{
		GcOwnerPanic(EWservPanicBufferPtr);
		}
	TPtrC gcPtr;
	gcPtr.Set(aStart,aLen);
	return(gcPtr);
	} 

void CPlaybackGc::DoDrawCommand(TWsGcOpcodes aOpcode, const TDesC8& aCmdData, const TRegion *aRegion)
	{
	TWsGcCmdUnion pData;
	pData.any=aCmdData.Ptr();
	
	if (aRegion)
		{
		WS_ASSERT_DEBUG(iWin,EWsPanicWindowNull);
		if (aRegion->Count()==0)
			return;
		iGc->SetClippingRegion(aRegion);
		WS_ASSERT_DEBUG(!iCurrentClippingRegion, EWsPanicDrawCommandsInvalidState);
		iCurrentClippingRegion = aRegion;
		}
	switch(aOpcode)
		{
		case EWsGcOpDrawWsGraphic:
		case EWsGcOpDrawWsGraphicPtr:
			{
			TRect screenRect(pData.WsGraphic->iRect);
			screenRect.Move(GcOrigin());
			if(iCurrentClippingRegion->Intersects(screenRect))
				{
				const TInt dataLen = pData.WsGraphic->iDataLen;
				TGraphicDrawerId id;
				id.iId = pData.WsGraphic->iId;
				id.iIsUid = (pData.WsGraphic->iFlags & EWsGraphicIdUid);
				CWsClient* owner = iWin->WsOwner();
				const CWsGraphicDrawer* graphic = owner->WindowServer().ResolveGraphic(id);
				if(graphic && graphic->IsSharedWith(owner->SecureId()))
					{
					if(aOpcode == EWsGcOpDrawWsGraphicPtr)
						{
						TRAPD(err, RemoteReadDataAndDrawL(graphic, owner, pData))
						if(err)
							WS_PANIC_DEBUG(EWsPanicWsGraphic);
						}
					else
						graphic->Draw(*this,pData.WsGraphic->iRect,CWsClient::BufferTPtr8((TUint8*)(pData.WsGraphic+1),dataLen));
	
					WS_ASSERT_DEBUG(!iGcBuf->Size(),EWsPanicWsGraphic);
					iGcBuf->Reset();
					}
				}
			break;
			}
		case EWsGcOpMapColorsLocal:
			iGc->MapColors(pData.MapColorsLocal->rect, pData.MapColorsLocal->colors,pData.MapColorsLocal->numPairs,pData.MapColorsLocal->mapForwards);
			break;
		case EWsGcOpDrawPolyLineLocalBufLen:
			iGc->DrawPolyLine(pData.DrawPolyLineLocalBufLen->points,pData.DrawPolyLineLocalBufLen->length);
			break;
		case EWsGcOpDrawPolyLineLocal:
			iGc->DrawPolyLine(pData.PointList);
			break;
		case EWsGcOpDrawPolygonLocalBufLen:
			iGc->DrawPolygon(pData.DrawPolygonLocalBufLen->points,pData.DrawPolygonLocalBufLen->length,pData.DrawPolygonLocalBufLen->fillRule);
			break;
		case EWsGcOpDrawPolygonLocal:
			iGc->DrawPolygon(pData.DrawPolygonLocal->pointList,pData.DrawPolygonLocal->fillRule);
			break;
		case EWsGcOpDrawBitmapLocal:
			iGc->DrawBitmap(pData.BitmapLocal->pos, pData.BitmapLocal->bitmap);
			break;
		case EWsGcOpDrawBitmap2Local:
			iGc->DrawBitmap(pData.Bitmap2Local->rect, pData.Bitmap2Local->bitmap);
			break;
		case EWsGcOpDrawBitmap3Local:
			iGc->DrawBitmap(pData.Bitmap3Local->rect, pData.Bitmap3Local->bitmap, pData.Bitmap3Local->srcRect);
			break;
		case EWsGcOpDrawBitmapMaskedLocal:
			iGc->DrawBitmapMasked(pData.iBitmapMaskedLocal->iRect, pData.iBitmapMaskedLocal->iBitmap, pData.iBitmapMaskedLocal->iSrcRect, pData.iBitmapMaskedLocal->iMaskBitmap,pData.iBitmapMaskedLocal->iInvertMask);
			break;
		case EWsGcOpAlphaBlendBitmapsLocal:
			iGc->AlphaBlendBitmaps(pData.AlphaBlendBitmapsLocal->point,pData.AlphaBlendBitmapsLocal->iBitmap,
						   			pData.AlphaBlendBitmapsLocal->source, pData.AlphaBlendBitmapsLocal->iAlpha,
									pData.AlphaBlendBitmapsLocal->alphaPoint);

			break;
		case EWsGcOpDrawText:
			iGc->DrawText(BufferTPtr((TText *)(pData.DrawText+1),pData.DrawText->length,aCmdData),pData.DrawText->pos);
			break;
		case EWsGcOpDrawBoxTextOptimised1:
			iGc->DrawText(BufferTPtr((TText *)(pData.BoxTextO1+1),pData.BoxTextO1->length,aCmdData),pData.BoxTextO1->box,
							pData.BoxTextO1->baselineOffset,CGraphicsContext::ELeft,0);
			break;
		case EWsGcOpDrawBoxTextOptimised2:
			iGc->DrawText(BufferTPtr((TText *)(pData.BoxTextO2+1),pData.BoxTextO2->length,aCmdData),pData.BoxTextO2->box,
							pData.BoxTextO2->baselineOffset,pData.BoxTextO2->horiz,pData.BoxTextO2->leftMrg);
			break;
		case EWsGcOpDrawTextPtr:
			iGc->DrawText(*pData.DrawTextPtr->text,pData.DrawTextPtr->pos);
			break;
		case EWsGcOpDrawTextPtr1:
		   	iGc->DrawText(*pData.DrawTextPtr->text);
			break;
		case EWsGcOpDrawBoxText:
			iGc->DrawText(BufferTPtr((TText *)(pData.BoxText+1),pData.BoxText->length,aCmdData),pData.BoxText->box,pData.BoxText->baselineOffset,pData.BoxText->width,pData.BoxText->horiz,pData.BoxText->leftMrg);
			break;
		case EWsGcOpDrawBoxTextPtr:
			iGc->DrawText(*pData.DrawBoxTextPtr->text,pData.DrawBoxTextPtr->box,pData.DrawBoxTextPtr->baselineOffset,pData.DrawBoxTextPtr->width,pData.DrawBoxTextPtr->horiz,pData.DrawBoxTextPtr->leftMrg);
			break;
		case EWsGcOpDrawBoxTextPtr1:
			iGc->DrawText(*pData.DrawBoxTextPtr->text,pData.DrawBoxTextPtr->box);
			break;
		case EWsGcOpDrawTextVertical:
			iGc->DrawTextVertical(BufferTPtr((TText *)(pData.DrawTextVertical+1),pData.DrawTextVertical->length,aCmdData),pData.DrawTextVertical->pos
							,pData.DrawTextVertical->up);
			break;
		case EWsGcOpDrawTextVerticalPtr:
			iGc->DrawTextVertical(*pData.DrawTextVerticalPtr->text,pData.DrawTextVerticalPtr->pos,pData.DrawTextVerticalPtr->up);
			break;
		case EWsGcOpDrawTextVerticalPtr1:
			iGc->DrawTextVertical(*pData.DrawTextVerticalPtr->text,pData.DrawTextVerticalPtr->up);
			break;
		case EWsGcOpDrawBoxTextVertical:
			iGc->DrawTextVertical(BufferTPtr((TText *)(pData.DrawBoxTextVertical+1),pData.DrawBoxTextVertical->length,aCmdData),
							pData.DrawBoxTextVertical->box,	pData.DrawBoxTextVertical->baselineOffset,
							pData.DrawBoxTextVertical->up,(CGraphicsContext::TTextAlign)pData.DrawBoxTextVertical->vert,pData.DrawBoxTextVertical->margin);
			break;
		case EWsGcOpDrawBoxTextVerticalPtr:
			iGc->DrawTextVertical(*pData.DrawBoxTextVerticalPtr->text,pData.DrawBoxTextVerticalPtr->box,pData.DrawBoxTextVerticalPtr->baselineOffset
							,pData.DrawBoxTextVerticalPtr->width,pData.DrawBoxTextVerticalPtr->up,pData.DrawBoxTextVerticalPtr->vert,pData.DrawBoxTextVerticalPtr->margin);
			break;
		case EWsGcOpDrawBoxTextVerticalPtr1:
			iGc->DrawTextVertical(*pData.DrawBoxTextVerticalPtr->text,pData.DrawBoxTextVerticalPtr->box,pData.DrawBoxTextVerticalPtr->up);
			break;
		case EWsGcOpDrawTextLocal:
			iGc->DrawText(*pData.DrawTextLocal->desc,pData.DrawTextLocal->pos);
			break;
		case EWsGcOpDrawBoxTextLocal:
			iGc->DrawText(*pData.BoxTextLocal->desc,pData.BoxTextLocal->box,pData.BoxTextLocal->baselineOffset,
							pData.BoxTextLocal->horiz,pData.BoxTextLocal->leftMrg);
			break;
		case EWsGcOpDrawLine:
			iGc->DrawLine(pData.DrawLine->pnt1,pData.DrawLine->pnt2);
			break;
		case EWsGcOpDrawTo:
			iGc->DrawLine(iLinePos,*pData.Point);
			break;
		case EWsGcOpDrawBy:
			iGc->DrawLine(iLinePos,iLinePos+(*pData.Point));
			break;
		case EWsGcOpPlot:
			iGc->Plot(*pData.Point);
			break;
		case EWsGcOpMoveTo:
		case EWsGcOpMoveBy:
			break;
		case EWsGcOpGdiBlt2Local:
			iGc->BitBlt(pData.GdiBlt2Local->pos,pData.GdiBlt2Local->bitmap);
			break;
		case EWsGcOpGdiBlt3Local:
			iGc->BitBlt(pData.GdiBlt3Local->pos,pData.GdiBlt3Local->bitmap, pData.GdiBlt3Local->rect);
			break;
		case EWsGcOpGdiBltMaskedLocal:
			iGc->BitBltMasked(pData.GdiBltMaskedLocal->pos,pData.GdiBltMaskedLocal->bitmap,
								pData.GdiBltMaskedLocal->rect,pData.GdiBltMaskedLocal->maskBitmap,
								pData.GdiBltMaskedLocal->invertMask);
			break;
		case EWsGcOpGdiWsBlt2:
		case EWsGcOpGdiWsBlt3:
		case EWsGcOpGdiWsBltMasked:
		case EWsGcOpGdiWsAlphaBlendBitmaps:
		case EWsGcOpWsDrawBitmapMasked:
			{
			// Andy - we continually duplicate bitmaps in here, and yet we already have them
			// somewhere as pointers so can't we both simplify and optimize this?
			CFbsBitmap* scratchBitmap=iScratchBitmap;
			CFbsBitmap* scratchMaskBimap=iScratchMaskBitmap;
			TInt maskHandle=0;
			TInt handle=WsBitmapHandle(aOpcode,pData, maskHandle);
			CWsClient* owner=iWin->WsOwner();
			if (owner!=NULL)
				{
				TInt wsBmpErr = KErrNone;
				DWsBitmap *bitmap=(DWsBitmap *)owner->HandleToObj(handle, WS_HANDLE_BITMAP);
				if (!bitmap)
					wsBmpErr = KErrNotFound;
				else
					scratchBitmap=bitmap->FbsBitmap();
				if (wsBmpErr == KErrNone)
					if (aOpcode==EWsGcOpGdiWsBltMasked || aOpcode==EWsGcOpGdiWsAlphaBlendBitmaps || aOpcode==EWsGcOpWsDrawBitmapMasked)
						{
						DWsBitmap *bitmap2=(DWsBitmap *)owner->HandleToObj(maskHandle, WS_HANDLE_BITMAP);
						if (!bitmap2)
							wsBmpErr = KErrNotFound;
						else
							scratchMaskBimap=bitmap2->FbsBitmap();
						}
				if (wsBmpErr == KErrNone)
					{
					switch(aOpcode)
						{
						case EWsGcOpGdiWsBlt2:
							iGc->BitBlt(pData.GdiBlt2->pos,scratchBitmap);
							break;
						case EWsGcOpGdiWsBlt3:
							iGc->BitBlt(pData.GdiBlt3->pos,scratchBitmap, pData.GdiBlt3->rect);
							break;
						case EWsGcOpGdiWsBltMasked:
							{
							iGc->BitBltMasked(pData.GdiBltMasked->destination,scratchBitmap,
											pData.GdiBltMasked->source, scratchMaskBimap,
											pData.GdiBltMasked->invertMask);
							}
							break;
						case EWsGcOpGdiWsAlphaBlendBitmaps:
							{
							iGc->AlphaBlendBitmaps(pData.AlphaBlendBitmaps->point,scratchBitmap,
											   pData.AlphaBlendBitmaps->source, scratchMaskBimap,
											   pData.AlphaBlendBitmaps->alphaPoint);
							}
							break;
						case EWsGcOpWsDrawBitmapMasked:
							{
							iGc->DrawBitmapMasked(pData.iBitmapMasked->iRect,scratchBitmap, 
												pData.iBitmapMasked->iSrcRect,scratchMaskBimap,
												pData.iBitmapMasked->iInvertMask);
							}
							break;
						}
					}
				}
			break;
			}
		case EWsGcOpGdiBlt2:
		case EWsGcOpGdiBlt3:
		case EWsGcOpGdiBltMasked:
		case EWsGcOpGdiAlphaBlendBitmaps:
		case EWsGcOpDrawBitmap:
		case EWsGcOpDrawBitmap2:
		case EWsGcOpDrawBitmap3:
		case EWsGcOpDrawBitmapMasked:
			{
			TInt maskHandle=0;
			TInt ret = iScratchBitmap->Duplicate(FbsBitmapHandle(aOpcode, pData,maskHandle));
			if (ret == KErrNone)
				{
				switch(aOpcode)
					{
					case EWsGcOpGdiBlt2:
						iGc->BitBlt(pData.GdiBlt2->pos,iScratchBitmap);
						break;
					case EWsGcOpGdiBlt3:
						iGc->BitBlt(pData.GdiBlt3->pos,iScratchBitmap, pData.GdiBlt3->rect);
						break;
					case EWsGcOpGdiBltMasked:
						{
						ret = iScratchMaskBitmap->Duplicate(pData.GdiBltMasked->maskHandle);
						if (ret == KErrNone)
							{
							iGc->BitBltMasked(pData.GdiBltMasked->destination,iScratchBitmap,
												pData.GdiBltMasked->source, iScratchMaskBitmap,
												pData.GdiBltMasked->invertMask);
							iScratchMaskBitmap->Reset();
							}
						}
						break;
					case EWsGcOpGdiAlphaBlendBitmaps:
						{
						ret = iScratchMaskBitmap->Duplicate(pData.AlphaBlendBitmaps->alphaHandle);
						if (ret == KErrNone)
							{
							iGc->AlphaBlendBitmaps(pData.AlphaBlendBitmaps->point, iScratchBitmap,
												pData.AlphaBlendBitmaps->source, iScratchMaskBitmap,
												pData.AlphaBlendBitmaps->alphaPoint);
							iScratchMaskBitmap->Reset();
							}
						break;
						}
					case EWsGcOpDrawBitmap:
						iGc->DrawBitmap(pData.Bitmap->pos, iScratchBitmap);
						break;
					case EWsGcOpDrawBitmap2:
						iGc->DrawBitmap(pData.Bitmap2->rect, iScratchBitmap);
						break;
					case EWsGcOpDrawBitmap3:
						iGc->DrawBitmap(pData.Bitmap3->rect, iScratchBitmap, pData.Bitmap3->srcRect);
						break;
					case EWsGcOpDrawBitmapMasked:
						{
						ret = iScratchMaskBitmap->Duplicate(pData.iBitmapMasked->iMaskHandle);
						if (ret == KErrNone)
							{
							iGc->DrawBitmapMasked(pData.iBitmapMasked->iRect, iScratchBitmap, 
												pData.iBitmapMasked->iSrcRect, iScratchMaskBitmap,
												pData.iBitmapMasked->iInvertMask);
							iScratchMaskBitmap->Reset();
							}
						}
						break;
					}
				iScratchBitmap->Reset();
				}
			break;
			}
		case EWsGcOpDrawSegmentedPolygon:
			iGc->DrawPolygon(iPolyPoints,iPolyPointListSize,pData.DrawSegmentedPolygon->fillRule);
			break;
		case EWsGcOpDrawPolygon:
			DoDrawPolygon(pData.Polygon);
			break;
		case EWsGcOpDrawPolyLine:
			DoDrawPolyLine(pData.PolyLine, EFalse);
			break;
		case EWsGcOpDrawPolyLineContinued:
			DoDrawPolyLine(pData.PolyLine, ETrue);
			break;
		case EWsGcOpClear:
			iGc->Clear(TRect(iWin->Size()));
			break;
		case EWsGcOpClearRect:
			iGc->Clear(*pData.Rect);
			break;
		case EWsGcOpDrawRect:
			iGc->DrawRect(*pData.Rect);
			break;
		case EWsGcOpDrawEllipse:
			iGc->DrawEllipse(*pData.Rect);
			break;
		case EWsGcOpDrawRoundRect:
			iGc->DrawRoundRect(*pData.Rect,pData.RoundRect->ellipse);
			break;
		case EWsGcOpDrawArc:
			iGc->DrawArc(*pData.Rect,pData.ArcOrPie->start,pData.ArcOrPie->end);
			break;
		case EWsGcOpDrawPie:
			iGc->DrawPie(*pData.Rect,pData.ArcOrPie->start,pData.ArcOrPie->end);
			break;
		case EWsGcOpCopyRect:
			iGc->CopyRect(pData.CopyRect->pos,*pData.Rect);
			break;
		case EWsGcOpMapColors:
			iGc->MapColors(pData.MapColors->rect,(TRgb *)(pData.MapColors+1),pData.MapColors->numPairs,pData.MapColors->mapForwards);
			break;
		default:
			TRAP_IGNORE(iWin->OwnerPanic(EWservPanicOpcode));
			break;
		}
	// DEBUGOSB // comment in for per-draw-command debug osb updates
	iGc->SetClippingRegion(NULL);
	iCurrentClippingRegion = NULL;
	}

TInt CPlaybackGc::WsBitmapHandle(TInt aOpcode, const TWsGcCmdUnion &pData, TInt& aMaskHandle) 
	{
	TInt handle=0;
	switch(aOpcode)
		{
		case EWsGcOpGdiWsBlt2:
			handle=pData.GdiBlt2->handle;
			break;
		case EWsGcOpGdiWsBlt3:
			handle=pData.GdiBlt3->handle;
			break;
		case EWsGcOpGdiWsBltMasked:
			handle=pData.GdiBltMasked->handle;
			aMaskHandle = pData.GdiBltMasked->maskHandle;
			break;
		case EWsGcOpGdiWsAlphaBlendBitmaps:
			handle=pData.AlphaBlendBitmaps->bitmapHandle;
			aMaskHandle = pData.AlphaBlendBitmaps->alphaHandle;
			break;
		case EWsGcOpWsDrawBitmapMasked:
			handle=pData.iBitmapMasked->iHandle;
			aMaskHandle=pData.iBitmapMasked->iMaskHandle;
			break;
		}
	return handle;
	}		

TInt CPlaybackGc::FbsBitmapHandle(TInt aOpcode, const TWsGcCmdUnion &pData, TInt& aMaskHandle)
	{
	TInt handle=0;
	aMaskHandle=0;
	switch(aOpcode)
		{
		case EWsGcOpGdiWsBlt2:
		case EWsGcOpGdiWsBlt3:
		case EWsGcOpGdiWsBltMasked:
		case EWsGcOpGdiWsAlphaBlendBitmaps:
			{
			TInt maskHandle = 0;
			DWsBitmap *bitmap=(DWsBitmap *)iWin->WsOwner()->HandleToObj(WsBitmapHandle(aOpcode,pData, maskHandle), WS_HANDLE_BITMAP);
			WS_ASSERT_DEBUG(bitmap, EWsPanicDrawCommandsInvalidState);
			if (bitmap)
				handle=bitmap->FbsBitmap()->Handle();
			if (aOpcode==EWsGcOpGdiWsBltMasked || aOpcode==EWsGcOpGdiWsAlphaBlendBitmaps)
				{
				DWsBitmap *bitmap2=(DWsBitmap *)iWin->WsOwner()->HandleToObj(maskHandle, WS_HANDLE_BITMAP);
				WS_ASSERT_DEBUG(bitmap2, EWsPanicDrawCommandsInvalidState);
				if (bitmap2)
					aMaskHandle=bitmap2->FbsBitmap()->Handle();
				}
			break;
			}
		case EWsGcOpGdiBlt2:
			handle=pData.GdiBlt2->handle;
			break;
		case EWsGcOpGdiBlt3:
			handle=pData.GdiBlt3->handle;
			break;
		case EWsGcOpGdiBltMasked:
			handle=pData.GdiBltMasked->handle;
			aMaskHandle=pData.GdiBltMasked->maskHandle;
			break;
		case EWsGcOpGdiAlphaBlendBitmaps:
			handle=pData.AlphaBlendBitmaps->bitmapHandle;
			aMaskHandle=pData.AlphaBlendBitmaps->alphaHandle;
			break;
		case EWsGcOpDrawBitmap:
			handle=pData.Bitmap->handle;
			break;
		case EWsGcOpDrawBitmap2:
			handle=pData.Bitmap2->handle;
			break;
		case EWsGcOpDrawBitmap3:
			handle=pData.Bitmap3->handle;
			break;
		case EWsGcOpDrawBitmapMasked:
			handle=pData.iBitmapMasked->iHandle;
			aMaskHandle=pData.iBitmapMasked->iMaskHandle;
			break;
		}
	return handle;
	}


void CPlaybackGc::UpdateJustification(TText* aText,TInt aLen, const TDesC8& aCmdData)
	{
	iGc->UpdateJustification(BufferTPtr(aText,aLen,aCmdData));
	}

void CPlaybackGc::DoDrawing(TWsGcOpcodes aOpcode, const TDesC8& aCmdData)
	{
	TWsGcCmdUnion pData;
	pData.any=aCmdData.Ptr();
	
	// Andy. We do this every time?  Shouldn't this be set up for us already by the render stage?
	// infact, aren't we breaking the render stage by doing it here?
	iGc->SetUserDisplayMode(iWin->DisplayMode());
	if (iClippingRectSet)
		{
		iGc->SetOrigin(iMasterOrigin);
		iGc->SetClippingRect(iClippingRect);
		}
	iGc->SetOrigin(iMasterOrigin + iOrigin);
	
	DoDrawCommand(aOpcode,aCmdData,iDrawRegion);

	iGc->CancelClippingRect();
	iGc->SetUserDisplayMode(ENone);
	switch(aOpcode)
		{
		case EWsGcOpDrawLine:
			iLinePos=pData.DrawLine->pnt2;
			break;
		case EWsGcOpDrawTo:
		case EWsGcOpMoveTo:
		case EWsGcOpPlot:
			iLinePos=(*pData.Point);
			break;
		case EWsGcOpDrawBy:
		case EWsGcOpMoveBy:
			iLinePos+=(*pData.Point);
			break;
		case EWsGcOpDrawSegmentedPolygon:
			EndSegmentedPolygon();
			break;
		case EWsGcOpDrawText:
			UpdateJustification((TText *)(pData.DrawText+1),pData.DrawText->length,aCmdData);
			break;
		case EWsGcOpDrawTextVertical:
			UpdateJustification((TText *)(pData.DrawTextVertical+1),pData.DrawTextVertical->length,aCmdData);
			break;
		case EWsGcOpDrawBoxText:
			UpdateJustification((TText *)(pData.BoxText+1),pData.BoxText->length,aCmdData);
			break;
		case EWsGcOpDrawBoxTextOptimised1:
			UpdateJustification((TText *)(pData.BoxTextO1+1),pData.BoxTextO1->length,aCmdData);
			break;
		case EWsGcOpDrawBoxTextOptimised2:
			UpdateJustification((TText *)(pData.BoxTextO2+1),pData.BoxTextO2->length,aCmdData);
			break;
		case EWsGcOpDrawBoxTextVertical:
			UpdateJustification((TText *)(pData.DrawBoxTextVertical+1),pData.DrawBoxTextVertical->length,aCmdData);
			break;
		case EWsGcOpDrawTextLocal:
			iGc->UpdateJustification(*pData.DrawTextLocal->desc);
			break;
		case EWsGcOpDrawBoxTextLocal:
			iGc->UpdateJustification(*pData.BoxTextLocal->desc);
			break;
		case EWsGcOpDrawTextPtr:
			iGc->UpdateJustification(*pData.DrawTextPtr->text);
			break;
		case EWsGcOpDrawTextVerticalPtr:
			iGc->UpdateJustification(*pData.DrawTextVerticalPtr->text);
			break;
		case EWsGcOpDrawBoxTextPtr:
			iGc->UpdateJustification(*pData.DrawBoxTextPtr->text);
			break;
		case EWsGcOpDrawBoxTextVerticalPtr:
			iGc->UpdateJustification(*pData.DrawBoxTextVerticalPtr->text);
			break;
		}
	}

void CPlaybackGc::CommandL(TWsGcOpcodes aOpcode, const TDesC8& aCmdData)
	{
	TWsGcCmdUnion pData;
	pData.any=aCmdData.Ptr();
	
	switch(aOpcode)
		{
	case EWsGcOpStartSegmentedDrawPolygon:
		StartSegmentedDrawPolygonL(pData.StartSegmentedDrawPolygon);
		break;
	case EWsGcOpSegmentedDrawPolygonData:
		SegmentedDrawPolygonData(pData.SegmentedDrawPolygonData);
		break;
	case EWsGcOpSetClippingRegion:
		WS_ASSERT_DEBUG(aOpcode != EWsGcOpSetClippingRegion, EWsPanicDrawCommandsInvalidState);
		break;
	case EWsGcOpSetClippingRect:
		SetClippingRect(*pData.Rect);
		break;
	case EWsGcOpCancelClippingRect:
		ResetClippingRect();
		break;
	case EWsGcOpCancelClippingRegion:
		CancelUserClippingRegion();
		break;
	case EWsGcOpSetFaded:
		iGc->SetFaded(*pData.Bool);
		break;
	case EWsGcOpSetFadeParams:
		iGc->SetFadingParameters(WservEncoding::ExtractFirst8BitValue(*pData.UInt),WservEncoding::ExtractSecond8BitValue(*pData.UInt));
		break;
	case EWsGcOpSetDrawMode:
		iGc->SetDrawMode((CGraphicsContext::TDrawMode)*pData.UInt);
		break;
	case EWsGcOpUseFont:
		if (CWsFontCache::Instance()->UseFont(iFont, *pData.UInt))
			iGc->UseFont(*pData.UInt);
		else
			iGc->UseFontNoDuplicate(iFont);
		break;
	case EWsGcOpDiscardFont:
		CWsFontCache::Instance()->ReleaseFont(iFont);
		iGc->DiscardFont();
		break;
	case EWsGcOpSetUnderlineStyle:
		iGc->SetUnderlineStyle(*pData.SetUnderlineStyle);
		break;
	case EWsGcOpSetStrikethroughStyle:
		iGc->SetStrikethroughStyle(*pData.SetStrikethroughStyle);
		break;
	case EWsGcOpUseBrushPattern:
		iGc->UseBrushPattern(*pData.handle);
		break;
	case EWsGcOpDiscardBrushPattern:
		iGc->DiscardBrushPattern();
		break;
	case EWsGcOpSetBrushColor:
		iGc->SetBrushColor(*pData.rgb);
		break;
	case EWsGcOpSetPenColor:
		iGc->SetPenColor(*pData.rgb);
		break;
	case EWsGcOpSetPenStyle:
		iGc->SetPenStyle((CGraphicsContext::TPenStyle)*pData.UInt);
		break;
	case EWsGcOpSetPenSize:
		iGc->SetPenSize(*pData.Size);
		break;
	case EWsGcOpSetBrushStyle:
		iGc->SetBrushStyle((CGraphicsContext::TBrushStyle)*pData.UInt);
		break;
	case EWsGcOpReset:
		CWsFontCache::Instance()->ReleaseFont(iFont);
		iGc->Reset();
		iOrigin.SetXY(0,0);
		ResetClippingRect();
		iGc->SetBrushColor(iWin->BackColor());
		break;
	case EWsGcOpSetBrushOrigin:
		iGc->SetBrushOrigin(*pData.Point);
		break;
	case EWsGcOpSetDitherOrigin:
		iGc->SetDitherOrigin(*pData.Point);
		break;
	case EWsGcOpSetWordJustification:
		iGc->SetWordJustification(pData.SetJustification->excessWidth, pData.SetJustification->numGaps);
		break;
	case EWsGcOpSetCharJustification:
		iGc->SetCharJustification(pData.SetJustification->excessWidth, pData.SetJustification->numGaps);
		break;
	case EWsGcOpSetOrigin:
		SetOrigin(*pData.Point);
		break;
	case EWsGcOpSetOpaque:
		// Andy - opaque drawing has not been implemented yet.
		//SetOpaque(*pData.Bool);
		break;
	case EWsGcOpSetShadowColor:
		iGc->SetShadowColor(*pData.rgb);
		break;
	default:	// Assume remaining functions will draw
		{
		DoDrawing(aOpcode,aCmdData);
		return;
		}
		}
	}

void CPlaybackGc::SetOrigin(const TPoint &aOrigin)
	{
	iOrigin=aOrigin;
	}
	
/*------------------------------------------------------------------------------
  Description: Retrieves graphics context information back from a given buffer
               from a given start position.
 -----------------------------------------------------------------------------*/
void CPlaybackGc::InternalizeL(const CBufBase& aBuffer,TInt& aStartPos)
	{
	// Open the stream used for the input from the given start position
	// in the buffer.
	RBufReadStream bufReadStream;
	bufReadStream.Open(aBuffer,aStartPos);
	CleanupClosePushL(bufReadStream);

	// Read the font/bitmap server data
	iGc->InternalizeL(bufReadStream);

	iOrigin.iX = bufReadStream.ReadInt32L();
	iOrigin.iY = bufReadStream.ReadInt32L();

	iClippingRectSet=bufReadStream.ReadInt8L();
	
	// If there is a clipping rectangle data read it.
	if (iClippingRectSet)
		bufReadStream>>iClippingRect;
	
	// Force FbsBitGc to reset its user clipping rect in case orientation has changed.
	// The user clipping rect of FbsBitGc will be set to iClippingRect later before
	// drawing the command.
	iGc->CancelClippingRect();
	
	// Read the clipping region data
	InternalizeClippingRegionL(bufReadStream);

	// Read the Alpha values for Brush and Pen colors.
	InternalizeAlphaValueL(bufReadStream);
	
	CleanupStack::PopAndDestroy(&bufReadStream);
	}

/*------------------------------------------------------------------------------
  Description: Retrieves TRgb::alpha value information back from a given buffer
               and updates the Brushcolor with the same.
 -----------------------------------------------------------------------------*/
void CPlaybackGc::InternalizeAlphaValueL(RReadStream& aReadStream)
	{
	TRgb brushColor(iGc->BrushColor());
	brushColor.SetAlpha(aReadStream.ReadUint8L());
	iGc->SetBrushColor(brushColor);
	TRgb penColor(iGc->PenColor());
	penColor.SetAlpha(aReadStream.ReadUint8L());
	iGc->SetPenColor(penColor);
	}

/*------------------------------------------------------------------------------
  Description: Helper method to retrieve clipping region data from a given
               read stream.
 -----------------------------------------------------------------------------*/
void CPlaybackGc::InternalizeClippingRegionL(RReadStream& aReadStream)
	{
	WS_ASSERT_DEBUG(iTargetRegion, EWsPanicDrawCommandsInvalidState);
	// Read flag to indicate if client had defined a clipping region
	TBool clipRegion = aReadStream.ReadInt8L();
	CancelUserClippingRegion();
	if (clipRegion)
		{
		// Note that this clipping region is in window relative coordinates when
		// received from the client (and being stored) but is in screen relative
		// coordinates after being retrieved from the redraw store.
		iUserDefinedClippingRegion = InternalizeRegionL(aReadStream);
		iUserDefinedClippingRegion->Offset(iWin->Origin());
		iUserDefinedClippingRegion->Intersect(*iTargetRegion);
		if (iUserDefinedClippingRegion->CheckError()) // fallback to no user clipping region
			{
			CancelUserClippingRegion();
			}
		else
			{
			iDrawRegion = iUserDefinedClippingRegion;
			}
		}
	}

/**
This pretty much replaces the whole of what was TDrawDestination
This can only be sensibly called from outside a sequence of drawing commands,
since it negates any user defined clipping regions.
*/
void CPlaybackGc::SetTargetRegion(const TRegion* aRegion)
	{
	iTargetRegion = aRegion;
	iDrawRegion = iTargetRegion;
	CancelUserClippingRegion();
	}
	
void CPlaybackGc::Reset()
	{
	iGc->Reset();
	}

TAny * CPlaybackGc::ResolveObjectInterface(TUint aId)
	{
	switch (aId)
		{
		case MWsWindow::EWsObjectInterfaceId:
			return dynamic_cast<MWsWindow *>(iWin);
		case MWsFader::EWsObjectInterfaceId:
		  	return iWin->Screen()->Fader();
		}
	return NULL;
	}