// Copyright (c) 2006-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 "BMDRAW.H"
#include <graphics/lookuptable.h>
#include <graphics/blendingalgorithms.h>

/*
Does the same conversion as NonPMA2PMAPixel, but leaves the values in the RB and G uint32 values
ready for use in the blending algorithm. Also assumes alphas of 0 or 0xFF have already been
stripped out as special cases.
*/
FORCEINLINE void NonPMA2PMAPixelRBG(TUint32 aPixel, TUint32 &aSrcRB, TUint32 &aSrcG, TUint32 &aMask)
	{
	TUint32 alpha=(aPixel >> 24);
	aMask = 0x0100 - alpha;	// for use in PMABlend_fromRBandG
	TUint32 tap1=alpha+1;
	aSrcRB = (aPixel & KRBMask) * tap1;
	aSrcRB >>= 8;
	aSrcRB &= KRBMask;
	aSrcG = (aPixel&0xFF000000) | (((aPixel & KGMask)>>8) * tap1) & KAGMask;
	}

/*
Uses same algorithm as PMABlend_noChecksInplace, but optimised for use with NonPMA2PMAPixelRBG where
source values are pre-split into RB and G components ready for use in blend
*/
FORCEINLINE void PMABlend_fromRBandG(TUint32& aDest_io, const TUint32 aSrcRB, const TUint32 aSrcG, const TUint8 aMask)
	{
	TUint32 dst_ag = (aDest_io & KAGMask) >> 8;
	aDest_io = aDest_io  & KRBMask;
	aDest_io = (aSrcRB +  ((aMask * aDest_io) >> 8)) & KRBMask;
	aDest_io |= (aSrcG +  (aMask * dst_ag)) & KAGMask;
	}

/*
Takes a non-PM source colour and an alpha value
First pre-multiplies aSrcPixel with aAlpha
Then blends it into aDestIo using the same alpha value.
*/
FORCEINLINE void PMABlend_FromNonPmAndAlpha(TUint32& aDest_io, const TUint32& aSrcPixel, TUint32 aAlpha)
	{
	TUint32 tap1=aAlpha+1;
	TUint32 srcRB = (aSrcPixel & KRBMask) * tap1;
	srcRB=(srcRB>>8) & KRBMask;
	TUint32 srcAG = (aSrcPixel & KGMask ) * tap1;
	srcAG=(srcAG>>8) & KGMask;
	srcAG|=aAlpha<<24;

	TUint32 dst_ag = (aDest_io & KAGMask) >> 8;
	TUint32 dst_rb = aDest_io & KRBMask;
	const TUint32 mask = 0x0100 - aAlpha;
	aDest_io = (srcRB +  ((mask*dst_rb) >> 8)) & KRBMask;
	aDest_io |= (srcAG + mask*dst_ag) & KAGMask;
	}

// Same as NonPMA2PMAPixel, but for use where checks for alpha of 0 or 0xFF have already been done
FORCEINLINE TUint32 NonPMA2PMAPixelNoCheck(TUint32 aPixel)
	{
	TUint32 tap1=(aPixel>>24)+1;
	TUint32 scaledRB = (aPixel & KRBMask) * tap1;
	TUint32 scaledG = (aPixel & KGMask ) * tap1;
	return (aPixel & 0xff000000) | ((scaledRB>>8) & KRBMask) | ((scaledG>>8)& KGMask);
	}

/**
Composite mask application
where AlphaComposite = AlphaColor * AlphaMask / 255.
@param aColor - non-premltiplied color with alpha 
@param aMask  - the mask value. (0 to 255).
@return the color with alpha combined from color and mask (combination
operator used is scaled multiplication).
*/
FORCEINLINE TUint32 CompMask(TUint32 aColor, TUint8 aMask)
	{
	// if the mask is FF the colour will be fully opaque and can be immediatly returned
	if (aMask == 0xff)
		return aColor;
	
	TUint32 cAlpha = (aMask+1) * (aColor >> 24);
	return ((aColor & 0x00ffffff) | ((cAlpha <<16) & 0xff000000));
	}

TInt CDrawThirtyTwoBppBitmapAlphaPM::Construct(TSize aSize)
	{
	return Construct(aSize, aSize.iWidth << 2);
	}

