diff -r 000000000000 -r 5d03bc08d59c graphicsdeviceinterface/directgdiadaptation/swsrc/swdirectgdidrawresource.cpp --- /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 +#include +#include +#include +#include +#include +#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(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(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(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(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(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(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(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.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(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(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); + } + } + }