imagehandlinglib/Src/CIHLScaler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 15:18:05 +0200
changeset 0 2014ca87e772
child 9 dea39715fc05
permissions -rw-r--r--
Revision: 201004

/*
* 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