TInt CDrawThirtyTwoBppBitmapAlphaPM::Construct(TSize aSize, TInt aStride)
	{
	iDispMode = EColor16MAP;
	return CDrawThirtyTwoBppBitmapCommon::Construct(aSize, aStride);
	}

/**
MAlphaBlend::WriteRgbAlphaLine() implementation.
Blends the supplied buffer of TRgb format data (ARGB), and the mask, to the destination
which is specified by the pixel positions aX, aY and length.
@param aX	The starting position on the x-axis for the destination pixel.
@param aY	The starting position on the y-axis for the destination pixel.
@param aRgbBuffer	The RGB source data buffer. This is assumed to be bit aligned 32 bit non-premultiplied data
@param aMaskBuffer	The masking data. The mask buffer and the source alpha values are multiplied together to
					generate the alpha used for blending to the destination.
@param aShadowing	The shadowing flag. Indicates when and how to apply shadowing.
@param aDrawMode	The mode for rendering the source image to the destination. 
					Currently supports EWriteAlpha and EPenmode.

@see MAlphaBlend::WriteRgbAlphaLine()
*/
void CDrawThirtyTwoBppBitmapAlphaPM::WriteRgbAlphaLine(TInt aX, TInt aY, TInt aLength,
                                                  const TUint8* aRgbBuffer,
                                                  const TUint8* aMaskBuffer,
                                                  MAlphaBlend::TShadowing aShadowing,
                                                  CGraphicsContext::TDrawMode aDrawMode)
    {
    // precondition for this function is that the aRgbBuffer lies on a word boundary
    // Assert checks that the pointer is at a word boundary
    __ASSERT_DEBUG(!(((TUint)aRgbBuffer) & 0x3), Panic(EScreenDriverPanicInvalidPointer));

	DeOrientate(aX,aY);
	TUint32* pixelPtr = PixelAddress(aX,aY);
	const TInt pixelPtrInc = PixelAddressIncrement();
	const TUint16* normTable = PtrTo16BitNormalisationTable();
	TRgb tmp;
	const TUint8* maskBufferPtrLimit = aMaskBuffer + aLength;
	// The purpose of this conditional is to remove if statements from within the loop
	// if shadow mode is not enabled and the UserDispMode is none or EColor16MAP
	if(!(iShadowMode & (EFade | EShadow)) && (iUserDispMode ==EColor16MAP || iUserDispMode == ENone))
		{
		while (aMaskBuffer < maskBufferPtrLimit)
			{
			if (*aMaskBuffer)
				{
				TUint32 srcData = *((TUint32*)aRgbBuffer);
				if ((aDrawMode == CGraphicsContext::EDrawModeWriteAlpha) && (*aMaskBuffer == 0xFF))
					{
					// Write the source straight through to the target, otherwise
					// blend. This relies on the fact that EDrawModeWriteAlpha can only
					// be used with masks that contain only black or white.
					Convert2PMA(srcData);
					*pixelPtr = srcData;
					}
				else
					{
					//blend alpha value in source with the mask value.
					srcData = CompMask(srcData, *aMaskBuffer);
					//pre-multiply, inplace.
					Convert2PMA(srcData);
					PMAInplaceBlend(*pixelPtr, srcData);
					}
				}
			pixelPtr += pixelPtrInc;
			aRgbBuffer += 4;
			aMaskBuffer++;
			}
		}
	else
		{
		while (aMaskBuffer < maskBufferPtrLimit)
			{
			TUint32 srcColor = *((TUint32*)(aRgbBuffer));
			if(*aMaskBuffer)
				{
				if(aShadowing == MAlphaBlend::EShdwBefore)
					{
					// src color in non-premultiplied.
					Shadow(*(TRgb*)&srcColor);
					}
				TUint32 srcData;
				if ((aDrawMode == CGraphicsContext::EDrawModeWriteAlpha) && (*aMaskBuffer == 0xFF))
					{
					Convert2PMA(srcColor);
					srcData = srcColor;
					}
				else
					{
					//blend alpha value in source with the mask value.
					srcData = CompMask(srcColor, *aMaskBuffer);
					//pre-multiply, inplace.
					Convert2PMA(srcData);
					srcData = PMAPixelBlend(*pixelPtr, srcData);
					}

				if(aShadowing == MAlphaBlend::EShdwAfter)
					{
					// shadow is done in PMA mode.
					Shadow(srcData);
					}
				if(iUserDispMode !=EColor16MAP && iUserDispMode != ENone)
					{
					tmp.SetInternal(PMA2NonPMAPixel(srcData, normTable));
					CDrawThirtyTwoBppBitmapCommon::MapColorToUserDisplayMode(tmp);
					srcData = tmp.Internal();
					Convert2PMA(srcData);
					}
				*pixelPtr = srcData;
				}
			pixelPtr += pixelPtrInc;
			aRgbBuffer += 4;
			aMaskBuffer++;
			}
		}
	}

