diff -r 000000000000 -r 2014ca87e772 imagehandlinglib/Src/CIHLScaler.cpp --- /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 +#include +#include + +// 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( 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( 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