imagehandlinglib/Src/CIHLScaler.cpp
changeset 0 2014ca87e772
child 11 dea39715fc05
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imagehandlinglib/Src/CIHLScaler.cpp	Tue Jan 26 15:18:05 2010 +0200
@@ -0,0 +1,1315 @@
+/*
+* Copyright (c) 2004 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:  Implementation of Scaling processor
+*              : using Font and Bitmap Server bitmaps.
+*
+*/
+
+// INCLUDE FILES
+#include "CIHLScaler.h"
+#include "IHLDebugPrint.h" // Debug print
+#include <MIHLImageViewer.h>        // MIHLImageViewer
+#include <fbs.h>
+#include <hal.h>
+
+// Private namespace for constants and functions
+namespace
+	{
+	// Processed pixels per one activesheduler step
+	const TInt KProcessPixelsPerStep = 16384;
+	
+	// These make the fast path scalings step out about every 5-10ms
+	// no reason to cut more aggressively since it just slows down everything
+	const TInt KBytesPerStepFastPath = 640*480;
+	const TInt KBytesPerStepFastPathScaleOnly = 640*480*2;
+	
+	const TInt KStepPrecision = 16; // 16 bit = 65k subpixels
+	const TInt KMaxProcessSize = KMaxTInt / 2;
+	}
+
+//functions and typedefs used inside ProcessBilinear scaling
+namespace
+	{
+	// using our own RGB instead of TRGB as we need TInt32 for each value
+	typedef struct
+		{
+		TInt32 r, g, b, a;
+		} TColorRGB;
+	
+	// structure for handling data of 4 pixels
+	typedef struct
+		{
+		TColorRGB colXY00, colXY01, colXY10, colXY11;
+		TColorRGB col0, col1, col;
+		} TPixelData;
+
+	// functions to handle conversion between TColorRGB and colr value stored in TInt32
+	inline void IntToRgb(TInt32 aIntRGB_val, TColorRGB &col)
+		{
+		col.r = (aIntRGB_val >> 16) & 0x00ff;
+		col.g = (aIntRGB_val >> 8) & 0x00ff;
+		col.b =  aIntRGB_val & 0x00ff;
+		col.a = (aIntRGB_val >> 24) & 0x00ff;
+		}
+	inline TUint32 RgbToInt(TColorRGB &col)
+		{
+		return 0x00000000 | ( ( col.r & 0xff) << 16 ) | ( ( col.g & 0xff) << 8  ) 
+		                  | (   col.b & 0xff)         | ( ( col.a & 0xff) << 24 );
+		}
+
+	// function which interpolates color using gathered data
+	inline void ProcessColorData( TPixelData &dat, TPoint aSrcPos )
+		{
+		TInt32 pixel_width = 1 << KStepPrecision;
+        
+        //first pass (Y axis)
+		dat.col0.r = (dat.colXY01.r - dat.colXY00.r)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY00.r;
+		dat.col0.g = (dat.colXY01.g - dat.colXY00.g)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY00.g;
+		dat.col0.b = (dat.colXY01.b - dat.colXY00.b)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY00.b;
+		dat.col0.a = (dat.colXY01.a - dat.colXY00.a)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY00.a;
+
+		dat.col1.r = (dat.colXY11.r - dat.colXY10.r)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY10.r;
+		dat.col1.g = (dat.colXY11.g - dat.colXY10.g)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY10.g;
+		dat.col1.b = (dat.colXY11.b - dat.colXY10.b)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY10.b;
+		dat.col1.a = (dat.colXY11.a - dat.colXY10.a)*(aSrcPos.iY % pixel_width) / pixel_width + dat.colXY10.a;
+
+		//second pass (X axis)
+		dat.col.r = (dat.col1.r - dat.col0.r)*(aSrcPos.iX % pixel_width) / pixel_width + dat.col0.r;
+		dat.col.g = (dat.col1.g - dat.col0.g)*(aSrcPos.iX % pixel_width) / pixel_width + dat.col0.g;
+		dat.col.b = (dat.col1.b - dat.col0.b)*(aSrcPos.iX % pixel_width) / pixel_width + dat.col0.b;
+		dat.col.a = (dat.col1.a - dat.col0.a)*(aSrcPos.iX % pixel_width) / pixel_width + dat.col0.a;
+		}
+	}
+	
+// ======================== STATIC FACTORY FUNCTION ============================
+// -----------------------------------------------------------------------------
+// IHLScaler::CreateL
+// -----------------------------------------------------------------------------
+//
+EXPORT_C MIHLScaler* IHLScaler::CreateL( const TUint32 aOptions )
+	{
+	return CIHLScaler::NewL( aOptions );
+	}
+
+// ============================ MEMBER FUNCTIONS ===============================
+// -----------------------------------------------------------------------------
+//
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+CIHLScaler::CIHLScaler( const TUint32 aOptions )
+:CActive( CActive::EPriorityStandard ), iOptions( aOptions )
+    {
+	CActiveScheduler::Add( this );
+    }
+
+// -----------------------------------------------------------------------------
+//
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+CIHLScaler* CIHLScaler::NewL( const TUint32 aOptions )
+    {
+    CIHLScaler* self = new( ELeave ) CIHLScaler( aOptions );
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// Destructor
+// -----------------------------------------------------------------------------
+CIHLScaler::~CIHLScaler()
+    {
+	Cancel();
+    }
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::Scale
+// -----------------------------------------------------------------------------
+TInt CIHLScaler::Scale( TRequestStatus& aStatus,
+					const CFbsBitmap& aSrcBitmap, const TRect& aSrcRect,
+					CFbsBitmap& aDstBitmap, const TRect& aDstRect )
+	{
+	if( IsBusy() )
+		{
+		return KErrNotReady;
+		}
+
+    TSize srcRectSize( aSrcRect.Size() );
+    TSize dstRectSize( aDstRect.Size() );
+	if( Abs( srcRectSize.iWidth ) > KMaxProcessSize ||
+	    Abs( srcRectSize.iHeight ) > KMaxProcessSize ||
+	    Abs( dstRectSize.iWidth ) > KMaxProcessSize ||
+	    Abs( dstRectSize.iHeight ) > KMaxProcessSize )
+	    {
+	    return KErrTooBig;
+	    }
+
+	if( !aSrcBitmap.Handle() ||
+		!IsValidRect( aSrcBitmap.SizeInPixels(), aSrcRect ) )
+		{
+		return KErrArgument;
+		}
+
+	if( aDstBitmap.Handle() )
+		{
+		if( !IsValidRect( aDstBitmap.SizeInPixels(), aDstRect ) )
+			{
+			return KErrArgument;
+			}
+		}
+	else
+		{		
+		// Create destination bitmap
+		TSize dstSize( Abs( dstRectSize.iWidth ), Abs( dstRectSize.iHeight ) );
+		TInt err = aDstBitmap.Create( dstSize, aSrcBitmap.DisplayMode() );
+		if( err )
+			{
+			return err;
+			}
+		}
+
+#ifdef RD_MEASURE_THROUGHPUT
+	iStartTime = User::FastCounter(); // For measuring scaler throughput
+#endif
+    
+    TSize srcSize( aSrcBitmap.SizeInPixels() );
+    TSize dstSize( aDstBitmap.SizeInPixels() );
+    
+    if( srcSize.iWidth == 0 || srcSize.iHeight == 0 ||
+        dstSize.iWidth == 0 || dstSize.iHeight == 0 ||
+        aSrcRect.iTl.iX == aSrcRect.iBr.iX ||
+        aSrcRect.iTl.iY == aSrcRect.iBr.iY ||
+        aDstRect.iTl.iX == aDstRect.iBr.iX ||
+        aDstRect.iTl.iY == aDstRect.iBr.iY )
+        {
+        // Bitmap or rect width or height is zero so there is nothing to do.
+        // Complete process without error.
+        iNeedProcess = EFalse;
+        }
+    else
+        {
+        iNeedProcess = ETrue;
+
+    	// Set parameters to member data references
+    	iSrcBitmap = &aSrcBitmap;
+    	iSrcRect = aSrcRect;
+    	iDstBitmap = &aDstBitmap;
+    	iDstRect = aDstRect;
+    	
+    	TDisplayMode srcMode(iSrcBitmap->DisplayMode());
+    	TDisplayMode dstMode(iDstBitmap->DisplayMode());
+
+		IHL_DEBUG3( KIHLDebug1, "CIHLScaler: src bitmap size %dx%d", srcSize.iWidth, srcSize.iHeight);
+		IHL_DEBUG3( KIHLDebug2, "CIHLScaler: dst bitmap size %dx%d", dstSize.iWidth, dstSize.iHeight);
+		IHL_DEBUG5( KIHLDebug3, "CIHLScaler: src bitmap rect %d.%d -> %d.%d", iSrcRect.iTl.iX, iSrcRect.iTl.iY, iSrcRect.iBr.iX, iSrcRect.iBr.iY);
+		IHL_DEBUG5( KIHLDebug4, "CIHLScaler: dst bitmap rect %d.%d -> %d.%d", iDstRect.iTl.iX, iDstRect.iTl.iY, iDstRect.iBr.iX, iDstRect.iBr.iY);
+		IHL_DEBUG2( KIHLDebug5, "CIHLScaler: src bitmap mode %d", srcMode);
+		IHL_DEBUG2( KIHLDebug6, "CIHLScaler: dst bitmap mode %d", dstMode);
+		IHL_DEBUG2( KIHLDebug7, "CIHLScaler: src compressed %d", iSrcBitmap->IsCompressedInRAM());
+		IHL_DEBUG2( KIHLDebug8, "CIHLScaler: dst compressed %d", iSrcBitmap->IsCompressedInRAM());
+		
+		// Init stepper values
+    	TBool onlyScaling = InitStepperValues();
+    	
+    	// Select correct code path
+    	InitCodePath(srcSize, srcMode, dstSize, dstMode, onlyScaling);
+        }
+
+    IHL_DEBUG1( KIHLDebug, "IHL - CIHLScaler - Start  bitmap scaling" );
+	
+	// Start processing
+	iScalerStatus = &aStatus;
+	*iScalerStatus = KRequestPending;
+
+	SelfComplete();
+	return KErrNone;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::IsBusy
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::IsBusy() const
+	{
+	return IsActive();
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::CancelProcess
+// -----------------------------------------------------------------------------
+void CIHLScaler::CancelProcess()
+	{
+	Cancel();
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::SetFilter
+// -----------------------------------------------------------------------------
+void CIHLScaler::SetFilter( MIHFilter* /*aFilter*/ )
+	{
+	// Not in use
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::DoCancel
+// -----------------------------------------------------------------------------
+void CIHLScaler::DoCancel()
+	{
+	RequestComplete( KErrCancel );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::RunL
+// -----------------------------------------------------------------------------
+void CIHLScaler::RunL()
+	{
+	User::LeaveIfError( iStatus.Int() );
+
+	// Process bitmap (this if-else may look weird but it removes one extra triggering of the AS
+	if( iNeedProcess )
+		{
+		// Call the selected code path directly via a function pointer
+		iNeedProcess = (this->*ProcessingFunc)();
+		}
+	
+	if(!iNeedProcess)
+		{
+#ifdef RD_MEASURE_THROUGHPUT
+		// Calculate throughput
+		TUint32 end = User::FastCounter();
+		
+		TInt tickPeriod;
+		HAL::Get(HALData::EFastCounterFrequency, tickPeriod);
+		TReal time = TReal(end-iStartTime) / TReal(tickPeriod);
+		TReal bytes = iProcessSize.iWidth*iProcessSize.iHeight*2;
+		
+		IHL_DEBUG3("IHL - CIHLScaler - Scaling complete, %.3f ms, %.3f MB/s", time*1000.0, (bytes/time)/1024.0/1024.0);
+#else
+        IHL_DEBUG1( KIHLDebug, "IHL - CIHLScaler - Scaling complete!" );
+#endif
+		// Process complete
+		RequestComplete( KErrNone );
+		}
+	else
+		{
+		// Another round
+		SelfComplete();
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::RunError
+// -----------------------------------------------------------------------------
+TInt CIHLScaler::RunError( TInt aError )
+	{
+    IHL_DEBUG2( KIHLDebug, "IHL - CIHLScaler - Scaling error: %d", aError );
+
+	RequestComplete( aError );
+	return KErrNone;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::IsValidRect
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::IsValidRect( const TSize& aSize, const TRect& aRect )
+	{
+	if( aRect.iTl.iX >= 0 &&
+		aRect.iTl.iX <= aSize.iWidth &&
+		aRect.iTl.iY >= 0 &&
+		aRect.iTl.iY <= aSize.iHeight &&
+		aRect.iBr.iX >= 0 &&
+		aRect.iBr.iX <= aSize.iWidth &&
+		aRect.iBr.iY >= 0 &&
+		aRect.iBr.iY <= aSize.iHeight )
+		{
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::InitStepperValues
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::InitStepperValues()
+	{
+	TBool onlyScaling = EFalse;
+	
+	TSize srcRectSize( Abs( iSrcRect.Width() ), Abs( iSrcRect.Height() ) );
+	TSize dstRectSize( Abs( iDstRect.Width() ), Abs( iDstRect.Height() ) );
+
+	TReal srcW = srcRectSize.iWidth << KStepPrecision;
+	TReal srcH = srcRectSize.iHeight << KStepPrecision;
+	
+    // -----------------------------------------
+	// Set source start point and increment
+    // -----------------------------------------
+	if( iSrcRect.iTl.iX > iSrcRect.iBr.iX &&
+		iSrcRect.iTl.iY < iSrcRect.iBr.iY )
+		{
+        TReal incrementInner( srcH / TReal(dstRectSize.iWidth) );
+        TReal incrementOuter( srcW / TReal(dstRectSize.iHeight) );
+
+		iSrcIncrementInner.iX = 0;
+		iSrcIncrementInner.iY = incrementInner;
+		iSrcIncrementOuter.iX = -incrementOuter;
+		iSrcIncrementOuter.iY = 0;
+
+		iSrcPos.SetXY( (iSrcRect.iTl.iX << KStepPrecision) - (incrementOuter / 2.0),
+		               (iSrcRect.iTl.iY << KStepPrecision) + (incrementInner / 2.0) );
+		}
+	else if	( iSrcRect.iTl.iX > iSrcRect.iBr.iX &&
+			  iSrcRect.iTl.iY > iSrcRect.iBr.iY )
+		{
+        TReal incrementInner( srcW / TReal(dstRectSize.iWidth) );
+        TReal incrementOuter( srcH / TReal(dstRectSize.iHeight) );
+
+		iSrcIncrementInner.iX = -incrementInner;
+		iSrcIncrementInner.iY = 0;
+		iSrcIncrementOuter.iX = 0;
+		iSrcIncrementOuter.iY = -incrementOuter;
+
+		iSrcPos.SetXY( (iSrcRect.iTl.iX << KStepPrecision) - (incrementInner / 2.0),
+		               (iSrcRect.iTl.iY << KStepPrecision) - (incrementOuter / 2.0) );
+		}
+	else if	( iSrcRect.iTl.iX < iSrcRect.iBr.iX &&
+			  iSrcRect.iTl.iY > iSrcRect.iBr.iY )
+		{
+        TReal incrementInner( srcH / TReal(dstRectSize.iWidth) );
+        TReal incrementOuter( srcW / TReal(dstRectSize.iHeight) );
+
+		iSrcIncrementInner.iX = 0;
+		iSrcIncrementInner.iY = -incrementInner;
+		iSrcIncrementOuter.iX = incrementOuter;
+		iSrcIncrementOuter.iY = 0;
+
+		iSrcPos.SetXY( (iSrcRect.iTl.iX << KStepPrecision) + (incrementOuter / 2.0),
+		               (iSrcRect.iTl.iY << KStepPrecision) - (incrementInner / 2.0) );
+		}
+	else
+		{
+        TReal incrementInner( srcW / TReal(dstRectSize.iWidth) );
+        TReal incrementOuter( srcH / TReal(dstRectSize.iHeight) );
+
+		IHL_DEBUG3(KIHLDebug, "incrementInner: %f, incrementOuter: %f", incrementInner, incrementOuter);
+		
+		iSrcIncrementInner.iX = incrementInner;
+		iSrcIncrementInner.iY = 0;
+		iSrcIncrementOuter.iX = 0;
+		iSrcIncrementOuter.iY = incrementOuter;
+
+		iSrcPos.SetXY( (iSrcRect.iTl.iX << KStepPrecision) + (incrementInner / 2.0),
+		               (iSrcRect.iTl.iY << KStepPrecision) + (incrementOuter / 2.0) );
+		               
+		onlyScaling = ETrue;
+		}
+
+	// -----------------------------------------
+	// Set destination start point and increment
+    // -----------------------------------------
+	
+	iDstPos.iX = iDstRect.iTl.iX;
+	iDstIncrementInner = 1;
+	iDstPos.iY = iDstRect.iTl.iY;
+	iDstIncrementOuter = 1;
+	
+	if( iDstRect.iTl.iX > iDstRect.iBr.iX )
+		{
+		// From right to left
+		iDstPos.iX--;
+		iDstIncrementInner = -1;
+		onlyScaling = EFalse;
+		}
+	
+	if( iDstRect.iTl.iY > iDstRect.iBr.iY )
+		{
+		// From bottom to up
+		iDstPos.iY--;
+		iDstIncrementOuter = -1;
+		onlyScaling = EFalse;
+		}
+
+    // -----------------------------------------
+	// Reset process counters
+    // -----------------------------------------
+	iProcessInner = 0;
+	iProcessOuter = 0;
+	iProcessSize = dstRectSize;
+	
+	// These are for fast code path
+	
+	// Init how many scanlines to process
+	iScanlinesLeft = iProcessSize.iHeight; 
+		
+	return onlyScaling;
+	}
+
+
+// Optimize the fast code paths properly for ARM
+#ifdef __ARMCC__
+#pragma push
+#pragma O3
+#pragma Otime
+#pragma arm
+#endif
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::ProcessNearestNeighbour64K
+// Description: Scales & rotates 64K color bitmaps
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::ProcessNearestNeighbour64K()
+	{
+	// Lock bitmap heaps
+	iSrcBitmap->LockHeap();
+	iDstBitmap->LockHeap();
+	
+	// Pointers to start of bitmap data
+	TUint16 *srcBitmapPtr = (TUint16*)iSrcBitmap->DataAddress();
+	TUint16 *dstBitmapPtr = (TUint16*)iDstBitmap->DataAddress();
+	
+	// Get pointer to correct destination pixel (offset is updated after every round)
+	register TUint16* dst = dstBitmapPtr + iDstStartOffset;
+	
+	TUint32 width = iProcessSize.iWidth;
+	
+	// Check how many scanlines we can process this round
+	register TUint32 scanlinesLeft = iScanlinesLeft;
+	if(scanlinesLeft>iScanlinesPerRound)
+		{
+		scanlinesLeft = iScanlinesPerRound;
+		}
+
+	// How many scanlines left for the next round
+	iScanlinesLeft-=scanlinesLeft;
+	
+	// Faster access to variables (it's slow to use member variables)
+	register TInt srcPosX(iSrcPos.iX);
+	register TInt srcPosY(iSrcPos.iY);
+	register TInt srcPitch = iSrcPitchInPixels;
+	
+	TInt incOuterSrcX = (-iSrcIncrementInner.iX * width) + iSrcIncrementOuter.iX;
+	TInt incOuterSrcY = (-iSrcIncrementInner.iY * width) + iSrcIncrementOuter.iY;
+	
+	while(scanlinesLeft!=0)
+        {
+		for(register TUint32 x = width; x!=0; x--)
+			{
+			register TUint16* src = srcBitmapPtr + ((srcPosY >> KStepPrecision) * srcPitch) + (srcPosX>>KStepPrecision);
+			*dst = *src;
+
+            // Add inner counters
+			srcPosX+=iSrcIncrementInner.iX;
+			srcPosY+=iSrcIncrementInner.iY;
+			
+			dst+=iDstIncrementInner;
+			}
+			
+		// Reset inner counters and add outer counters
+		srcPosX += incOuterSrcX;
+		srcPosY += incOuterSrcY;
+
+		// Move destination pointer to next pixel
+		dst += iDstResidualPixels;
+		
+		// One scanline done, n to go
+		scanlinesLeft--;
+		}
+	// Unlock bitmap heaps
+	iDstBitmap->UnlockHeap();
+	iSrcBitmap->UnlockHeap();
+	
+	if(iScanlinesLeft)
+		{
+		// Not all scanlines were processed yet
+		
+		// Save the necessary offsets for next round
+		iSrcPos.iX = srcPosX;
+		iSrcPos.iY = srcPosY;
+		iDstStartOffset = dst - dstBitmapPtr;
+		
+		return ETrue;
+		}
+	
+	// Processing done
+	return EFalse;
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::ProcessNearestNeighbour64KScaleOnly
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::ProcessNearestNeighbour64KScaleOnly()
+	{
+	// Lock bitmap heaps
+	iSrcBitmap->LockHeap();
+	iDstBitmap->LockHeap();
+	
+	// Get bitmap data start addresses
+	TUint16 *srcBitmapPtr = (TUint16*)iSrcBitmap->DataAddress();
+	TUint16 *dstBitmapPtr = (TUint16*)iDstBitmap->DataAddress();
+
+	// Target height and width
+	TInt width = iProcessSize.iWidth;
+	
+	// fixed point source coordinates
+	TInt startX = iSrcPos.iX;
+	TInt srcY = iSrcPos.iY;
+	
+	// How much to increase src position (in fixed point)
+	TInt srcYIncrement = iSrcIncrementOuter.iY;
+	register TInt srcXIncrement = iSrcIncrementInner.iX;
+	
+	// Set pointers to correct location (src = start of scanline, dst = start pixel)
+	register TUint16* dst = dstBitmapPtr + iDstStartOffset;
+	
+	// Calculate how many scanlines we can process this round
+	register TInt scanlinesLeft = iScanlinesLeft;
+	if(scanlinesLeft>iScanlinesPerRound)
+	{
+		scanlinesLeft = iScanlinesPerRound;
+	}
+	
+	iScanlinesLeft-=scanlinesLeft;
+		
+	while(scanlinesLeft--)
+		{
+		// Outer loop
+		
+		// Reset src X and scanline pointer
+		register TInt srcX = startX;
+		register TUint16* src = srcBitmapPtr + (srcY >> KStepPrecision) * iSrcPitchInPixels;
+		
+		// Init pixel counter
+		register TUint32 pixels = width;
+		
+		// Unaligned pixels to the left of 8B-aligned section
+		while((TUint32(dst)&0x7) && pixels)
+		{
+			*dst++ = (*(src + (srcX >> KStepPrecision)));
+
+			srcX += srcXIncrement;
+			
+			pixels--;
+		}
+		
+		// Aligned middle section		
+		register TUint32 middle = pixels&0xFFFFFFFC;
+		pixels &= 0x3;
+		
+		while(middle)
+			{
+			// Read four pixels
+			register TUint16 p1 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			register TUint16 p2 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			register TUint16 p3 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			register TUint16 p4 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			
+			// Write four pixels
+			*(dst++) = p1;
+			*(dst++) = p2;
+			*(dst++) = p3;
+			*(dst++) = p4;
+
+			middle-=4;
+			}
+			
+		// Unaligned residual pixels to the right of 8-aligned section
+		while(pixels)
+		{
+			*dst++ = (*(src + (srcX >> KStepPrecision)));
+
+			srcX += srcXIncrement;
+			
+			pixels--;
+		}
+		
+		// Move to next correct src scanline
+		srcY += srcYIncrement;
+		
+		// Advance dst to start of correct pixel in the next row
+		dst += iDstResidualPixels;
+		}
+	
+	iDstBitmap->UnlockHeap();
+	iSrcBitmap->UnlockHeap();
+	
+	if(iScanlinesLeft)
+		{
+		// Not all scanlines were processed yet
+		
+		// Save the necessary offsets for next round
+		iSrcPos.iY = srcY;
+		iDstStartOffset = dst - dstBitmapPtr;
+
+		return ETrue;
+		}
+	
+	return EFalse;
+	
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::ProcessNearestNeighbour16MX
+// Description: Scales & rotates 16MU/MA color bitmaps
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::ProcessNearestNeighbour16MX()
+	{
+	// Lock bitmap heaps
+	iSrcBitmap->LockHeap();
+	iDstBitmap->LockHeap();
+	
+	// Pointers to start of bitmap data
+	TUint32 *srcBitmapPtr = (TUint32*)iSrcBitmap->DataAddress();
+	TUint32 *dstBitmapPtr = (TUint32*)iDstBitmap->DataAddress();
+	
+	// Get pointer to correct destination pixel (offset is updated after every round)
+	register TUint32* dst = dstBitmapPtr + iDstStartOffset;
+	
+	TUint32 width = iProcessSize.iWidth;
+	
+	// Check how many scanlines we can process this round
+	register TUint32 scanlinesLeft = iScanlinesLeft;
+	if(scanlinesLeft>iScanlinesPerRound)
+		{
+		scanlinesLeft = iScanlinesPerRound;
+		}
+
+	// How many scanlines left for the next round
+	iScanlinesLeft-=scanlinesLeft;
+	
+	// Faster access to variables (it's slow to use member variables)
+	register TInt srcPosX(iSrcPos.iX);
+	register TInt srcPosY(iSrcPos.iY);
+	register TInt srcPitch = iSrcPitchInPixels;
+	
+	TInt incOuterSrcX = (-iSrcIncrementInner.iX * width) + iSrcIncrementOuter.iX;
+	TInt incOuterSrcY = (-iSrcIncrementInner.iY * width) + iSrcIncrementOuter.iY;
+	
+	while(scanlinesLeft!=0)
+        {
+		for(register TUint32 x = width; x!=0; x--)
+			{
+			register TUint32* src = srcBitmapPtr + ((srcPosY >> KStepPrecision) * srcPitch) + (srcPosX>>KStepPrecision);
+			*dst = *src;
+
+            // Add inner counters
+			srcPosX+=iSrcIncrementInner.iX;
+			srcPosY+=iSrcIncrementInner.iY;
+			
+			dst+=iDstIncrementInner;
+			}
+			
+		// Reset inner counters and add outer counters
+		srcPosX += incOuterSrcX;
+		srcPosY += incOuterSrcY;
+
+		// Move destination pointer to next pixel
+		dst += iDstResidualPixels;
+		
+		// One scanline done, n to go
+		scanlinesLeft--;
+		}
+	// Unlock bitmap heaps
+	iDstBitmap->UnlockHeap();
+	iSrcBitmap->UnlockHeap();
+	
+	if(iScanlinesLeft)
+		{
+		// Not all scanlines were processed yet
+		
+		// Save the necessary offsets for next round
+		iSrcPos.iX = srcPosX;
+		iSrcPos.iY = srcPosY;
+		iDstStartOffset = dst - dstBitmapPtr;
+		
+		return ETrue;
+		}
+	
+	// Processing done
+	return EFalse;
+	}
+	
+// -----------------------------------------------------------------------------
+// CIHLScaler::ProcessNearestNeighbour64KScaleOnly
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::ProcessNearestNeighbour16MXScaleOnly()
+	{
+	// Lock bitmap heaps
+	iSrcBitmap->LockHeap();
+	iDstBitmap->LockHeap();
+	
+	// Get bitmap data start addresses
+	TUint32 *srcBitmapPtr = (TUint32*)iSrcBitmap->DataAddress();
+	TUint32 *dstBitmapPtr = (TUint32*)iDstBitmap->DataAddress();
+
+	// Target height and width
+	TInt width = iProcessSize.iWidth;
+	
+	// fixed point source coordinates
+	TInt startX = iSrcPos.iX;
+	TInt srcY = iSrcPos.iY;
+	
+	// How much to increase src position (in fixed point)
+	TInt srcYIncrement = iSrcIncrementOuter.iY;
+	register TInt srcXIncrement = iSrcIncrementInner.iX;
+	
+	// Set pointers to correct location (src = start of scanline, dst = start pixel)
+	register TUint32* dst = dstBitmapPtr + iDstStartOffset;
+	
+	// Calculate how many scanlines we can process this round
+	register TInt scanlinesLeft = iScanlinesLeft;
+	if(scanlinesLeft>iScanlinesPerRound)
+	{
+		scanlinesLeft = iScanlinesPerRound;
+	}
+	
+	iScanlinesLeft-=scanlinesLeft;
+		
+	while(scanlinesLeft--)
+		{
+		// Outer loop
+		
+		// Reset src X and scanline pointer
+		register TInt srcX = startX;
+		register TUint32* src = srcBitmapPtr + (srcY >> KStepPrecision) * iSrcPitchInPixels;
+		
+		// Init pixel counter
+		register TUint32 pixels = width;
+		
+		// Unaligned pixels to the left of 16B-aligned section
+		while((TUint32(dst)&0xF) && pixels)
+		{
+			*dst++ = (*(src + (srcX >> KStepPrecision)));
+
+			srcX += srcXIncrement;
+			
+			pixels--;
+		}
+		
+		// Aligned middle section		
+		register TUint32 middle = pixels&0xFFFFFFFC;
+		pixels &= 0x3;
+		
+		while(middle)
+			{
+			// Read four pixels
+			register TUint32 p1 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			register TUint32 p2 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			register TUint32 p3 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			register TUint32 p4 = (*(src + (srcX >> KStepPrecision)));
+			srcX += srcXIncrement;
+			
+			// Write four pixels
+			*(dst+0) = p1;
+			*(dst+1) = p2;
+			*(dst+2) = p3;
+			*(dst+3) = p4;
+
+			middle-=4;
+			dst+=4;		
+			}
+			
+		// Unaligned residual pixels to the right of 8-aligned section
+		while(pixels)
+		{
+			*dst++ = (*(src + (srcX >> KStepPrecision)));
+
+			srcX += srcXIncrement;
+			
+			pixels--;
+		}
+		
+		// Move to next correct src scanline
+		srcY += srcYIncrement;
+		
+		// Advance dst to start of correct pixel in the next row
+		dst += iDstResidualPixels;
+		}
+	
+	iDstBitmap->UnlockHeap();
+	iSrcBitmap->UnlockHeap();
+	
+	if(iScanlinesLeft)
+		{
+		// Not all scanlines were processed yet
+		
+		// Save the necessary offsets for next round
+		iSrcPos.iY = srcY;
+		iDstStartOffset = dst - dstBitmapPtr;
+
+		return ETrue;
+		}
+	
+	return EFalse;
+	
+	}
+	
+// Restore previous pragma state
+#ifdef __ARMCC__
+#pragma pop
+#endif
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::ProcessNearestNeighbour
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::ProcessNearestNeighbour()
+	{
+	// Util needs non-const pointers even if it's only used for reading pixels
+    TBitmapUtil srcBitmap( const_cast<CFbsBitmap*>( iSrcBitmap ) );
+    TBitmapUtil dstBitmap( iDstBitmap );
+    srcBitmap.Begin( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+    dstBitmap.Begin( iDstPos ); // heap already locked by srcBitmap
+	TInt pixelCounter( KProcessPixelsPerStep );
+
+    // Start outer and inner process loops
+    while( iProcessOuter < iProcessSize.iHeight && pixelCounter )
+        {
+		while( iProcessInner < iProcessSize.iWidth && pixelCounter )
+			{
+			srcBitmap.SetPos( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+			dstBitmap.SetPos( iDstPos );
+			dstBitmap.SetPixel( srcBitmap );
+
+            // Add inner counters
+			iSrcPos += iSrcIncrementInner;
+			iDstPos.iX += iDstIncrementInner;
+			++iProcessInner;
+			--pixelCounter;
+			}
+        // Check if inner loop is finished (not just pixel counter)
+		if( iProcessInner == iProcessSize.iWidth )
+			{
+			// Reset inner counters
+			iSrcPos.iX -= iSrcIncrementInner.iX * iProcessInner;
+			iSrcPos.iY -= iSrcIncrementInner.iY * iProcessInner;
+
+			iDstPos.iX -= iDstIncrementInner * iProcessInner;
+			iProcessInner = 0;
+
+            // Add outer counters
+			iSrcPos += iSrcIncrementOuter;
+			iDstPos.iY += iDstIncrementOuter;
+			++iProcessOuter;
+			}
+		}
+
+    // Release heap lock
+    dstBitmap.End();
+    srcBitmap.End();
+
+    // ETrue if more processing is still needed
+	return ( iProcessOuter < iProcessSize.iHeight );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::ProcessBilinear
+// -----------------------------------------------------------------------------
+TBool CIHLScaler::ProcessBilinear()
+	{
+	// Util needs non-const pointers
+	//even if it's only used for reading pixels
+	TBitmapUtil srcBitmap( const_cast<CFbsBitmap*>( iSrcBitmap ) );
+	TBitmapUtil dstBitmap( iDstBitmap );
+
+	srcBitmap.Begin( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+	dstBitmap.Begin( iDstPos ); // heap already locked by srcBitmap
+	TInt pixelCounter( KProcessPixelsPerStep );
+
+	TInt incSrcX = iSrcIncrementInner.iX + iSrcIncrementOuter.iX;
+	TInt incSrcY = iSrcIncrementInner.iY + iSrcIncrementOuter.iY;
+
+	TPixelData pixelData;
+	TUint32 color;
+
+	TInt32 pixel_width = 1 << KStepPrecision;
+
+	//if there is no scaling then process through loop using NearestNeighbour alghoritm
+	if ( incSrcX == pixel_width || -incSrcX == pixel_width ) 
+		{
+		while( iProcessOuter < iProcessSize.iHeight && pixelCounter )
+			{
+			while( iProcessInner < iProcessSize.iWidth && pixelCounter )
+				{
+				srcBitmap.SetPos( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+				dstBitmap.SetPos( iDstPos );
+				dstBitmap.SetPixel( srcBitmap );
+
+				// Add inner counters
+				iSrcPos += iSrcIncrementInner;
+				iDstPos.iX += iDstIncrementInner;
+				++iProcessInner;
+				--pixelCounter;
+				}
+
+			// Check if inner loop is finished (not just pixel counter)
+			if( iProcessInner == iProcessSize.iWidth )
+				{
+				// Reset inner counters
+				iSrcPos.iX -= iSrcIncrementInner.iX * iProcessInner;
+				iSrcPos.iY -= iSrcIncrementInner.iY * iProcessInner;
+
+				iDstPos.iX -= iDstIncrementInner * iProcessInner;
+				iProcessInner = 0;
+
+				// Add outer counters
+				iSrcPos += iSrcIncrementOuter;
+				iDstPos.iY += iDstIncrementOuter;
+				++iProcessOuter;
+				}
+			}
+		}
+	// Process using bilinear method according to 
+	// orientation between source and destination bitmap
+	//
+	// There are 4 possibilities of orientation and can be
+	// determined by this if we're increasing or decreasing
+	// position on source bitmap.
+	else if ( ( incSrcY >= 0) && (incSrcX >= 0 ) )
+		{
+		while( iProcessOuter < iProcessSize.iHeight && pixelCounter )
+			{
+			while( iProcessInner < iProcessSize.iWidth && pixelCounter )
+				{
+				srcBitmap.SetPos( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+				dstBitmap.SetPos( iDstPos );
+
+				// gather pixel data with step:
+				// 00 > 10 
+				//      V
+				// 01 < 11
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY00);
+				srcBitmap.IncXPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY10);
+				srcBitmap.IncYPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY11);
+				srcBitmap.DecXPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY01);
+				//return to starting position
+				srcBitmap.DecYPos();
+
+				// interpolate color using gathered pixel-data
+				ProcessColorData(pixelData, iSrcPos);
+
+				// put color in destination package
+				color = RgbToInt(pixelData.col);
+				dstBitmap.SetPixel(color);
+
+				// Add inner counters
+				iSrcPos += iSrcIncrementInner;
+				iDstPos.iX += iDstIncrementInner;
+				++iProcessInner;
+				--pixelCounter;
+				}
+
+			// Check if inner loop is finished (not just pixel counter)
+			if( iProcessInner == iProcessSize.iWidth )
+				{
+				// Reset inner counters
+				iSrcPos.iX -= iSrcIncrementInner.iX * iProcessInner;
+				iSrcPos.iY -= iSrcIncrementInner.iY * iProcessInner;
+
+				iDstPos.iX -= iDstIncrementInner * iProcessInner;
+				iProcessInner = 0;
+
+				// Add outer counters
+				iSrcPos += iSrcIncrementOuter;
+				iDstPos.iY += iDstIncrementOuter;
+				++iProcessOuter;
+				}
+			}
+		}
+	else if ( ( incSrcY >= 0) && (incSrcX < 0 ) )
+		{
+		while( iProcessOuter < iProcessSize.iHeight && pixelCounter )
+			{
+			while( iProcessInner < iProcessSize.iWidth && pixelCounter )
+				{
+				srcBitmap.SetPos( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+				dstBitmap.SetPos( iDstPos );
+
+				// gather pixel data with step:
+				// 00   10 
+				// /\   V
+				// 01 < 11
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY10);
+				srcBitmap.IncYPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY11);
+				srcBitmap.DecXPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY01);
+				srcBitmap.DecYPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY00);
+				//return to starting position
+				srcBitmap.IncXPos();
+
+				// interpolate color using gathered pixel-data
+				ProcessColorData(pixelData, iSrcPos);
+
+				// put color in destination package
+				color = RgbToInt(pixelData.col);
+				dstBitmap.SetPixel(color);
+
+				// Add inner counters
+				iSrcPos += iSrcIncrementInner;
+				iDstPos.iX += iDstIncrementInner;
+				++iProcessInner;
+				--pixelCounter;
+				}
+
+			// Check if inner loop is finished (not just pixel counter)
+			if( iProcessInner == iProcessSize.iWidth )
+				{
+				// Reset inner counters
+				iSrcPos.iX -= iSrcIncrementInner.iX * iProcessInner;
+				iSrcPos.iY -= iSrcIncrementInner.iY * iProcessInner;
+
+				iDstPos.iX -= iDstIncrementInner * iProcessInner;
+				iProcessInner = 0;
+
+				// Add outer counters
+				iSrcPos += iSrcIncrementOuter;
+				iDstPos.iY += iDstIncrementOuter;
+				++iProcessOuter;
+				}
+
+			}
+		}
+	else if ( ( incSrcY < 0) && (incSrcX >= 0 ) )
+		{
+		while( iProcessOuter < iProcessSize.iHeight && pixelCounter )
+			{
+			while( iProcessInner < iProcessSize.iWidth && pixelCounter )
+				{
+				srcBitmap.SetPos( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+				dstBitmap.SetPos( iDstPos );
+
+				// gather pixel data with step:
+				// 00 > 10 
+				// /\   V
+				// 01   11
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY01);
+				srcBitmap.DecYPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY00);
+				srcBitmap.IncXPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY10);
+				srcBitmap.IncYPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY11);
+				//return to starting position
+				srcBitmap.DecXPos();
+
+				// interpolate color using gathered pixel-data
+				ProcessColorData(pixelData, iSrcPos);
+
+				// put color in destination package
+				color = RgbToInt(pixelData.col);
+				dstBitmap.SetPixel(color);
+
+				// Add inner counters
+				iSrcPos += iSrcIncrementInner;
+				iDstPos.iX += iDstIncrementInner;
+				++iProcessInner;
+				--pixelCounter;
+				}
+
+			// Check if inner loop is finished (not just pixel counter)
+			if( iProcessInner == iProcessSize.iWidth )
+				{
+				// Reset inner counters
+				iSrcPos.iX -= iSrcIncrementInner.iX * iProcessInner;
+				iSrcPos.iY -= iSrcIncrementInner.iY * iProcessInner;
+
+				iDstPos.iX -= iDstIncrementInner * iProcessInner;
+				iProcessInner = 0;
+
+				// Add outer counters
+				iSrcPos += iSrcIncrementOuter;
+				iDstPos.iY += iDstIncrementOuter;
+				++iProcessOuter;
+				}
+			}
+		}
+	else
+		{
+		while( iProcessOuter < iProcessSize.iHeight && pixelCounter )
+			{
+			while( iProcessInner < iProcessSize.iWidth && pixelCounter )
+				{
+				srcBitmap.SetPos( TPoint( iSrcPos.iX >> KStepPrecision, iSrcPos.iY >> KStepPrecision ) );
+				dstBitmap.SetPos( iDstPos );
+
+				// gather pixel data with step:
+				// 00 > 10 
+				// /\   
+				// 01 < 11
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY11);
+				srcBitmap.DecXPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY01);
+				srcBitmap.DecYPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY00);
+				srcBitmap.IncXPos();
+				IntToRgb(srcBitmap.GetPixel(),pixelData.colXY10);
+				//return to starting position
+				srcBitmap.IncYPos();
+
+				// interpolate color using gathered pixel-data
+				ProcessColorData(pixelData, iSrcPos);
+
+				// put color in destination package
+				color = RgbToInt(pixelData.col);
+				dstBitmap.SetPixel(color);
+
+				// Add inner counters
+				iSrcPos += iSrcIncrementInner;
+				iDstPos.iX += iDstIncrementInner;
+				++iProcessInner;
+				--pixelCounter;
+				}
+
+			// Check if inner loop is finished (not just pixel counter)
+			if( iProcessInner == iProcessSize.iWidth )
+				{
+				// Reset inner counters
+				iSrcPos.iX -= iSrcIncrementInner.iX * iProcessInner;
+				iSrcPos.iY -= iSrcIncrementInner.iY * iProcessInner;
+
+				iDstPos.iX -= iDstIncrementInner * iProcessInner;
+				iProcessInner = 0;
+
+				// Add outer counters
+				iSrcPos += iSrcIncrementOuter;
+				iDstPos.iY += iDstIncrementOuter;
+				++iProcessOuter;
+				}
+
+			}
+		}
+
+	// Release heap lock
+	dstBitmap.End();
+	srcBitmap.End();
+
+	// ETrue if more processing is still needed
+	return ( iProcessOuter < iProcessSize.iHeight );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::SelfComplete
+// -----------------------------------------------------------------------------
+void CIHLScaler::SelfComplete()
+	{
+	SetActive();
+	iStatus = KRequestPending;
+	TRequestStatus* status = &iStatus;
+	User::RequestComplete( status, KErrNone );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::RequestComplete
+// -----------------------------------------------------------------------------
+void CIHLScaler::RequestComplete( TInt aReason )
+	{
+	ASSERT( iScalerStatus );
+	User::RequestComplete( iScalerStatus, aReason );
+	}
+
+// -----------------------------------------------------------------------------
+// CIHLScaler::InitCodePath
+// -----------------------------------------------------------------------------
+void CIHLScaler::InitCodePath(const TSize& aSrcSize, const TDisplayMode &aSrcMode, const TSize& aDstSize, const TDisplayMode &aDstMode, TBool aOnlyScaling)
+	{
+	// Choose the correct processing code path	
+	ProcessingFunc = &CIHLScaler::ProcessNearestNeighbour;
+	    	
+	if( iOptions & MIHLImageViewer::EOptionUseBilinearInterpolation )
+		{
+		// TODO: optimize bilinear scaling
+		IHL_DEBUG("CIHLScaler::InitCodePath: slow bilinear");
+		ProcessingFunc = CIHLScaler::ProcessBilinear;
+		iScanlinesPerRound = KProcessPixelsPerStep / iProcessSize.iWidth;
+		}
+	else if(aSrcMode == EColor64K && aSrcMode==aDstMode && !iSrcBitmap->IsCompressedInRAM() && !iDstBitmap->IsCompressedInRAM())
+		{
+		// 16 bit non-compressed bitmaps
+		
+		// Calculate how many pixels the scanline is actually wide in the memory
+		iSrcPitchInPixels = CFbsBitmap::ScanLineLength( aSrcSize.iWidth, aSrcMode ) >> 1;
+		iDstPitchInPixels = CFbsBitmap::ScanLineLength( aDstSize.iWidth, aDstMode ) >> 1;
+		
+		// How many pixels to move destination pointer at the end of each scanline to get to next pixel
+		iDstResidualPixels = iDstIncrementOuter*iDstPitchInPixels - iDstIncrementInner*iProcessSize.iWidth;
+
+		// Buffer offset to the first destination pixel
+		iDstStartOffset = iDstPos.iY * iDstPitchInPixels + iDstPos.iX;
+
+		if(!aOnlyScaling)
+			{
+			IHL_DEBUG("CIHLScaler::InitCodePath: fast 64K scale&rotate");
+			ProcessingFunc = &CIHLScaler::ProcessNearestNeighbour64K;
+			
+			// Calculate how often the process should allow AS to run
+			iScanlinesPerRound = KBytesPerStepFastPath / (iProcessSize.iWidth<<1);
+			}
+		else
+			{
+			IHL_DEBUG("CIHLScaler::InitCodePath: fast 64K scale only");
+			ProcessingFunc = &CIHLScaler::ProcessNearestNeighbour64KScaleOnly;
+			
+			// Calculate how often the process should allow AS to run
+			iScanlinesPerRound = KBytesPerStepFastPathScaleOnly / (iProcessSize.iWidth<<1);
+			}
+		}
+		
+	else if((aSrcMode == EColor16MU || aSrcMode == EColor16MA) && aSrcMode==aDstMode && !iSrcBitmap->IsCompressedInRAM() && !iDstBitmap->IsCompressedInRAM())
+		{
+		// 32 bit non-compressed bitmaps
+		
+		// Calculate how many pixels the scanline is actually wide in the memory
+		iSrcPitchInPixels = CFbsBitmap::ScanLineLength( aSrcSize.iWidth, aSrcMode ) >> 2;
+		iDstPitchInPixels = CFbsBitmap::ScanLineLength( aDstSize.iWidth, aDstMode ) >> 2;
+		
+		// How many pixels to move destination pointer at the end of each scanline to get to next pixel
+		iDstResidualPixels = iDstIncrementOuter*iDstPitchInPixels - iDstIncrementInner*iProcessSize.iWidth;
+
+		// Buffer offset to the first destination pixel
+		iDstStartOffset = iDstPos.iY * iDstPitchInPixels + iDstPos.iX;
+
+		if(!aOnlyScaling)
+			{
+			IHL_DEBUG("CIHLScaler::InitCodePath: fast 16MX scale&rotate");
+			ProcessingFunc = &CIHLScaler::ProcessNearestNeighbour16MX;
+			
+			// Calculate how often the process should allow AS to run
+			iScanlinesPerRound = KBytesPerStepFastPath / (iProcessSize.iWidth<<2);
+			}
+		else
+			{
+			IHL_DEBUG("CIHLScaler::InitCodePath: fast 16MX scale only");
+			ProcessingFunc = &CIHLScaler::ProcessNearestNeighbour16MXScaleOnly;
+			
+			// Calculate how often the process should allow AS to run
+			iScanlinesPerRound = KBytesPerStepFastPathScaleOnly / (iProcessSize.iWidth<<2);
+			}
+		}
+	else
+		{
+		IHL_DEBUG("CIHLScaler::InitCodePath: slow nearest neighbor");
+		iScanlinesPerRound = KProcessPixelsPerStep / iProcessSize.iWidth;
+		}
+		
+	IHL_DEBUG2(KIHLDebug, "CIHLScaler::InitCodePath: scanlines per round = %d", iScanlinesPerRound);
+	}
+
+//  End of File