/**
This assumes that the pen color (aColor) alpha information is to be blended with
the alpha provided by the mask buffer.
If pen is opaque, the mask is used as transparency information to be used.
If pen is semi-transparent, the mask is contructed by calculating the resulting alpha
by multiplying the mask with the alpha information of the pen color.
@see CFbsDrawDevice::WriteRgbAlphaMulti
*/
void CDrawThirtyTwoBppBitmapAlphaPM::WriteRgbAlphaMulti(TInt aX,TInt aY,TInt aLength,TRgb aColor,const TUint8* aMaskBuffer)
	{
	TUint32 srcAlpha = aColor.Alpha();
	if (srcAlpha==0 || aLength<=0)
		return;
	DeOrientate(aX,aY);
	TUint32* pixelPtr = PixelAddress(aX,aY);
	const TInt pixelPtrInc = PixelAddressIncrement();
	const TUint8* maskBufferPtrLimit = aMaskBuffer + aLength;
	if (iShadowMode)
		CDrawThirtyTwoBppBitmapCommon::Shadow(aColor);
	// pre-multiply the color.
	const TUint32 srcColor = aColor.Internal();
	if (srcAlpha == 255)
		{ // source (pen) is opaque, so we simply blend it using the mask
		// little trick to make main loop more optimised, having 'pixelPtr += pixelPtrInc' at the top 
		// of the loop makes the compiler generate better code without extra jumps to reach the
		// 'pixelPtr += pixelPtrInc' line.
		// We also need to do the FOREVER loop with a break when aMaskBuffer==maskBufferPtrLimit or
		// else the compiler puts the test at the end and extra jumps are required to reach it.
		pixelPtr -= pixelPtrInc;
		FOREVER
			{
			pixelPtr += pixelPtrInc;
			if (aMaskBuffer==maskBufferPtrLimit)
				break;
			const TUint maskAlpha=*aMaskBuffer++;
			if (maskAlpha==0)
				{	// Most pixels in text are blank, so this is the most important route to optimise
				continue;
				}
			else if (maskAlpha==0xFF)
				{	// Second most important are fully opaque pixels
				*pixelPtr=srcColor;
				continue;
				}
			TUint32 maskColor = NonPMA2PMAPixelNoCheck((srcColor&~0xFF000000) | ((TUint32)maskAlpha << 24));
			PMAInplaceBlend(*pixelPtr, maskColor);
			}
		}
	else if(srcAlpha > 0)
		{
		// pen is semi-transparent, so we must blend using both the mask and pen alpha
		// pre calculate ffMaskColor optimised for common case where aMaskBuffer contains 0xFF
		const TUint32 ffMaskColor=NonPMA2PMAPixelNoCheck(srcColor);
		const TUint32 noAlphaSrcColor=srcColor&0xFFFFFF;
		while (aMaskBuffer < maskBufferPtrLimit)
			{
			const TUint maskAlpha=*aMaskBuffer;
			if (maskAlpha)
				{
				if (maskAlpha==0xFF)
					PMABlend_noChecksInplace(*pixelPtr, ffMaskColor, srcAlpha);
				else
					{
					TUint32 mixedAlpha = ((maskAlpha+1) * srcAlpha)>>8;
					PMABlend_FromNonPmAndAlpha(*pixelPtr, noAlphaSrcColor, mixedAlpha);
					}
				}
			pixelPtr += pixelPtrInc;
			aMaskBuffer++;
			}
		}
	}

