graphicsdeviceinterface/directgdiadaptation/swsrc/swdirectgdidrawresource.cpp
changeset 0 5d03bc08d59c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graphicsdeviceinterface/directgdiadaptation/swsrc/swdirectgdidrawresource.cpp	Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,895 @@
+// Copyright (c) 2008-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:
+//
+
+#include "directgdiadapter.h"
+#include "swdirectgdiengine.h"
+#include "swdirectgdiimagesourceimpl.h"
+#include "swdirectgdidriverimpl.h"
+#include <bitdrawinterfaceid.h>
+#include <e32cmn.h>
+#include <bitdraw.h>
+#include <bmalphablend.h>
+#include <graphics/directgdidrawablesource.h>
+#include <pixelformats.h>
+#include "pixelutil.h"
+
+/**
+Helper class to deal with the case of blending 32-bit MAP source into 16-bit target which is not 
+supported by screen driver CDrawSixteenBppBitmap implementation.
+
+@publishedPartner
+@prototype
+@deprecated
+*/
+class TDrawDeviceWrapper
+	{
+public:
+	TDrawDeviceWrapper(CFbsDrawDevice* aDrawDevice, CGraphicsContext::TDrawMode aDrawMode);
+	inline void WriteLine(TInt aX, TInt aY, TInt aLength, TUint32* aBuffer, CGraphicsContext::TDrawMode aDrawMode);
+private:
+	inline TUint16 ConvertTo64K(TUint32 aColor);
+	inline TUint16 Blend16MapTo64K(TUint16 aDest, TUint32 aSrc);
+	void BlendLine16MapTo64K(TInt aX, TInt aY, TInt aLength, TUint32* aBuffer, CGraphicsContext::TDrawMode aDrawMode);
+	void OriginalWriteLine(TInt aX, TInt aY, TInt aLength, TUint32* aBuffer, CGraphicsContext::TDrawMode aDrawMode);
+
+private:
+	CFbsDrawDevice* iDrawDevice;
+	TUint32* iBits;
+
+	typedef void (TDrawDeviceWrapper::*TWriteLineFunc)(TInt,TInt,TInt,TUint32*,CGraphicsContext::TDrawMode);
+	TWriteLineFunc iWriteLineFunc;
+	};
+
+TDrawDeviceWrapper::TDrawDeviceWrapper(CFbsDrawDevice* aDrawDevice, CGraphicsContext::TDrawMode aDrawMode):
+	iDrawDevice(aDrawDevice)
+	{
+	TAny* interface = NULL;
+	TInt err = iDrawDevice->GetInterface(KFastBlit2InterfaceID, interface);
+	// interface is guaranted to exist for 16-bit and 32-bit draw device
+	GRAPHICS_ASSERT_DEBUG(err == KErrNone, EDirectGdiPanicUnexpectedError);
+
+	iBits = (TUint32*) reinterpret_cast<MFastBlit2*>(interface)->Bits();
+	
+	// setup which funtion to call here rather tha making decision inside WriteLine which is usually called within
+	// a tight scanline loop
+	iWriteLineFunc = iDrawDevice->DisplayMode() == EColor64K && aDrawMode == CGraphicsContext::EDrawModePEN ? 
+			&TDrawDeviceWrapper::BlendLine16MapTo64K : &TDrawDeviceWrapper::OriginalWriteLine;
+	}
+
+inline void TDrawDeviceWrapper::WriteLine(TInt aX, TInt aY, TInt aLength, TUint32* aBuffer, CGraphicsContext::TDrawMode aDrawMode)
+	{
+	// calling member functions via pointer to member functions i.e.
+	// (object.*member_fn)(arg)
+	//
+	(this->*iWriteLineFunc)(aX, aY, aLength, aBuffer, aDrawMode);
+	}
+
+void TDrawDeviceWrapper::OriginalWriteLine(TInt aX, TInt aY, TInt aLength, TUint32* aBuffer, CGraphicsContext::TDrawMode aDrawMode)
+	{
+	iDrawDevice->WriteLine(aX, aY, aLength, aBuffer, aDrawMode);
+	}
+
+void TDrawDeviceWrapper::BlendLine16MapTo64K(TInt aX, TInt aY, TInt aLength, TUint32* aBuffer, CGraphicsContext::TDrawMode)
+	{
+	TUint16* pixelPtr = reinterpret_cast<TUint16*>(iBits);
+	pixelPtr += (aY * iDrawDevice->LongWidth()) + aX;
+	const TUint32* bufferPtr = aBuffer;
+	const TUint32* bufferPtrLimit = bufferPtr + aLength;
+
+	while (bufferPtr < bufferPtrLimit)
+		{
+		*pixelPtr = Blend16MapTo64K(*pixelPtr, *bufferPtr);
+		++bufferPtr;
+		++pixelPtr;
+		}
+	}
+
+inline TUint16 TDrawDeviceWrapper::ConvertTo64K(TUint32 aSrc)
+	{
+	TInt col = (aSrc & 0x0000f8) >> 3;
+	col |= (aSrc & 0x00fc00) >> 5;
+	col |= (aSrc & 0xf80000) >> 8;
+	
+	return col;
+	}
+	
+inline TUint16 TDrawDeviceWrapper::Blend16MapTo64K(TUint16 aDst, TUint32 aSrc)
+	{
+	const TInt alpha = aSrc >> 24;
+
+	if(alpha == 0x00)
+		{
+		return aDst;
+		}
+
+	if (alpha == 0xff)
+		{
+		return ConvertTo64K(aSrc);
+		}
+
+	// extract source components from 16MAP
+	const TInt src_rb = aSrc & 0x00ff00ff;
+	const TInt src_g = aSrc & 0x0000ff00;
+	const TInt oneMinusAlpha = 0x0100 - alpha;
+
+	// extract destination components from 64K format
+    TInt dr = (aDst & 0xf800) >> 8;
+    dr += dr >> 5;
+    TInt dg = (aDst & 0x07e0) >> 3;
+    dg += dg >> 6;
+    TInt db = (aDst & 0x001f) << 3;
+    db += db >> 5;
+
+	// combine red and blue components into a word to combine mult in one go
+	TInt dst_rb = (dr << 16) | db;
+	TInt dst_g = dg << 8;
+
+	// dst and src are in pre-multiplied format (64K can be treated both as pre or non-pre)
+	// dst = src + (1-alpha) * dst
+	//
+	dst_rb = (src_rb + ((oneMinusAlpha * dst_rb) >> 8)) & 0x00ff00ff;
+	dst_g = (src_g + ((oneMinusAlpha * dst_g) >> 8)) & 0x0000ff00;
+	
+	const TUint32 argb = 0xff000000 | dst_rb | dst_g;
+	return ConvertTo64K(argb);
+	}
+
+//
+// implements MDirectGdiEngine interfaces
+//
+
+/**
+@see MDirectGdiEngine::DrawResource(const TRect&,const RDirectGdiDrawableSource&,const TDesC8&)
+*/
+void CSwDirectGdiEngine::DrawResource(
+		const TRect& aDestRect,
+		const RDirectGdiDrawableSource& aSource,
+		const TDesC8& /*aParam*/)
+	{
+	// DirectGDI reference implementation only support pixel based resource
+	// see CSwDirectGdiDriverImpl::CreateDrawableSource()
+	//
+	CSwDirectGdiImageSourceImpl* imgSrc = NULL;
+	TSize imgSize;
+	if (CheckImageSource(aSource.Handle(), imgSrc, &imgSize) != KErrNone)
+		{
+		return;
+		}
+
+	// drawing resource unscaled with no rotation
+	//
+	DrawResourceCommon(aDestRect, imgSrc, TRect(TPoint(0,0), imgSize), DirectGdi::EGraphicsRotationNone);
+	}
+
+/**
+@see MDirectGdiEngine::DrawResource(const TPoint&,const RDirectGdiDrawableSource&,DirectGdi::TGraphicsRotation)
+*/
+void CSwDirectGdiEngine::DrawResource(const TPoint& aPos,
+		const RDirectGdiDrawableSource& aSource,
+		DirectGdi::TGraphicsRotation aRotation)
+	{
+	CSwDirectGdiImageSourceImpl* imgSrc = NULL;
+	TSize imgSize;
+	if (CheckImageSource(aSource.Handle(), imgSrc, &imgSize) != KErrNone)
+		{
+		return;
+		}
+
+	// drawing resource unscaled
+	//
+	// rotation will be applied before scaling, we must create destination rectangle 
+	// that match the size of rotated image
+
+	TRect destRect(aPos, imgSize);
+	if (aRotation==DirectGdi::EGraphicsRotation90 || aRotation==DirectGdi::EGraphicsRotation270)
+		{
+		// keep the top left corner in the same position but swap width and height
+		destRect.SetWidth(imgSize.iHeight);
+		destRect.SetHeight(imgSize.iWidth);
+		}
+
+	DrawResourceCommon(destRect, imgSrc, TRect(TPoint(0,0), imgSize), aRotation);
+	}
+
+/**
+@see MDirectGdiEngine::DrawResource(const TRect&,const RDirectGdiDrawableSource&,DirectGdi::TGraphicsRotation)
+*/
+void CSwDirectGdiEngine::DrawResource(const TRect& aDestRect,
+		const RDirectGdiDrawableSource& aSource,
+		DirectGdi::TGraphicsRotation aRotation)
+	{
+	// aDestRect is not empty when we reach here
+	//
+	CSwDirectGdiImageSourceImpl* imgSrc = NULL;
+	TSize imgSize;
+	if (CheckImageSource(aSource.Handle(), imgSrc, &imgSize) != KErrNone)
+		{
+		return;
+		}
+
+	DrawResourceCommon(aDestRect, imgSrc, TRect(TPoint(0,0), imgSize), aRotation);
+	}
+
+/**
+@see MDirectGdiEngine::DrawResource(const TRect&,const RDirectGdiDrawableSource&,const TRect&,DirectGdi::TGraphicsRotation)
+*/
+void CSwDirectGdiEngine::DrawResource(
+		const TRect& aDestRect,				
+		const RDirectGdiDrawableSource& aSource, 
+		const TRect& aSrcRect,
+		DirectGdi::TGraphicsRotation aRotation)
+	{
+	// pre:
+	// aDestRect and aSrcRect are not empty
+	//
+	CSwDirectGdiImageSourceImpl* imgSrc = NULL;
+	TSize imgSize;
+	if (CheckImageSource(aSource.Handle(), imgSrc, &imgSize) != KErrNone)
+		{
+		return;
+		}
+
+	// check source rectangle is fully contained within the image resource extent
+	if (aSrcRect.iTl.iX < 0 ||
+		aSrcRect.iTl.iY < 0 ||
+		aSrcRect.iBr.iX > imgSize.iWidth ||
+		aSrcRect.iBr.iY > imgSize.iHeight)
+		{
+		iDriver->SetError(KErrArgument);
+		return;
+		}   	
+	
+	DrawResourceCommon(aDestRect, imgSrc, aSrcRect, aRotation);
+	}
+
+//
+// internal functions
+//
+/**
+Checks image resource is fully constructed and registered with the driver.
+
+@param  aHandle A valid handle to an image source.
+@param  aImg On return, contains the image source.
+@param  aSize If not NULL, will contain the dimensions of the image source.
+@return KErrNone if successful, KErrBadHandle if aHandle is not a valid handle to an image source.
+*/
+TInt CSwDirectGdiEngine::CheckImageSource(TInt aHandle, CSwDirectGdiImageSourceImpl*& aImg, TSize* aSize)
+	{
+	// check image exist
+	if (!iDriver->ValidImageSource(aHandle))
+		{
+		// replace KErrNotFound
+		const TInt err = KErrBadHandle;
+		iDriver->SetError(err);
+		return err;
+		}
+	
+	aImg = reinterpret_cast<CSwDirectGdiImageSourceImpl*>(aHandle);
+
+	// RSgImage cannot be created with zero size, so there is no point in validating its size and
+	// simply return image size if requested
+	if (aSize)
+		{		
+		*aSize = aImg->Size();
+		}
+
+	return KErrNone;
+	}
+
+/**
+Implements common DrawResource() functionality used by all DrawResource() variants.
+*/
+void CSwDirectGdiEngine::DrawResourceCommon(
+		const TRect& aDestRect,
+		const CSwDirectGdiImageSourceImpl* aImg,
+		const TRect& aSrcRect,
+		DirectGdi::TGraphicsRotation aRotation)
+	{
+	// pre:
+	// aDestRect and aSrcRect are not empty
+	// aSrcRect is within the image extent
+	//
+	// translate relative coord to target absolute coord system
+	TRect destRectAbs(aDestRect);
+	destRectAbs.Move(iOrigin);
+
+	// check whether we need to blend or write
+	const TBool opaqueSource = (!PixelFormatUtil::HasAlpha(aImg->PixelFormat())) && (iDrawMode == DirectGdi::EDrawModePEN);
+	if (opaqueSource)
+		{
+		iDrawMode = DirectGdi::EDrawModeWriteAlpha;
+		}
+
+	// repeat drawing op for each rectangle in the clipping region
+	const TInt numOfRects = iDefaultRegionPtr->Count();	
+	for (TInt idx = 0; idx < numOfRects; ++idx)
+		{
+		TRect clipRectAbs = (*iDefaultRegionPtr)[idx];
+		if (!clipRectAbs.Intersects(destRectAbs))
+			{
+			continue;
+			}
+
+		// intersect current clip rect with dest rect to get actual draw area
+		clipRectAbs.Intersection(destRectAbs);
+		DoDrawResource(destRectAbs, aImg, aSrcRect, aRotation, clipRectAbs);
+
+		iDrawDevice->UpdateRegion(clipRectAbs);
+		}
+	
+	if (opaqueSource)
+		{
+		iDrawMode = DirectGdi::EDrawModePEN;
+		}	
+	}
+
+/**
+Rotate a given rectangle 90 degree clockwise around the specified origin.
+*/
+void CSwDirectGdiEngine::Rotate90(TRect& aRect, const TPoint& aOrigin)
+	{
+	// rotated bottom-left become top-left of rotated rect
+	//
+	TPoint bl(aRect.iTl.iX, aRect.iBr.iY);
+	const TInt w = aRect.Width();
+	const TInt h = aRect.Height();
+	
+	const TPoint dbl = bl - aOrigin;
+	bl.iX = aOrigin.iX - dbl.iY;
+	bl.iY = aOrigin.iY + dbl.iX;
+
+	aRect = TRect(bl, TSize(h,w));
+	}
+
+/**
+Rotate a given rectangle 180 degree clockwise around the specified origin.
+*/
+void CSwDirectGdiEngine::Rotate180(TRect& aRect, const TPoint& aOrigin)
+	{
+	// rotated bottom-right become top-left of rotated rect
+	//
+	TPoint br(aRect.iBr);
+	const TSize sz = aRect.Size();
+
+	const TPoint dbr = br - aOrigin;
+	br.iX = aOrigin.iX - dbr.iX;
+	br.iY = aOrigin.iY - dbr.iY;
+
+	aRect = TRect(br, sz);
+	}
+
+/**
+Rotate a given rectangle 270 degree clockwise around the specified origin.
+*/
+void CSwDirectGdiEngine::Rotate270(TRect& aRect, const TPoint& aOrigin)
+	{
+	// rotated top-right become top-left of rotated rect
+	//
+	TPoint tr(aRect.iBr.iX, aRect.iTl.iY);
+	const TInt w = aRect.Width();
+	const TInt h = aRect.Height();
+	
+	const TPoint dtr = tr - aOrigin;
+	tr.iX = aOrigin.iX + dtr.iY;
+	tr.iY = aOrigin.iY - dtr.iX;
+
+	aRect = TRect(tr, TSize(h,w));
+	}
+
+/**
+Tests that the size of a given rotated rectangle match the image source extent.
+*/
+TBool CSwDirectGdiEngine::RotatedSizeMatch(const TRect& aDst, const TRect& aSrc, DirectGdi::TGraphicsRotation aRot)
+	{
+	if (aRot==DirectGdi::EGraphicsRotationNone || aRot==DirectGdi::EGraphicsRotation180)
+		{
+		return aDst.Size()==aSrc.Size();
+		}
+	else
+		{
+		return aDst.Width()==aSrc.Height() && aDst.Height()==aSrc.Width();
+		}
+	}
+
+/**
+Draws a rectangular area of resource and apply rotation and/or scaling if requested.
+*/
+void CSwDirectGdiEngine::DoDrawResource(
+		const TRect& aDestRectAbs,
+		const CSwDirectGdiImageSourceImpl* aImg,
+		const TRect& aSrcRect,
+		DirectGdi::TGraphicsRotation aRotation,
+		const TRect& aClipRectAbs)
+	{
+	// pre:
+	// SetClippingRegion() already check that clipping region is always contained
+	// within device full extent.
+
+	// check src rect match the size of rotated dest rect
+	if (RotatedSizeMatch(aDestRectAbs, aSrcRect, aRotation))
+		{
+		// aClipRect is the effective drawing clipped area, it has been intersected with dest rect by the
+		// calling function/DoDrawResourceCommon() and it is not empty when we reach here
+
+		// image size has been checked at the top level DrawResource() no need to check it again
+
+		// use aClipRect to determine how much area should be read from the image and transform
+		// the effective read area into source rect depending on rotation parameter
+		
+		// start with aClipRect
+		TRect xformSrcRect(aClipRectAbs);
+		switch (aRotation)
+			{
+			case DirectGdi::EGraphicsRotationNone:
+				// align top-left corner of dest rect with top-left corner of src rect
+				xformSrcRect.Move(aSrcRect.iTl - aDestRectAbs.iTl);
+				break;
+			case DirectGdi::EGraphicsRotation90:
+				{
+				// align top-right corner of dest rect with top-left corner of src rect
+				xformSrcRect.Move(aSrcRect.iTl - TPoint(aDestRectAbs.iBr.iX, aDestRectAbs.iTl.iY));
+				// rotate 270 (-90) degree using top-left corner of src rect as pivot point
+				Rotate270(xformSrcRect, aSrcRect.iTl);
+				}
+				break;
+			case DirectGdi::EGraphicsRotation180:
+				{
+				// align bottom-right corner of dest rect with top-left corner of src rect
+				xformSrcRect.Move(aSrcRect.iTl - aDestRectAbs.iBr);
+				// rotate 180 (-180) degree using top-left corner of src rect as pivot point
+				Rotate180(xformSrcRect, aSrcRect.iTl);
+				}
+				break;
+			case DirectGdi::EGraphicsRotation270:
+				{
+				// align bottom-left corner of dest rect with top-left corner of src rect
+				xformSrcRect.Move(aSrcRect.iTl - TPoint(aDestRectAbs.iTl.iX, aDestRectAbs.iBr.iY));
+				// rotate 90 (-270) degree using top-left corner of src rect as pivot point
+				Rotate90(xformSrcRect, aSrcRect.iTl);
+				}
+				break;
+
+			// no need for extra check, aRotation has been checked at generic layer
+			}
+
+		DoBlitResource(aClipRectAbs.iTl, aImg, xformSrcRect, aRotation);
+		return;
+		}
+
+	DoScaledBlitResource(aDestRectAbs, aImg, aSrcRect, aRotation, aClipRectAbs);
+	}
+
+/**
+Draws a rectangular area of resource rotated with no scaling.
+@panic DGDIAdapter 1009, if the pixel format of the draw device is unknown (debug only). 
+*/
+void CSwDirectGdiEngine::DoBlitResource(
+		const TPoint& aDest,
+		const CSwDirectGdiImageSourceImpl* aImg,
+		const TRect& aSrcRect,
+		DirectGdi::TGraphicsRotation aRotation)
+	{		
+	// pre:
+	// aDest is the top-left of clipped destination rectangle i.e. intersection of user destination
+	// rectangle with current clipping rect
+	// aSrcRect is rotated clipped read area i.e. effective read area with respect to original 
+	// image orientation i.e.it no longer represents user specified source rect.
+
+	// no need to do extra check on parameters here because:
+	// aDest is guaranteed to be within device drawing area
+	// aSrcRect is guaranteed to be within image source area
+	
+	const TUidPixelFormat devFormat = PixelFormatUtil::ConvertToPixelFormat(iDrawDevice->DisplayMode());
+	GRAPHICS_ASSERT_DEBUG(devFormat != EUidPixelFormatUnknown, EDirectGdiPanicInvalidDisplayMode);
+
+	const TUidPixelFormat imgFormat = aImg->PixelFormat();
+	const TUint32* imgAddr = reinterpret_cast<TUint32*>(aImg->DataBuffer());
+	const TInt imgStride = aImg->Stride();
+	const TSize imgSize = aImg->Size();
+	const CGraphicsContext::TDrawMode drawMode = GcDrawMode(iDrawMode);
+	
+	const TInt width = aSrcRect.Width();
+	const TInt height = aSrcRect.Height();
+	// write scanline starting from top dest row
+	TInt destY = aDest.iY;
+	// setup pixel reader toolkit
+	TPixelBufferReader reader(imgAddr, imgSize, imgStride, imgFormat);
+	TDrawDeviceWrapper writer(iDrawDevice, GcDrawMode(iDrawMode));
+	
+	//
+	// special case when rotation is none
+	//
+	if (aRotation == DirectGdi::EGraphicsRotationNone)
+		{
+		TAny* interface = NULL;
+	
+		// source and destination format match and using write alpha mode i.e. reduce blit to memcpy
+		if (iDrawMode == DirectGdi::EDrawModeWriteAlpha && devFormat == imgFormat)
+			{
+			TInt err = iDrawDevice->GetInterface(KFastBlit2InterfaceID, interface);
+			if (err == KErrNone)
+				{
+				GRAPHICS_ASSERT_DEBUG(interface, EDirectGdiPanicInvalidPointer);
+				MFastBlit2* fblit = static_cast<MFastBlit2*>(interface);
+				err = fblit->WriteBitmapBlock(aDest, imgAddr, imgStride, imgSize, aSrcRect);
+				if (err == KErrNone)
+					{
+					return;
+					}
+				}
+			}
+
+		// fallback from MFastBlit2  when source and destination format match. 
+		// Note that there was previously an optimization added here that used MFlastBlend if 
+		// available, however it did not work correctly for some cases using DrawResource so has been 
+		// removed from this code. (A source drawn with OpenGLES on to a target in XRGB_8888 format
+		// actually had alpha in the unused channel which was being drawn by MFastBlend)
+		if (devFormat == imgFormat)
+			{
+			for (TInt row=aSrcRect.iTl.iY; row<aSrcRect.iBr.iY; ++row,++destY)
+				{
+				const TPoint pos(aSrcRect.iTl.iX, row);
+				const TUint32* slptr = reader.GetPixelAddr(pos);
+				writer.WriteLine(aDest.iX, destY, width, const_cast<TUint32*>(slptr), drawMode);
+				}
+			
+			return;
+			}
+
+		// there is one additional case that can be optimised by eleminating copy when:
+		// rotation : none
+		// scaling 	: none
+		// dst		: opaque
+		// src		: has alpha and premultiplied
+		// mode 	: PEN
+		}
+
+	//
+	// generic cases
+	//
+	// copying is necessary either to convert pixel format or to read back buffer in reverse order 
+	// to achieve rotation
+	//
+	const TInt scanLineBytes = iDrawDevice->ScanLineBytes();
+	TUint32* scanLineBuffer = iDrawDevice->ScanLineBuffer();
+	TPtr8 scanLineDes(reinterpret_cast<TUint8*>(scanLineBuffer),scanLineBytes,scanLineBytes);
+
+	// if destination is opaque e.g. RGB_565, XRGB_8888 and the source has alpha, it shall blend
+	// (because iDrawMode is set to WritePEN)
+	// we treat opaque destination as in pre-multiplied format (with alpha value 1), and read the 
+	// source in pre-multiplied format too, to enable optimum blending computation i.e.
+	// "src + (1-alpha) * dst"
+	const TUidPixelFormat readFormat = !PixelFormatUtil::HasAlpha(devFormat) &&
+							iDrawMode == DirectGdi::EDrawModePEN ?
+							EUidPixelFormatARGB_8888_PRE : devFormat;
+
+	if (aRotation == DirectGdi::EGraphicsRotationNone)
+		{
+		// general fallback from FastBlendBitmap
+		//
+		const TInt readLen = width;
+		// normal read scanline, left to right
+		//
+		for (TInt row=aSrcRect.iTl.iY; row<aSrcRect.iBr.iY; ++row,++destY)
+			{
+			const TPoint pos(aSrcRect.iTl.iX, row);
+			reader.GetScanLine(scanLineDes,pos, readLen, readFormat, TPixelBufferReader::EReadHorizontal);
+			writer.WriteLine(aDest.iX, destY, readLen, scanLineBuffer, drawMode);
+			}
+		}
+	else if (aRotation == DirectGdi::EGraphicsRotation90)
+		{
+		const TInt readLen = height;
+		// read scanline vertically from bottom to up, and from the lef-most column
+		//
+		for (TInt col=aSrcRect.iTl.iX; col<aSrcRect.iBr.iX; ++col,++destY)
+			{
+			const TPoint pos(col, aSrcRect.iBr.iY-1);
+			reader.GetScanLine(scanLineDes, pos, readLen, readFormat, TPixelBufferReader::EReadVerticalReverse);
+			writer.WriteLine(aDest.iX, destY, readLen, scanLineBuffer, drawMode);
+			}
+		}
+	else if (aRotation == DirectGdi::EGraphicsRotation180)
+		{
+		const TInt readLen = width;
+		// read scanline from right to left, starting from the most bottom scanline
+		//
+		for (TInt row=aSrcRect.iBr.iY-1; row>=aSrcRect.iTl.iY; --row,++destY)
+			{
+			const TPoint pos(aSrcRect.iBr.iX-1, row);
+			reader.GetScanLine(scanLineDes, pos, readLen, readFormat, TPixelBufferReader::EReadHorizontalReverse);
+			writer.WriteLine(aDest.iX, destY, readLen, scanLineBuffer, drawMode);
+			}
+		}
+	else if (aRotation == DirectGdi::EGraphicsRotation270)
+		{
+		const TInt readLen = height;
+		// read scanline vertically top to bottom, and from the right-most column
+		//
+		for (TInt col=aSrcRect.iBr.iX-1; col>=aSrcRect.iTl.iX; --col,++destY)
+			{
+			const TPoint pos(col, aSrcRect.iTl.iY);
+			reader.GetScanLine(scanLineDes, pos, readLen, readFormat, TPixelBufferReader::EReadVertical);
+			writer.WriteLine(aDest.iX, destY, readLen, scanLineBuffer, drawMode);
+			}
+		}
+	}
+
+/**
+Draws a rectangular area of resource rotated and/or scaled up/down.
+@panic DGDIAdapter 1009, if the pixel format of the draw device is unknown (debug only).
+*/
+void CSwDirectGdiEngine::DoScaledBlitResource(
+		const TRect& aDestRectAbs,
+		const CSwDirectGdiImageSourceImpl* aImg,
+		const TRect& aSrcRect,
+		DirectGdi::TGraphicsRotation aRotation,
+		const TRect& aClipRectAbs)
+	{
+	// pre:
+	// aDestRectAbs is the user specified dest rect that has been translated to target coord system. It may
+	// be larger/outside the target drawing area. We must not modify this rect as it will be used to
+	// determine the scaling factor.
+	// aSrcRect is the user specified src rect, it is within the image extent.
+	// aClipRectAbs is the intersection of current clipping rect and aDestRectAbs and is within the
+	// target drawing area.
+
+	const TUidPixelFormat devFormat = PixelFormatUtil::ConvertToPixelFormat(iDrawDevice->DisplayMode());
+	GRAPHICS_ASSERT_DEBUG(devFormat != EUidPixelFormatUnknown, EDirectGdiPanicInvalidDisplayMode);
+
+	const TUint32* imgAddr = reinterpret_cast<TUint32*>(aImg->DataBuffer());
+	const TInt imgStride = aImg->Stride();
+	const TSize imgSize = aImg->Size();
+	const TUidPixelFormat imgFormat = aImg->PixelFormat();
+	const CGraphicsContext::TDrawMode drawMode = GcDrawMode(iDrawMode);
+
+	// if destination is opaque e.g. RGB_565, XRGB_8888 and the source has alpha, it shall blend
+	// (because iDrawMode is set to WritePEN)
+	// we treat opaque destination as in pre-multiplied format (with alpha value 1), and read the 
+	// source in pre-multiplied format too, to enable optimum blending computation i.e.
+	// "src + (1-alpha) * dst"
+	const TUidPixelFormat readFormat = !PixelFormatUtil::HasAlpha(devFormat) &&
+							iDrawMode == DirectGdi::EDrawModePEN ?
+							EUidPixelFormatARGB_8888_PRE : devFormat;
+
+	TUint32* scanLineBuffer = iDrawDevice->ScanLineBuffer();
+	const TInt scanLineBytes = iDrawDevice->ScanLineBytes();
+	TPtr8 scanLineDes(reinterpret_cast<TUint8*>(scanLineBuffer), scanLineBytes, scanLineBytes);
+
+	// setup pixel reader toolkit
+	//
+	TPixelBufferReader reader(imgAddr, imgSize, imgStride, imgFormat);
+	TDrawDeviceWrapper writer(iDrawDevice, GcDrawMode(iDrawMode));
+
+	if (aRotation == DirectGdi::EGraphicsRotationNone)
+		{		
+		// Note that there was previously an optimization added here that used MFlastBlend if 
+		// available, however it did not work correctly for some cases using DrawResource so has been 
+		// removed from this code. (A source drawn with OpenGLES on to a target in XRGB_8888 format
+		// actually had alpha in the unused channel which was being drawn by MFastBlend)
+		//
+		const TInt srcLen = aSrcRect.Width();
+		const TInt destLen = aDestRectAbs.Width();
+		const TInt clipLen = aClipRectAbs.Width();
+		const TInt clipPos = aClipRectAbs.iTl.iX - aDestRectAbs.iTl.iX;
+
+		// setup DDA for scaling in Y direction based on src rect and dst rect size
+		// scaling in X direction will be done by GetScaledScanLine()
+		//
+		// setup Y scaler and start from top-left of src/dest to bottom-right of src/dest
+		
+		TLinearDDA yScaler;
+		yScaler.Construct(
+				TPoint(aSrcRect.iTl.iY, aDestRectAbs.iTl.iY),
+				TPoint(aSrcRect.iBr.iY, aDestRectAbs.iBr.iY),
+				TLinearDDA::ELeft);
+
+		// move to position of current clip rect top row as the start of dest Y
+		TInt srcY;	
+		yScaler.JumpToYCoord2(srcY, aClipRectAbs.iTl.iY);
+
+		// yPos contains mapping between src Y (TPoint.iX) and dest Y (TPoint.iY)
+		TPoint yPos(srcY, aClipRectAbs.iTl.iY);
+
+		// write to target from top to bottom
+		//
+		while (yPos.iY < aClipRectAbs.iBr.iY)
+			{
+			reader.GetScaledScanLine(scanLineDes,
+									TPoint(aSrcRect.iTl.iX, yPos.iX),	// src X and Y
+									clipPos,							// clipped dest X
+									clipLen,
+									destLen,
+									srcLen,
+									readFormat,
+									TPixelBufferReader::EReadHorizontal);
+			
+			// use dest Y here
+			writer.WriteLine(aClipRectAbs.iTl.iX, yPos.iY, clipLen, scanLineBuffer, drawMode);
+
+			// move scaler one position
+			yScaler.NextStep(yPos);
+			};
+		}
+	else if (aRotation == DirectGdi::EGraphicsRotation90)
+		{
+		// we're going to read source vertically from bottom to up, swap relevant bits and pieces
+		// dst-width corresponds to src-height
+		// dst-height corresponds to src-width
+		//
+		const TInt srcLen = aSrcRect.Height();
+		const TInt destLen = aDestRectAbs.Width();
+
+		// the following doesn't change, the amount of pixel read (vertically) will be based on
+		// the drawing area witdh i.e. clip width
+		//
+		const TInt clipLen = aClipRectAbs.Width();
+
+		// offset into read area doesn't change either, it will be translated into some Y position
+		// from bottom row of source image
+		const TInt clipPos = aClipRectAbs.iTl.iX - aDestRectAbs.iTl.iX;
+
+		// setup DDA for scaling in src X direction based on src rect and dst rect size
+		// scaling in src Y direction will be done by GetScaledScanLine(EReadVerticalReverse)
+		//
+		// scaler map dest Y to src X because of 90 degre rotation
+		// start from  top row dest, left-most column src to bottom row dest, right-most column src
+		//
+		TLinearDDA xScaler;
+		xScaler.Construct(
+				TPoint(aSrcRect.iTl.iX, aDestRectAbs.iTl.iY),
+				TPoint(aSrcRect.iBr.iX, aDestRectAbs.iBr.iY),
+				TLinearDDA::ELeft);
+		
+		// move to position of current clip rect top row as the start of src X
+		TInt srcX;	
+		xScaler.JumpToYCoord2(srcX, aClipRectAbs.iTl.iY);
+
+		// xPos contains mapping between src X (TPoint.iX) and dest Y (TPoint.iY)
+		TPoint xPos(srcX, aClipRectAbs.iTl.iY);
+
+		// write to target from top to bottom
+		//
+		while (xPos.iY < aClipRectAbs.iBr.iY)
+			{
+			// read pixel vertically from left column to right
+			reader.GetScaledScanLine(scanLineDes,
+									TPoint(xPos.iX, aSrcRect.iBr.iY - 1),	// src X, src Y
+									clipPos,								// distance from bottom source
+									clipLen,
+									destLen,
+									srcLen,
+									readFormat,
+									TPixelBufferReader::EReadVerticalReverse);
+			
+			// use dest Y here
+			writer.WriteLine(aClipRectAbs.iTl.iX, xPos.iY, clipLen, scanLineBuffer, drawMode);
+
+			// move scaler one position
+			xScaler.NextStep(xPos);
+			}
+		}
+	else if (aRotation == DirectGdi::EGraphicsRotation180)
+		{
+		const TInt srcLen = aSrcRect.Width();
+		const TInt destLen = aDestRectAbs.Width();
+		const TInt clipLen = aClipRectAbs.Width();
+
+		// clipPos doesn't need to be inverted (using iBr.iX) because the yScaler below
+		// will do that for us (by stepping backward)
+		//
+		const TInt clipPos = aClipRectAbs.iTl.iX - aDestRectAbs.iTl.iX;
+
+		// setup DDA for scaling in Y direction based on src rect and dst rect size
+		// scaling in X direction will be done by GetScaledScanLine()
+		//
+		// setup Y scaler and start from bottom-right of src/dest to top-left of src/dest
+		// to do backward stepping (src Y inversion)
+
+		TLinearDDA yScaler;
+		yScaler.Construct(
+				// we starting from 1st pixel from bottom of source image, we need to
+				// step back 1 position as bottom-row coord is beyond the last pixel in the source rect
+				TPoint(aSrcRect.iBr.iY - 1, aDestRectAbs.iTl.iY),
+				TPoint(aSrcRect.iTl.iY - 1, aDestRectAbs.iBr.iY),
+				TLinearDDA::ELeft);
+
+		// move to position of current clip rect top row as the start of dest Y
+		// which will calculate inverted src Y distance from bottom of source rectangle
+		TInt invSrcY;	
+		yScaler.JumpToYCoord2(invSrcY, aClipRectAbs.iTl.iY);
+
+		// yPos contains mapping between inverted src Y (TPoint.iX) and dest Y (TPoint.iY)
+		TPoint yPos(invSrcY, aClipRectAbs.iTl.iY);
+
+		// write to target from top to bottom
+		//
+		while (yPos.iY < aClipRectAbs.iBr.iY)
+			{
+			// read scanline from righ to left i.e. use aSrcRect.iBr.iX-1 (last column)
+			// as starting src X value
+			reader.GetScaledScanLine(scanLineDes,
+									TPoint(aSrcRect.iBr.iX - 1, yPos.iX),	// src X and inverted src Y
+									clipPos,								// clipped dest X
+									clipLen,
+									destLen,
+									srcLen,
+									readFormat,
+									TPixelBufferReader::EReadHorizontalReverse);
+			
+			// use dest Y here
+			writer.WriteLine(aClipRectAbs.iTl.iX, yPos.iY, clipLen, scanLineBuffer, drawMode);
+
+			// move scaler one position
+			yScaler.NextStep(yPos);
+			}
+		}
+	else if(aRotation == DirectGdi::EGraphicsRotation270)
+		{
+		// we're going to read source vertically from top to bottom, swap relevant bits and pieces
+		// dst-width corresponds to src-height
+		// dst-height corresponds to src-width
+		const TInt srcLen = aSrcRect.Height();
+		const TInt destLen = aDestRectAbs.Width();
+
+		// the following doesn't change, the amount of pixel read (vertically) will be based on
+		// the drawing area witdh i.e. clip width
+		const TInt clipLen = aClipRectAbs.Width();
+
+		// offset into read area doesn't change either, it will be translated into some Y position
+		// from top row of source image
+		const TInt clipPos = aClipRectAbs.iTl.iX - aDestRectAbs.iTl.iX;
+
+		// setup DDA for scaling in src X direction based on src rect and dst rect size
+		// scaling in src Y direction will be done by GetScaledScanLine(EReadVertical)
+		//
+		// scaler map dest Y to src X because of 270 degre rotation
+		// start from top row dest, right-most column src to bottom row dest, left-most column src
+
+		TLinearDDA xScaler;
+		xScaler.Construct(
+				// decrement 1 pixel to get into last pixel within source
+				TPoint(aSrcRect.iBr.iX - 1, aDestRectAbs.iTl.iY),
+				TPoint(aSrcRect.iTl.iX - 1, aDestRectAbs.iBr.iY),
+				TLinearDDA::ELeft);
+
+		// move to position of current clip rect top row as the start of src X
+		TInt srcX;	
+		xScaler.JumpToYCoord2(srcX, aClipRectAbs.iTl.iY);
+		// xPos contains mapping between src X (TPoint.iX) and dest Y (TPoint.iY)
+		TPoint xPos(srcX, aClipRectAbs.iTl.iY);
+
+		// write to target from top to bottom
+		//
+		while (xPos.iY < aClipRectAbs.iBr.iY)
+			{
+			// read pixel vertically from left column to right
+			reader.GetScaledScanLine(scanLineDes,
+									TPoint(xPos.iX, aSrcRect.iTl.iY),		// src X, src Y
+									clipPos,								// distance from bottom source
+									clipLen,
+									destLen,
+									srcLen,
+									readFormat,
+									TPixelBufferReader::EReadVertical);
+			
+			// use dest Y here
+			writer.WriteLine(aClipRectAbs.iTl.iX, xPos.iY, clipLen, scanLineBuffer, drawMode);
+
+			// move scaler one position
+			xScaler.NextStep(xPos);
+			}
+		}
+	}