/**
@see CFbsDrawDevice::WriteRgb
*/
void CDrawThirtyTwoBppBitmapAlphaPM::WriteRgb(TInt aX,TInt aY,TRgb aColor)
	{
	TUint32 srcColor = NonPMA2PMAPixel(aColor.Internal());
	TUint32* pixelPtr = PixelAddress(aX,aY);
	PMAInplaceBlend(*pixelPtr, srcColor);
	}

/**
@see CFbsDrawDevice::WriteBinary
*/
void CDrawThirtyTwoBppBitmapAlphaPM::WriteBinary(TInt aX,TInt aY,TUint32* aBuffer,TInt aLength,TInt aHeight,TRgb aColor)
	{
	const TUint32 colorInternal=aColor.Internal();
	const TUint8  srcAlpha = colorInternal >> 24;
	if (srcAlpha==0)
		return;
	DeOrientate(aX,aY);

	TInt pixelInc;
	TInt rowInc;

	switch(iOrientation)
		{
		case EOrientationNormal:
			{
			pixelInc = 1;
			rowInc = iScanLineWords;
			break;
			}
		case EOrientationRotated90:
			{
			pixelInc = iScanLineWords;
			rowInc = -1;
			break;
			}
		case EOrientationRotated180:
			{
			pixelInc = -1;
			rowInc = -iScanLineWords;
			break;
			}
		default: // EOrientationRotated270
			{
			pixelInc = -iScanLineWords;
			rowInc = 1;
			}
		}

	const TUint32* dataLimit = aBuffer + aHeight;
	const TUint32 dataMaskLimit = (aLength < 32) ? 1 << aLength : 0;

	TUint32* pixelPtr = PixelAddress(aX,aY);
	if (srcAlpha==0xFF)
		{
		while (aBuffer < dataLimit)
			{
			TUint32 dataWord = *aBuffer++;
			TUint32 dataMask = 1;
			TUint32* tempPixelPtr = pixelPtr;
			while (dataMask != dataMaskLimit)
				{
				if(dataWord & dataMask)
					{
					*tempPixelPtr=colorInternal;
					}

				tempPixelPtr += pixelInc;
				dataMask <<= 1;
				}

			pixelPtr += rowInc;
			}
		}
	else
		{
		TUint32 src_rb;
		TUint32 src_g;
		TUint32 mask;
	//pre-multiply the src color, before blending.
		NonPMA2PMAPixelRBG(colorInternal,src_rb,src_g,mask);
		while (aBuffer < dataLimit)
			{
			TUint32 dataWord = *aBuffer++;
			TUint32 dataMask = 1;
			TUint32* tempPixelPtr = pixelPtr;
			while (dataMask != dataMaskLimit)
				{
				if(dataWord & dataMask)
					{
					PMABlend_fromRBandG(*tempPixelPtr, src_rb, src_g, mask);
					}

				tempPixelPtr += pixelInc;
				dataMask <<= 1;
				}

			pixelPtr += rowInc;
			}
		}
	}

/**
@see CFbsDrawDevice::WriteBinaryLineVertical
*/
void CDrawThirtyTwoBppBitmapAlphaPM::WriteBinaryLineVertical(TInt aX,TInt aY,TUint32* aBuffer,TInt aHeight,TRgb aColor,TBool aUp)
	{
	const TUint32 colorInternal=aColor.Internal();
	const TInt sourceAlpha = colorInternal>>24;
	if (sourceAlpha==0)
		return;

	DeOrientate(aX,aY);

	TInt scanlineWordLength;

	switch(iOrientation)
		{
		case EOrientationNormal:
			scanlineWordLength = iScanLineWords;
			break;
		case EOrientationRotated90:
			scanlineWordLength = -1;
			break;
		case EOrientationRotated180:
			scanlineWordLength = -iScanLineWords;
			break;
		default: // EOrientationRotated270
			scanlineWordLength = 1;
		}

	if (aUp)
		scanlineWordLength = -scanlineWordLength;

	TUint32* pixelPtr = PixelAddress(aX,aY);
	const TUint32* pixelPtrLimit = pixelPtr + (aHeight * scanlineWordLength);
	TUint32 dataWord = *aBuffer;
	TUint32 dataMask = 1;

	if (sourceAlpha==0xFF)
		{
		while(pixelPtr != pixelPtrLimit)
			{
			if(!dataMask)
				{
				dataMask = 1;
				aBuffer++;
				dataWord = *aBuffer;
				}

			if(dataWord & dataMask)
				{
				*pixelPtr = colorInternal;
				}
			dataMask <<= 1;
			pixelPtr += scanlineWordLength;
			}
		}
	else
		{
		TUint32 src_rb;
		TUint32 src_g;
		TUint32 mask;
	//pre-multiply the src color, before blending.
		NonPMA2PMAPixelRBG(colorInternal,src_rb,src_g,mask);
		while(pixelPtr != pixelPtrLimit)
			{
			if(!dataMask)
				{
				dataMask = 1;
				aBuffer++;
				dataWord = *aBuffer;
				}

			if(dataWord & dataMask)
				{
				PMABlend_fromRBandG(*pixelPtr, src_rb, src_g, mask);
				}
			dataMask <<= 1;
			pixelPtr += scanlineWordLength;
			}
		}
	}

/**
@see CFbsDrawDevice::BlendRgbMulti
*/
void CDrawThirtyTwoBppBitmapAlphaPM::BlendRgbMulti(TInt aX,TInt aY,TInt aLength,TInt aHeight,TRgb aColor)
	{
	const TInt sourceAlpha = aColor.Alpha();
	if (sourceAlpha==255)// opaque
		{
		WriteRgbMulti(aX,aY,aLength,aHeight,aColor);
		return;
		}
	if (sourceAlpha==0)// transparent
		return;
	TUint32* pixelPtr = PixelAddress(aX,aY);
	TUint32* pixelRowPtrLimit = pixelPtr + (aHeight * iScanLineWords);
	TUint32* pixelPtrLimit = pixelPtr + aLength;

	TUint32 srcValue = NonPMA2PMAPixel(aColor.Internal());
	TUint32 src_c = srcValue & KRBMask;
	TUint32 src_g = (srcValue & KAGMask)>>8;
	const TUint32 mask = 0x0100 - sourceAlpha;
	while (pixelPtr < pixelRowPtrLimit)
		{
		for (TUint32* tempPixelPtr = pixelPtr; tempPixelPtr < pixelPtrLimit; tempPixelPtr++)
			{
			const TUint32 dst = *tempPixelPtr;
			TUint32 dst_c = dst  & KRBMask;
			dst_c = (src_c +  ((mask * dst_c)>>8)) & KRBMask;
			const TUint32 dst_ag = (dst & KAGMask)>>8;
			dst_c |= ((src_g +  ((mask * dst_ag)>>8)) & KRBMask)<<8;
			*tempPixelPtr=dst_c;
			}
		pixelPtr += iScanLineWords;
		pixelPtrLimit += iScanLineWords;
		}
	}

/**
@see CFbsDrawDevice::BlendLine
*/
void CDrawThirtyTwoBppBitmapAlphaPM::BlendLine(TInt aX,TInt aY,TInt aLength,TUint32* aBuffer)
	{
	TUint32* pixelPtr = PixelAddress(aX,aY);

	const TUint32* bufferPtrLimit = aBuffer + aLength;
	const TInt pixelPtrInc = (iOrientation == EOrientationNormal) ? 1 : PixelAddressIncrement();

	while (aBuffer < bufferPtrLimit)
		{
		PMAInplaceBlend(*pixelPtr,  *aBuffer);
		aBuffer++;
		pixelPtr += pixelPtrInc;
		}
	}
/**
@see CFbsDrawDevice::ShadowArea
*/
void CDrawThirtyTwoBppBitmapAlphaPM::ShadowArea(const TRect& aRect)
	{
	const TRect rect(DeOrientate(aRect));
	__ASSERT_DEBUG(rect.iTl.iX>=0 && rect.iBr.iX<=iSize.iWidth,Panic(EScreenDriverPanicOutOfBounds));
	__ASSERT_DEBUG(rect.iTl.iY>=0 && rect.iBr.iY<=iSize.iHeight,Panic(EScreenDriverPanicOutOfBounds));

	TUint32* pixelPtr = PixelAddress(rect.iTl.iX,rect.iTl.iY);
	TUint32* pixelRowPtrLimit = pixelPtr + (rect.Height() * iScanLineWords);

	TUint32* pixelRowPtr = pixelPtr;
	TUint32* pixelPtrLimit = pixelPtr + rect.Width();

	if (iShadowMode & EFade)
		{
		while (pixelRowPtr < pixelRowPtrLimit)
			{
			TUint32* tempPixelPtr = pixelRowPtr;

			while (tempPixelPtr < pixelPtrLimit)
				{
				const TUint32 color = *tempPixelPtr;
#if defined(SYMBIAN_USE_FAST_FADING)
				const TUint32 fast_fade_offset = NonPMA2PMAPixel((color & 0xff000000) | SYMBIAN_USE_FAST_FADING) & 0x00ffffff;
				*tempPixelPtr++ = (color & 0xff000000) | ((((color & 0x00ffffff) >> 1) & ~0x00808080) + fast_fade_offset);
#else
				const TInt alpha = (color >> 24) + 1;
				const TUint32 fadeMapOffset = ((alpha * iFadeMapOffset) >> 8) & 0xff;
				const TUint32 wordFadeMapOffset = ((fadeMapOffset) << 16) | (fadeMapOffset);
				const TUint32 rb = ((((color & 0x00ff00ff) * iFadeMapFactor) >> 8) + wordFadeMapOffset) & 0x00ff00ff;
		  		const TUint32 g = ((((color & 0x0000ff00) * iFadeMapFactor) >> 16) + fadeMapOffset) << 8;
		  		*tempPixelPtr++ = (color & 0xff000000) | rb | g;
#endif
				}
			pixelRowPtr += iScanLineWords;
			pixelPtrLimit += iScanLineWords;
			}
		}

	if (iShadowMode & EShadow)
		{
		pixelRowPtr = pixelPtr;
		pixelPtrLimit = pixelPtr + rect.Width();

		while (pixelRowPtr < pixelRowPtrLimit)
			{
			TUint32* tempPixelPtr = pixelRowPtr;

			while (tempPixelPtr < pixelPtrLimit)
				{
				const TUint32 color = *tempPixelPtr;
				const TInt alpha = (color >> 24) + 1;
				const TInt uLimit = ((0x40) * alpha) >> 8;
				TInt r = (color >> 16) & 0xff;
				r = (r > uLimit) ? (r-uLimit) : 0;
				TInt g = (color >> 8) & 0xff;
				g = (g > uLimit) ? (g - uLimit) : 0;
				TInt b = color & 0xff;
				b = (b > uLimit) ? (b - uLimit) : 0;
				*tempPixelPtr++	=  (color & 0xff000000) | (r << 16) | (g << 8) | b;
				}
			pixelRowPtr += iScanLineWords;
			pixelPtrLimit += iScanLineWords;
			}
		}
	}

/**
@see CFbsDrawDevice::WriteLine
*/
void CDrawThirtyTwoBppBitmapAlphaPM::WriteLine(TInt aX,TInt aY,TInt aLength,TUint32* aBuffer,
		CGraphicsContext::TDrawMode aDrawMode)
	{
	const TPoint originalPoint(aX,aY);
	DeOrientate(aX,aY);//aX and aY - physical coordinates

	__ASSERT_DEBUG(aX >= 0,Panic(EScreenDriverPanicOutOfBounds));
	__ASSERT_DEBUG(aY >= 0,Panic(EScreenDriverPanicOutOfBounds));
#if defined(_DEBUG)
	switch (iOrientation)
		{
	case EOrientationNormal:
		__ASSERT_DEBUG(aX + aLength <= iSize.iWidth,Panic(EScreenDriverPanicOutOfBounds));
		break;
	case EOrientationRotated90:
		__ASSERT_DEBUG(aY + aLength <= iSize.iHeight,Panic(EScreenDriverPanicOutOfBounds));
		break;
	case EOrientationRotated180:
		__ASSERT_DEBUG(aX - aLength >= -1,Panic(EScreenDriverPanicOutOfBounds));
		break;
	default: // EOrientationRotated270
		__ASSERT_DEBUG(aY - aLength >= -1,Panic(EScreenDriverPanicOutOfBounds));
		break;
		}
#endif
	__ASSERT_DEBUG(aLength > 0,Panic(EScreenDriverPanicZeroLength));
	__ASSERT_DEBUG(aBuffer,Panic(EScreenDriverPanicNullPointer));

	MapBufferToUserDisplayMode(aLength,aBuffer);
	if(iShadowMode)
		{
		ShadowBuffer(aLength,aBuffer);
		}
	if(aDrawMode&CGraphicsContext::EInvertPen)
		{
#if defined(_DEBUG)
		// code in CFbsBitGc::DoBitBltMasked() does not require logical operator pen modes in premultiplied alpha screen driver for blitting of icons with masks
//		RDebug::Printf("Premultiplied alpha mode invert pen behaviour not defined / implemented");
#endif
		// no-operation
		}
	if(aDrawMode&CGraphicsContext::EPenmode)
		{
		BlendLine(aX,aY,aLength,aBuffer);
		return;
		}
	if(aDrawMode&CGraphicsContext::EWriteAlpha)
		{
		CDrawThirtyTwoBppBitmapCommon::WriteLine(aX,aY,aLength,aBuffer);
		return;
		}
	if(aDrawMode&CGraphicsContext::EInvertScreen)
		{
#if defined(_DEBUG)
//		RDebug::Printf("Premultiplied alpha mode invert screen behaviour not defined / implemented");
#endif
		return; //no-operation
		}
	if(aDrawMode&CGraphicsContext::EXor)
		{
#if defined(_DEBUG)
		// code in CFbsBitGc::DoBitBltMasked() does not require logical operator pen modes in premultiplied alpha screen driver for blitting of icons with masks
//		RDebug::Printf("Premultiplied alpha mode XOR operation not defined / implemented");
#endif
		return; //no-operation
		}
else if(aDrawMode&CGraphicsContext::EAnd)
		{
#if defined(_DEBUG)
		// code in CFbsBitGc::DoBitBltMasked() does not require logical operator pen modes in premultiplied alpha screen driver for blitting of icons with masks
//		RDebug::Printf("Premultiplied alpha mode AND operation not defined / implemented");
#endif
		return; //no-operation
		}
	else if(aDrawMode&CGraphicsContext::EOr)
		{
#if defined(_DEBUG)
//		RDebug::Printf("Premultiplied alpha mode OR operation not defined / implemented");
#endif
		return; //no-operation
		}
	}

TRgb CDrawThirtyTwoBppBitmapAlphaPM::RgbColor(TUint32 aColor) const
	{
	return TRgb::_Color16MAP(aColor);
	}

TUint32 CDrawThirtyTwoBppBitmapAlphaPM::Color(const TRgb& aColor)
	{
	return aColor._Color16MAP();
	}
	
TInt CDrawThirtyTwoBppBitmapAlphaPM::WriteRgbOutlineAndShadow(TInt aX, TInt aY, const TInt aLength,
															TUint32 aOutlinePenColor, TUint32 aShadowColor,
															TUint32 aFillColor, const TUint8* aDataBuffer)
	{
	const TUint alphaShifted = aOutlinePenColor & 0xff000000;
	const TUint alpha = alphaShifted>>24;
	if (alpha==0 || aLength<=0)
		return KErrNone;
	DeOrientate(aX,aY);
	TUint32* pixelPtr = PixelAddress(aX,aY);
	const TInt pixelPtrInc = PixelAddressIncrement();
	const TUint8* dataBufferPtrLimit = aDataBuffer + aLength;
	TInt blendedRedColor;
	TInt blendedGreenColor;
	TInt blendedBlueColor;
	TUint8 index = 0;
	TUint32 finalColor;

	//Get red color. Equivalent to TRgb::Red()
	const TInt redOutlinePenColor = (aOutlinePenColor & 0xff0000) >> 16;
	const TInt redShadowColor = (aShadowColor & 0xff0000) >> 16;
	const TInt redFillColor = (aFillColor & 0xff0000) >> 16;

	//Get green color. Equivalent to TRgb::Green()
	const TInt greenOutlinePenColor = (aOutlinePenColor & 0xff00) >> 8;
	const TInt greenShadowColor = (aShadowColor & 0xff00) >> 8;
	const TInt greenFillColor = (aFillColor & 0xff00) >> 8;

	//Get blue color. Equivalent to TRgb::Blue()
	const TInt blueOutlinePenColor = aOutlinePenColor & 0xff;
	const TInt blueShadowColor = aShadowColor & 0xff;
	const TInt blueFillColor = aFillColor & 0xff;
	const TUint16* normTable = PtrTo16BitNormalisationTable();

	// Calculate PMA values for aFillColor & aOutlinePenColor that we can use for fast blending in the simple cases
	// Don't pre calculate PMA version of aShadowColor as it is presumed not to be used enough to make this worthwhile
	const TUint32 pmaFillColor = NonPMA2PMAPixel((aFillColor&0x00FFFFFF)|alphaShifted);
	const TUint32 pmaOutlineColor = NonPMA2PMAPixel(aOutlinePenColor);
	while (aDataBuffer < dataBufferPtrLimit)
		{
		TUint backgroundAlpha;
		TUint outlineAlpha;
		TUint shadowAlpha;
		TUint fillAlpha;
		index = *aDataBuffer++;
		backgroundAlpha = FourColorBlendLookup[index][KBackgroundColorIndex];
		if (backgroundAlpha == 255)
			{
			//background colour
			//No drawing required so move on to next pixel.
			pixelPtr += pixelPtrInc;
			continue;
			}
		fillAlpha=FourColorBlendLookup[index][KFillColorIndex];
		if (fillAlpha == 255)
			{
			//Use fill colour to draw
			finalColor = pmaFillColor;
oneColorBlend:
			if (alpha==0xFF)
				*pixelPtr=finalColor;
			else
				PMABlend_noChecksInplace(*pixelPtr, finalColor, alpha);
			pixelPtr += pixelPtrInc;
			continue;
			}
		outlineAlpha = FourColorBlendLookup[index][KOutlineColorIndex];
		if (outlineAlpha == 255)
			{
			//Use outline colour to draw
			finalColor = pmaOutlineColor;
			goto oneColorBlend;
			}
		shadowAlpha = FourColorBlendLookup[index][KShadowColorIndex];
		if (shadowAlpha == 255)
			{
			//Use shadow colour to draw
			finalColor = NonPMA2PMAPixel((aShadowColor&0x00FFFFFF)|alphaShifted);
			goto oneColorBlend;
			}
		blendedRedColor = redOutlinePenColor * outlineAlpha + 
					   		redShadowColor * shadowAlpha +
					  		redFillColor * fillAlpha;

		blendedGreenColor = greenOutlinePenColor * outlineAlpha + 
							greenShadowColor * shadowAlpha +
							greenFillColor * fillAlpha;

		blendedBlueColor = blueOutlinePenColor * outlineAlpha + 
							blueShadowColor * shadowAlpha +
							blueFillColor * fillAlpha;
		if (backgroundAlpha)
			{
			const TUint32 backgroundColor = PMA2NonPMAPixel(*pixelPtr, normTable);
			blendedRedColor += ((backgroundColor & 0xff0000) >> 16) * backgroundAlpha;
			blendedGreenColor += ((backgroundColor & 0xff00) >> 8) * backgroundAlpha;
			blendedBlueColor += (backgroundColor & 0xff) * backgroundAlpha;
			}
		finalColor = ((blendedRedColor&0xFF00)<<8) | (blendedGreenColor&0xFF00) | (blendedBlueColor>>8);

		if (alpha==0xFF)
			*pixelPtr=finalColor|0xFF000000;
		else
			{
			//pre-multiply, inplace.
			finalColor = NonPMA2PMAPixel(finalColor|alphaShifted);
			PMABlend_noChecksInplace(*pixelPtr, finalColor, alpha);
			}
		pixelPtr += pixelPtrInc;
		}
	return KErrNone;
	}
