diff -r 000000000000 -r 40261b775718 mmplugins/imagingplugins/bitmaptransform/src/refplugin/BitmapScalerPlugin.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmplugins/imagingplugins/bitmaptransform/src/refplugin/BitmapScalerPlugin.cpp Tue Feb 02 01:56:55 2010 +0200 @@ -0,0 +1,1851 @@ +// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "BitmapTransformsPlugin.h" + +#include "BitmapConverter.h" +#include +#include + +const TInt KFixedPointBits = 4; +const TInt KFixedPointScale = (1 << KFixedPointBits); +const TInt KLinesPerCall = 10; +const TInt KBitsPerByte = 8; + +/* +*The function NewL constructs a CBitmapScalerPlugin +* +*@return CBitmapScalerPlugin* +*/ +MBitmapScalerPlugin* CBitmapScalerPlugin::NewL() + { + CBitmapScalerPlugin* self = new(ELeave) CBitmapScalerPlugin(); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +/* +* +*CBitmapScalerPlugin() +*Constructor for this class. Adds itself to CActiveScheduler. +*/ +CBitmapScalerPlugin::CBitmapScalerPlugin() + : CActive(CActive::EPriorityIdle), iOrigDes(NULL, 0), iDestDes(NULL, 0), + iQualityLevel(CBitmapScaler::EMaximumQuality) + { + CActiveScheduler::Add(this); + } + +/* +* +*ConstructL() +*Performs second phase of construction. +*/ +void CBitmapScalerPlugin::ConstructL() + { + iScanlineBitmap = new(ELeave) CFbsBitmap; + + SetPostProcessingEnabled(ETrue); + ASSERT( GetCurrentState() == EInactiveState ); + } + + +/* +* +*~CBitmapScalerPlugin +*This is the destructor for the CBitmapScalerPlugin +*and is resposible for deallocating all resources +*allocated by the CBitmapScalerPlugin. +*/ +CBitmapScalerPlugin::~CBitmapScalerPlugin() + { + Cancel(); + Cleanup(); + + delete iScanlineBitmap; + + // should have been deleted by cleanup + ASSERT(iScaleBitmap==NULL || !(iFlags & EScaleBitmapIsOwned)); + ASSERT(iFilterIndexTable==NULL); + ASSERT(iFilterCoeffsTable==NULL); + ASSERT(iDevice==NULL); + ASSERT(iGc==NULL); + ASSERT(iPostProcessBitmap == NULL ); + ASSERT(iBitmapConverter == NULL ); + ASSERT( GetCurrentState() == EInactiveState ); + } + +/* +*This function performs deallocation of memory allocated by the class +*which is allocated on each scale as opposed to the entire liftime +*of the scaler object. +*/ +void CBitmapScalerPlugin::Cleanup() + { + delete[] iFilterIndexTable; + iFilterIndexTable = NULL; + delete[] iFilterCoeffsTable; + iFilterCoeffsTable = NULL; + + if (iOrigDes.Length() != 0) // Test is required to avoid PANIC 45 + { + User::Free( reinterpret_cast( const_cast( iOrigDes.Ptr() ) ) ); + iOrigDes.SetLength(0); + } + + if (iDestDes.Length() != 0) // Test is required to avoid PANIC 45 + { + User::Free( reinterpret_cast( const_cast( iDestDes.Ptr() ) ) ); + iDestDes.SetLength(0); + } + + if (iFlags & ECreatedTempBitmap) + { + delete iTempBitmap; + iTempBitmap = NULL; + iFlags &= ~ECreatedTempBitmap; + } + + // Reset the EIsSourceBitmapResized flag here + if (iFlags & EIsSourceBitmapResized) + { + iFlags &= ~EIsSourceBitmapResized; + ASSERT((iFlags & EIsSourceBitmapResized) == 0); + } + + // Only delete the scale bitmap if we created it. + if (iFlags & EScaleBitmapIsOwned) + { + delete iScaleBitmap; + iFlags &= ~EScaleBitmapIsOwned; + } + iScaleBitmap = NULL; + + delete iGc; + iGc = NULL; + delete iDevice; + iDevice = NULL; + iDestBmpPtr = NULL; // we don't own it + delete iPostProcessBitmap; + iPostProcessBitmap = NULL; + delete iBitmapConverter; + iBitmapConverter = NULL; + SetCurrentState( EInactiveState ); + } + +/* +* Allocates some memory and assigns a pointer to it. +* @param aPtr Pointer to the allocated memory +* @param aAllocSize The size in bytes of the memory to allocate +* @pre aAllocSize >= 0 +* @pre InVariant +*/ +TInt CBitmapScalerPlugin::AllocPtr( TPtr8& aPtr, TInt aAllocSize ) + { + //[ preconditions ] + ASSERT(aAllocSize >= 0 ); + ASSERT(InVariant()); + + TInt rc = KErrNone; + + // User::Alloc() returns NULL if no memory + TUint8* addr = static_cast(User::Alloc(aAllocSize + sizeof(TUint32) )); + if (addr) + { + // coverity [memory_leak] + addr = reinterpret_cast( _ALIGN_UP(TLinAddr(addr), sizeof(TUint32)) ); + aPtr.Set(addr, aAllocSize, aAllocSize); + } + else + { + rc = KErrNoMemory; + } + + return rc; + } + +// MBitmapScalerPlugin::Cancel() calls CActive::Cancel() +void CBitmapScalerPlugin::Cancel() + { + CActive::Cancel(); + } + +/* +* +* ScaleRequestPostcondition +* @return 'ETrue' when post conditions for scale request are +* correct +* +*/ +TBool CBitmapScalerPlugin::ScaleRequestPostcondition() const + { + TBool result = ETrue; + + if (QualityAlgorithm() != CBitmapScaler::EMaximumQuality ) + { + if( ( iFinalOffset != iDestSize.iHeight ) || + ( iFilterIndexTable != NULL ) || + ( iFilterCoeffsTable != 0 ) ) + { + result = EFalse; + } + } + else + { + // Using the original (maximum quality) algorithm + if( ( iFlags & EVerticalScan) || + ( iFilterIndexTable == NULL ) || + ( iFinalOffset != iOrigSize.iHeight ) || + ( iFilterCoeffsTable == 0 ) ) + { + result = EFalse; + } + + if (( iDevice == NULL ) || + ( iScanlineBitmap->Handle() == 0 ) || + ( iCurOffset != 0 )) + { + result = EFalse; + } + } + + return result; + } + +/** + * + * Begins the bitmap re-scaling operation. + * + * The scaling factor is based on the relative value of the source bitmap + * size and the explicitly supplied size. The operation is asynchronous. + * When it is complete, successfully or otherwise, the + * TRequestStatus& is set, passing the state of the operation. + * + * @param "TRequestStatus* aRequestStatus" + * + * @param "CFbsBitmap& aBitmap" + * The bitmap to be re-scaled. This reference is also the + * target location for the re-scaled bitmap. + * @param "const TSize& aDestinationSize" + * The requested target size for the re-scaled bitmap. + * @param "TBool aMaintainAspectRatio = ETrue" + * ETrue - the aspect ratio is retained; + * this is the default. The same scaling factor is + * applied in both the horizontal and vertical + * directions. This is the smaller of the horizontal + * scaling factor and the vertical scaling factor. + * EFalse - the aspect ratio need not be + * retained. + * + */ +void CBitmapScalerPlugin::Scale(TRequestStatus* aRequestStatus, + CFbsBitmap& aBitmap, + const TSize& aDestinationSize, + TBool aMaintainAspectRatio) + { + //[ assert preconditions ] + // [ panic if aRequestStatus is NULL ] + __ASSERT_ALWAYS( (aRequestStatus != NULL), Panic( EBadArgumentScale ) ); + + //[ panic if the src has not been created] + __ASSERT_ALWAYS( (aBitmap.Handle() != 0), Panic( ENoSourceBitmap ) ); + + iScaleStatus = aRequestStatus; + *iScaleStatus = KRequestPending; + + if(aBitmap.ExtendedBitmapType()!=KNullUid) + { + RequestComplete(KErrNotSupported); + return; + } + + // Set up the sizes before they're lost + iOrigSize = aBitmap.SizeInPixels(); + iDestSize = aDestinationSize; + + // Dithering is not required if the tgt's display mode + // is none of EGray256, EColor16M, EColor16MU, EColor16MA + TBool dither = ETrue; + iTgtDisplayMode = aBitmap.DisplayMode(); + if ((iTgtDisplayMode == EGray256) || + (iTgtDisplayMode == EColor16M) || + (iTgtDisplayMode == EColor16MA)|| + (iTgtDisplayMode == EColor16MU) ) + { + dither = EFalse; + } + + // Create a temporary bitmap if we're not using the low memory algorithm + // OR if we're using the low memory algorithm AND dithering + TInt leaveErr = KErrNone; + if (!UseLowMemoryAlgorithm() || + (UseLowMemoryAlgorithm() && dither) ) + { + if (iFlags & ECreatedTempBitmap) + { + delete iTempBitmap; + iTempBitmap = NULL; + iFlags &= ~ECreatedTempBitmap; + } + + iTempBitmap = new CFbsBitmap; + + if (iTempBitmap != NULL) + { + // iTempBitmap has been created so flag that it needs to be deleted + iFlags |= ECreatedTempBitmap; + leaveErr = iTempBitmap->Create(aDestinationSize, iTgtDisplayMode); + } + else + { + // No memory to new the temp bitmap + leaveErr = KErrNoMemory; + } + } + + if (leaveErr == KErrNoMemory || + (UseLowMemoryAlgorithm() && !dither) ) + { + // Either failed to create the tempbitmap OR + // we explicitly want to use the low-memory algorithm. + // Either way use the source directly in the manner of the low-memory algorithm. + // Check if we need to delete the tempbitmap + if (iFlags & ECreatedTempBitmap) + { + delete iTempBitmap; + iTempBitmap = NULL; + iFlags &= ~ECreatedTempBitmap; + } + iTempBitmap = &aBitmap; + iFlags |= EIsSourceBitmapResized; + } + else if (leaveErr != KErrNone) + { + iScaleStatus = aRequestStatus; + *iScaleStatus = KRequestPending; + ProcessError(leaveErr); + return; + } + + Scale(aRequestStatus, aBitmap, *iTempBitmap, aMaintainAspectRatio); + } + +/* + * + * Begins the bitmap re-scaling operation. + * + * The scaling factor is based on the relative sizes of the source + * and target bitmaps. The operation is asynchronous. When it is + * complete, successfully or otherwise, the TRequestStatus & + * aStatus is set, passing the state of the operation. + * + * @param TRequestStatus* aRequestStatus + * + * @param "CFbsBitmap& aSrcBitmap" + * The bitmap to be re-scaled. + * @param "CFbsBitmap& aTgtBitmap" + * The target location for the re-scaled bitmap. + * @param "TBool aMaintainAspectRatio = ETrue" + * ETrue - the aspect ratio is retained; + * this is the default. The same scaling factor is + * applied in both the horizontal and vertical + * directions. This is the smaller of the horizontal + * scaling factor and the vertical scaling factor. + * EFalse - the aspect ratio need not be + * retained. + * + * + */ +void CBitmapScalerPlugin::Scale(TRequestStatus* aRequestStatus, + CFbsBitmap& aSrcBitmap, + CFbsBitmap& aTgtBitmap, + TBool aMaintainAspectRatio) + { + // [ precondition checking on arguments ] + // [ panic if aRequestStatus is NULL ] + __ASSERT_ALWAYS( (aRequestStatus != NULL), Panic( EBadArgumentScale ) ); + + //[ panic if the src has not been created] + __ASSERT_ALWAYS( (aSrcBitmap.Handle() != 0), Panic( ENoSourceBitmap ) ); + + //[ panic if the tgt has not been created] + __ASSERT_ALWAYS( (aTgtBitmap.Handle() != 0), Panic( ENoDestinationBitmap ) ); + + iScaleStatus = aRequestStatus; + *iScaleStatus = KRequestPending; + + //[ current state must be inactive ] + if( GetCurrentState() != EInactiveState ) + { + ProcessError( KErrGeneral ); + return; + } + + ASSERT(iDevice==NULL); + + if(aSrcBitmap.ExtendedBitmapType()!=KNullUid || aTgtBitmap.ExtendedBitmapType()!=KNullUid) + { + RequestComplete(KErrNotSupported); + return; + } + + iSrcBitmap = &aSrcBitmap; + iTgtBitmap = &aTgtBitmap; + + if (!(iFlags & EIsSourceBitmapResized)) + { + // As we're not scaling directly to the source bitmap + // we can determine the scaling sizes here (otherwise + // we'd overwrite the values that we got prior to setting + // the tgtBitmap to the srcBitmap!!) + iOrigSize = iSrcBitmap->SizeInPixels(); + iDestSize = iTgtBitmap->SizeInPixels(); + } + + TInt err = KErrNone; + + // Target's colour depth + iTgtDisplayMode = iTgtBitmap->DisplayMode(); + + // Check the aspect ratio and adjust the resolution if needed + if (aMaintainAspectRatio) + { + //[ update destination height to preserve aspect ratio ] + HeightAndWidthPreservingAspectRatio( iDestSize, iOrigSize ); + } + + iIntermediateSize.iWidth = iDestSize.iWidth; + if ( QualityAlgorithm() != CBitmapScaler::EMaximumQuality ) + { + // Using one of the lower quality algorithms so use the destination size + // that we are scaling to. + iIntermediateSize.iHeight = iDestSize.iHeight; + } + else + { + // Using the original algorithm (which firstly scales horizontally to + // an intermediate bitmap), so the intermediate size requires the original + // height unchanged. + iIntermediateSize.iHeight = iOrigSize.iHeight; + } + + // if either of the destination dimension is zero then + // complete with KErrNone + if ((iDestSize.iWidth == 0) || + (iDestSize.iHeight == 0)) + { + ProcessError(KErrNone); + return; + } + + // if either of the source dimension is zero then send back an error + if ((iOrigSize.iWidth == 0) || + (iOrigSize.iHeight == 0)) + { + ProcessError( KErrArgument ); + return; + } + + // Scale to bitmap of display mode EColor16M except for + // target bitmaps of EGray256, EColor16MU and EColor16MA + if ( (iTgtDisplayMode == EGray256) || (iTgtDisplayMode == EColor16MU) + || (iTgtDisplayMode == EColor16MA)) + { + iLocalDisplayMode = iTgtDisplayMode; + } + else + { + iLocalDisplayMode = EColor16M; + } + + if (iTgtDisplayMode != EColor16M && iTgtDisplayMode != EColor16MA && iTgtDisplayMode != EColor16MU) + { + // Create an intermediate bitmap to scale to for cases other than EColor16M + if (iScaleBitmap == NULL) + { + // Only want one instance of iScaleBitmap + iScaleBitmap = new CFbsBitmap; + iFlags |= EScaleBitmapIsOwned; // flag that we're the owner of the scale bitmap + if (iScaleBitmap == NULL) + { + err = KErrNoMemory; + } + } + if( err != KErrNone ) + { + ProcessError( err ); + return; + } + + err = iScaleBitmap->Create(iIntermediateSize, iLocalDisplayMode); + + if( err != KErrNone ) + { + ProcessError( err ); + return; + } + + iDestBmpPtr = iScaleBitmap; + } + else + { + // For EColor16M scale directly to TgtBitmap + iScaleBitmap = iTgtBitmap; + + // Downscaling one bitmap to another requires + // the scale bitmap to be resized to ensure that + // the correct amount of data is read from the source. + if (!(iFlags & EIsSourceBitmapResized)) + { + err = iScaleBitmap->Resize(iIntermediateSize); + if (err != KErrNone) + { + ProcessError(err); + return; + } + } + } + + iComponentsPerPixel = TDisplayModeUtils::NumDisplayModeBitsPerPixel(iLocalDisplayMode) / KBitsPerByte; + if (iComponentsPerPixel == 0) + { + iComponentsPerPixel = 1; + } + + iSrcY = 0; + if (iDestSize.iHeight < iOrigSize.iHeight) + { + iCurY = 0; + } + else + { + iCurY = (iOrigSize.iHeight << KFixedPointScale); + } + + if (QualityAlgorithm() == CBitmapScaler::EMaximumQuality) + { + // Try to create and size the scanline bitmap here instead of in the ConstructL + if ((err = iScanlineBitmap->Create(TSize(1, iDestSize.iHeight), iLocalDisplayMode)) != KErrNone) + { + ProcessError(err); + return; + } + } + + // Upscaling so ensure we're writing to a suitably + // large target + TInt newWidth = iTgtBitmap->SizeInPixels().iWidth; + TInt newHeight = iTgtBitmap->SizeInPixels().iHeight; + + TBool needsResize = EFalse; + if(iDestSize.iWidth > iOrigSize.iWidth) + { + newWidth = iDestSize.iWidth; + needsResize = ETrue; + } + + if(iDestSize.iHeight > iOrigSize.iHeight) + { + newHeight = iDestSize.iHeight; + needsResize = ETrue; + } + + if(needsResize) + { + err = iTgtBitmap->Resize(TSize(newWidth, newHeight)); + + if( err != KErrNone ) + { + ProcessError(err); + return; + } + } + + if (QualityAlgorithm() == CBitmapScaler::EMaximumQuality) + { + // [Create device for normal condition where target bitmap is the + // destination] + TRAP(err, iDevice = CFbsBitmapDevice::NewL(iTgtBitmap)); + if( err != KErrNone ) + { + ProcessError(KErrNoMemory); + return; + } + } + + iDestBmpPtr = iTgtBitmap; + + SetProcessingNeeded( EFalse ); + + //[ Is Post processing required ] + if( ( iTgtDisplayMode != EColor16M) && + ( iTgtDisplayMode != EColor16MU) && + ( iTgtDisplayMode != EColor16MA) && + ( iTgtDisplayMode != EGray256) && + (! IsPostProcessingDisabled() ) || + ((iTgtDisplayMode == EGray256) && (QualityAlgorithm() != CBitmapScaler::EMaximumQuality) && + (iLocalDisplayMode != EGray256)) + ) + { + SetProcessingNeeded( ETrue ); + + //[ create a bitmap converter to colour quantize the image + // to the number of colours available in the output + // note CFbsBitmap has a private constructor whence new is not + //used ] + TRAP(err, iBitmapConverter = CBitmapConverter::NewL()); + if(err != KErrNone ) + { + ProcessError(err); + return; + } + + //[ create a post processing bitmap] + iPostProcessBitmap = new CFbsBitmap; + if( iPostProcessBitmap == NULL ) + { + err = KErrNoMemory; + ProcessError(err); + return; + } + + //[ size the post processing bitmap ] + if ((err = iPostProcessBitmap->Create(iDestSize, EColor16M )) != KErrNone) + { + ProcessError(err); + return; + } + + // Create device with post processing bitmap as the target + delete iDevice; + iDevice = NULL; + iDestBmpPtr = NULL; + if (QualityAlgorithm() == CBitmapScaler::EMaximumQuality) + { + TRAP(err, iDevice = CFbsBitmapDevice::NewL(iPostProcessBitmap)); + if( err != KErrNone ) + { + ProcessError(err); + return; + } + } + iDestBmpPtr = iPostProcessBitmap; + } + + iCurOffset = 0; + iCurOffsetUp = iDestSize.iHeight; + if (QualityAlgorithm() != CBitmapScaler::EMaximumQuality) + { + // Using one of the lower quality algorithms + iFinalOffset = iDestSize.iHeight; + if (iTgtBitmap->SizeInPixels() != iDestSize && iTgtBitmap->Handle() != iSrcBitmap->Handle()) + { + err = iTgtBitmap->Resize(iDestSize); + if( err != KErrNone ) + { + ProcessError(err); + return; + } + } + } + else + { + // We're using the original scaling algorithm + // Set Horizontal Scan flag + iFlags &= ~EVerticalScan; + iFinalOffset = iOrigSize.iHeight; + iCurLineSize = iOrigSize.iWidth; + + // Set up filter tables + err = FilterTables(); + if( err != KErrNone ) + { + ProcessError( err ); + return; + } + } + + // Allocate the buffers + TInt allocErr = AllocPtr( iOrigDes, iOrigSize.iWidth * iComponentsPerPixel ); + if (allocErr == KErrNone) + { + allocErr = AllocPtr( iDestDes, iDestSize.iWidth * iComponentsPerPixel ); + } + + if( allocErr != KErrNone ) + { + ProcessError(allocErr); + return; + } + + if (QualityAlgorithm() == CBitmapScaler::EMaximumQuality) + { + err = iDevice->CreateContext(iGc); + } + + if (err != KErrNone) + { + ProcessError(err); + return; + } + + //[ set state to scaling ] + SetCurrentState(EScalingState); + + //[ assert post conditions of scale request ] + ASSERT( ScaleRequestPostcondition() ); + + // Start the active object + SelfComplete(KErrNone); + } + +/* +* +* FilterTables +* Initialise the filter tables +* +*/ +TInt CBitmapScalerPlugin::FilterTables() + { + TInt result = KErrNone; + + ASSERT(iFilterCoeffsTable==NULL); + ASSERT(iFilterIndexTable==NULL); + + if (iDestSize.iWidth > iDestSize.iHeight) + { + iFilterIndexNum = 2 * iDestSize.iWidth; + } + else + { + iFilterIndexNum = 2 * iDestSize.iHeight; + } + + if (iDestSize.iWidth > iOrigSize.iWidth) + { + iFilterCoeffsNum = iDestSize.iWidth; + } + else + { + iFilterCoeffsNum = 2 * iOrigSize.iWidth; + } + + TInt filterCoeffsNumVert; + + if (iDestSize.iHeight > iOrigSize.iHeight) + { + filterCoeffsNumVert = iDestSize.iHeight; + } + else + { + filterCoeffsNumVert = 2 * iOrigSize.iHeight; + } + + if (filterCoeffsNumVert > iFilterCoeffsNum) + { + iFilterCoeffsNum = filterCoeffsNumVert; + } + + //[ allocate a filter index table and check for memory alloc failure] + iFilterIndexTable = new TInt[iFilterIndexNum]; + if( iFilterIndexTable == NULL ) + { + return KErrNoMemory; + } + + //[ alloc filter coefficient table and check for memory alloc failure] + iFilterCoeffsTable = new TInt[iFilterCoeffsNum]; + if( iFilterCoeffsTable == NULL ) + { + return KErrNoMemory; + } + + // Calculate filter tables for horizontal scaling + iFilterIndexTablePtr = iFilterIndexTable; + iFilterCoeffsTablePtr = iFilterCoeffsTable; + result = CalcFilterTables(iOrigSize.iWidth, iDestSize.iWidth, iFilterIndexTablePtr, iFilterCoeffsTablePtr); + + return result; + } + +/* +* +* DoScale +* This function does the scaling of a bitmap in chunks +* +*/ +void CBitmapScalerPlugin::DoScale() + { + //[assert the invariant ] + __ASSERT_DEBUG( InVariant(), Panic( EBadInvariant ) ); + + switch( iCurrentState ) + { + case EScalingState: + { + //[ we are scaling the image] + if (QualityAlgorithm() != CBitmapScaler::EMaximumQuality) + { + FastScale(); + } + else + { + Scale(); + } + break; + } + case EStartPostProcessState: + { + //[ we are setting up post processing] + StartPostProcessing(); + break; + } + case ECleanUpState: + { + //[ cleaning up ] + ScaleCleanUp(); + break; + } + default: + { + //[we can never get here + // because the invariant has been asserted + //assert the invariant ] + __ASSERT_DEBUG( InVariant(), Panic( EBadInvariant ) ); + } + } + + //[assert the invariant ] + __ASSERT_DEBUG( InVariant(), Panic( EBadInvariant ) ); + } + +/** +* +* Scale +* This method does the scaling +* @pre current state is ScalingState +*/ +void CBitmapScalerPlugin::Scale() + { +#if defined (__SCALING_PROFILING) + RDebug::ProfileStart(7); +#endif // __SCALING_PROFILING + + //[ assert precondition ] + ASSERT( GetCurrentState() == EScalingState ); + //[ assert invariant ] + ASSERT( InVariant() ); + + TInt linesLeftPerCall = KLinesPerCall; + + while (linesLeftPerCall>0 && iCurOffsetGetScanLine(iOrigDes, TPoint(0, iCurOffset), iOrigSize.iWidth, iLocalDisplayMode); + #if defined (__SCALING_PROFILING) + RDebug::ProfileEnd(8); + #endif // __SCALING_PROFILING + } + else + { + #if defined (__SCALING_PROFILING) + RDebug::ProfileStart(11); + #endif // __SCALING_PROFILING + // Get the vertical scan line... + iScaleBitmap->GetVerticalScanLine(iOrigDes, iCurOffset, iLocalDisplayMode); + #if defined (__SCALING_PROFILING) + RDebug::ProfileEnd(11); + #endif // __SCALING_PROFILING + } + + if (!(iFlags & EVerticalScan)) + { + // Scale the scan line horizontally + for (TInt i=0; i < iComponentsPerPixel; i++) + { + ScaleLine(iOrigDes, iDestDes, i, iOrigSize.iWidth, iDestSize.iWidth, iFilterIndexTable, iFilterCoeffsTable); + } + } + else + { + // + // Scale vertically + for (TInt i=0; i < iComponentsPerPixel; i++) + { + ScaleLine(iOrigDes, iDestDes, i, iOrigSize.iHeight, iDestSize.iHeight, iFilterIndexTable, iFilterCoeffsTable); + } + } + +#if defined (__SCALING_PROFILING) + RDebug::ProfileStart(10); +#endif //__SCALING_PROFILING + + if (!(iFlags & EVerticalScan)) + { + // Set the scaled horizontal line back into the bitmap + iScaleBitmap->SetScanLine(iDestDes, iCurOffset); + } + else + { + // Write to output bitmap + // Lock & unlock the heap to prevent the Font & Bitmap server moving the bitmap around + // Now writing to iScaleBitmap + if (iDestBmpPtr->DisplayMode()==EColor16MA && iLocalDisplayMode==EColor16MA) + { + TUint8* dataAddress = reinterpret_cast( iDestBmpPtr->DataAddress()); + dataAddress +=iCurOffset*iComponentsPerPixel; + const TInt scanLineLength = iDestBmpPtr->ScanLineLength(iDestBmpPtr->SizeInPixels().iWidth, iDestBmpPtr->DisplayMode() ); + + const TInt limit = iDestSize.iHeight * iComponentsPerPixel; + const TUint8* ptr = iDestDes.Ptr(); + for (TInt y = 0; y < limit; y += iComponentsPerPixel, dataAddress += scanLineLength) + { + CopyPixel(dataAddress,ptr); + ptr += iComponentsPerPixel; + } + } + else + { + TUint8* dataAddress = reinterpret_cast( iScanlineBitmap->DataAddress()); + ASSERT( iScanlineBitmap->SizeInPixels().iWidth==1 ); // that must be a "column" + const TInt scanLineLength = iScanlineBitmap->ScanLineLength(1, iScanlineBitmap->DisplayMode() ); + + const TInt limit = iDestSize.iHeight * iComponentsPerPixel; + const TUint8* ptr = iDestDes.Ptr(); + for (TInt y = 0; y < limit; y += iComponentsPerPixel, dataAddress += scanLineLength) + { + CopyPixel(dataAddress, ptr); + ptr += iComponentsPerPixel; + } + + iGc->BitBlt(TPoint(iCurOffset, 0), iScanlineBitmap); + } + } + +#if defined (__SCALING_PROFILING) + RDebug::ProfileEnd(10); +#endif //__SCALING_PROFILING + + linesLeftPerCall--; + iCurOffset++; + } + + if ( IsScalingComplete() && !(iFlags & EVerticalScan)) + { + iCurLineSize = iOrigSize.iHeight; + iCurOffset = 0; + iFinalOffset = iDestSize.iWidth; + iFlags |= EVerticalScan; + // Finished the horizontal scaling so now need to setup descriptors + // for the intermediate size as we're now longer using an intermediate + // bitmap. + iOrigSize = iIntermediateSize; + + User::Free( reinterpret_cast( const_cast( iOrigDes.Ptr() ) ) ); + iOrigDes.SetLength(0); + + TInt allocErr = AllocPtr(iOrigDes, iOrigSize.iHeight * iComponentsPerPixel); + if( allocErr != KErrNone ) + { + ProcessError( allocErr ); + return; + } + + User::Free( reinterpret_cast( const_cast( iDestDes.Ptr() ) ) ); + iDestDes.SetLength(0); + + allocErr = AllocPtr(iDestDes, iDestSize.iHeight * iComponentsPerPixel); + if( allocErr != KErrNone ) + { + ProcessError( allocErr ); + return; + } + iFilterIndexTablePtr = iFilterIndexTable; + iFilterCoeffsTablePtr = iFilterCoeffsTable; + TInt err = CalcFilterTables(iOrigSize.iHeight, iDestSize.iHeight, iFilterIndexTablePtr, iFilterCoeffsTablePtr); + if( err != KErrNone ) + { + ProcessError(err); + return; + } + } + else if ( IsScalingComplete() ) + { + // Finished the vertical scaling so, as the tgtbitmap has been scaled in place (ie + // to the intermediate size) we need to resize it to the true destination size + TInt err = iTgtBitmap->Resize(iDestSize); + if( err != KErrNone ) + { + ProcessError(err); + return; + } + + if( IsPostProcessingNeeded() ) + { + //[ transition to start post processing state] + SetCurrentState( EStartPostProcessState ); + } + else + { + //[ transition to cleanup state ] + SetCurrentState( ECleanUpState ); + } + } + + // Start the active object + SelfComplete(KErrNone); + +#if defined (__SCALING_PROFILING) + RDebug::ProfileEnd(7); +#endif // __SCALING_PROFILING + + //[ assert invariant ] + ASSERT( InVariant() ); + } + +/* +* +* StartPostProcessing +* +*/ +void CBitmapScalerPlugin::StartPostProcessing() + { + ASSERT( InVariant() ); + ASSERT( GetCurrentState() == EStartPostProcessState ); + // set state to post processing in progress + // lauch async post process operation + SetCurrentState(ECleanUpState); + + iBitmapConverter->Convert( &iStatus, *iTgtBitmap, *iDestBmpPtr); + + SetActive(); + ASSERT( InVariant() ); + ASSERT( GetCurrentState() == ECleanUpState ); + } + +/* +* +* GetCurrentState +* @return CBitmapScalerPlugin::TScaleState The current state +* of the state machine +* +*/ +CBitmapScalerPlugin::TScaleState CBitmapScalerPlugin::GetCurrentState() const + { + return iCurrentState; + } + +/* +* +* SetCurrentState +* @param aState +* @pre aState <= CleanUpState +* @pre aState >= InactiveState +* @post Invariant +* +*/ +void CBitmapScalerPlugin::SetCurrentState( TScaleState aState ) + { + iCurrentState = aState; + } + +/* +* +* CleanUp +* +*/ +void CBitmapScalerPlugin::ScaleCleanUp() + { + TInt errStatus = iStatus.Int(); + + if( errStatus == KErrNone ) + { + if ((iFlags & ECreatedTempBitmap) != 0) + { + if (iTempBitmap) + { + TInt handle = 0; + if (QualityAlgorithm() != CBitmapScaler::EMaximumQuality) + { + // medium OR min qualitity algorithm + handle = iDestBmpPtr->Handle(); + } + else + { + // Normal (maximum quality) algorithm + handle = iTgtBitmap->Handle(); + } + errStatus = iSrcBitmap->Duplicate(handle); + } + } + } + + ProcessError( errStatus ); + } + +/* +* +* ProcessError +* @param aErrorStatus +* +*/ +void CBitmapScalerPlugin::ProcessError( TInt aErrorStatus ) + { + if( iPostProcessBitmap ) + { + iPostProcessBitmap->Reset(); + } + Cleanup(); + RequestComplete( aErrorStatus ); + } + +/* +* +* IsScalingComplete +* @return TBool scaling process has been completed +* +*/ +TBool CBitmapScalerPlugin::IsScalingComplete() const + { + TBool result = EFalse ; + if(iCurOffset == iFinalOffset || iCurOffsetUp == 0) + { + result = ETrue; + } + return result; + } + +/* +* +* RunL +* Handles an active object’s request completion event. +* The function is called by the active scheduler +* when a request completion event occurs. +*/ +void CBitmapScalerPlugin::RunL() + { + DoScale(); + } + +/* +* +* DoCancel +* Called by active object to prematurely terminate this bitmap scaling. +* +*/ +void CBitmapScalerPlugin::DoCancel() + { + ASSERT( InVariant()); + + if( iBitmapConverter ) + { + iBitmapConverter->Cancel(); + } + + Cleanup(); + ASSERT( GetCurrentState() == EInactiveState ); + ASSERT( InVariant()); + RequestComplete(KErrCancel); + } + +/* +* +* RequestComplete +* @param "TInt aReason" +* +*/ +void CBitmapScalerPlugin::RequestComplete(TInt aReason) + { + ASSERT(iScaleStatus); + TRequestStatus* status = iScaleStatus; + User::RequestComplete(status, aReason); + } + +/* +* +* SelfComplete +* This function activates the active object and +* signals completion of the current asynchronous operation +* +* @param "TInt aReason" +* +*/ +void CBitmapScalerPlugin::SelfComplete(TInt aReason) + { + SetActive(); + TRequestStatus* status = &iStatus; + User::RequestComplete(status, aReason); + } + +/* +* +* This function calculates the filter tables for scaling +* CalcFilterTables +* @param "TInt aOrigWidth" +* @param "TInt aDestWidth" +* @param "TInt*& aFilterCoeffsTablePtr" +* +*/ +TInt CBitmapScalerPlugin::CalcFilterTables(TInt aOrigWidth, TInt aDestWidth, TInt*& aFilterIndexTablePtr, TInt*& aFilterCoeffsTablePtr) + { + TInt inputWidth = aOrigWidth; + TInt scaledWidth = aDestWidth; + //this is added since to test indX<< KFixedPointBits for overflow + TInt fixedPointsVal = 1< KMaxTInt ) + { + return KErrOverflow; + } + //Checking OverFlow for ((indX << KFixedPointBits) * inputWidth) + if ( (MAKE_TUINT64(0,inputWidth) * MAKE_TUINT64(0,scaledWidth) * MAKE_TUINT64(0,fixedPointsVal)) > KMaxTInt ) + { + return KErrOverflow; + } + if ( scaledWidth > inputWidth ) + { + // Calculate tables for interpolation (scaling up) + for (TInt indX = 0; indX < scaledWidth; indX++) + { + const TInt indFilt = ((indX << KFixedPointBits) * inputWidth) / scaledWidth; + const TInt firstX = indFilt >> KFixedPointBits; + const TInt lastX = (firstX >= inputWidth - 1) ? firstX : firstX+1; + + *aFilterIndexTablePtr++ = firstX; + *aFilterIndexTablePtr++ = lastX; + *aFilterCoeffsTablePtr++ = indFilt - (firstX << KFixedPointBits); + } + } + else + { + // Calculate tables for decimation (scaling down) + const TInt ftNew = (inputWidth << KFixedPointBits)/scaledWidth; + TInt firstX = 0; + TInt lastX = 0; + for (TInt indX = 0; indX < scaledWidth; indX++) + { + firstX = lastX; + lastX = ((indX + 1) * inputWidth) / scaledWidth; + + *aFilterIndexTablePtr++ = firstX; + *aFilterIndexTablePtr++ = lastX; + + const TInt indOrig = ((indX << KFixedPointBits) * inputWidth) / scaledWidth; + const TInt indOrigInt = indOrig >> KFixedPointBits; + for (TInt indFilt = firstX; indFilt <= lastX; indFilt++) + { + TInt fixTap; + if(indFilt <= indOrigInt) + { + fixTap = ftNew - (indOrig - (indFilt << KFixedPointBits)); + } + else + { + fixTap = ftNew - ((indFilt << KFixedPointBits) - indOrig); + } + + if (fixTap < 0) + { + fixTap = 0; + } + *aFilterCoeffsTablePtr++ = (fixTap << KFixedPointBits) / ftNew; + } + } + } + return KErrNone; + } + + +/* +* This function calculates the filter tables for scaling +* ScaleLine +* @param "const TDesC8& aInDes" +* @param "TDes8& aOutDes" +* @param "TInt* aTableCoeffsPtr" +* +*/ +void CBitmapScalerPlugin::ScaleLine(const TDesC8& aInDes, + TDes8& aOutDes, + TInt aStartIndex, + TInt aInputWidth, + TInt aOutputWidth, + TInt* aTableIndexPtr, TInt* aTableCoeffsPtr) + { +#if defined (__SCALING_PROFILING) + RDebug::ProfileStart(9); +#endif // __SCALING_PROFILING + + // Initialize variables + TInt* tableIndexesPtr = aTableIndexPtr; + TInt* tableCoeffsPtr = aTableCoeffsPtr; + + if (aOutputWidth == aInputWidth) + { + aOutDes = aInDes; + } + else if (aOutputWidth > aInputWidth) + { + // Perform interpolation when scaling up + const TInt startIndex = aStartIndex; + const TUint8* inputDesPtr = aInDes.Ptr(); + TUint8* outputDesPtr = const_cast( aOutDes.Ptr() ); + + for (TInt indX = 0; indX < aOutputWidth; indX++, aStartIndex += iComponentsPerPixel) + { + TInt firstX = *tableIndexesPtr++; + TInt lastX = *tableIndexesPtr++; + TInt fixTap = *tableCoeffsPtr++; + + // Stay in bounds + if (firstX >= aInputWidth) + { + firstX = aInputWidth - 1; + } + // Factor for aggregated colour component + firstX *= iComponentsPerPixel; + firstX += startIndex; + + // Stay in bounds + if (lastX >= aInputWidth) + { + lastX = aInputWidth - 1; + } + lastX *= iComponentsPerPixel; + lastX += startIndex; + + // Filtering + outputDesPtr[aStartIndex] = + TUint8(((fixTap * TInt(inputDesPtr[lastX])+ + (KFixedPointScale - fixTap) * TInt(inputDesPtr[firstX])) + >> KFixedPointBits)); + } + } + else + { + // Perform decimation if scaling down requested + const TInt startIndex = aStartIndex; + const TInt widthLimit = iComponentsPerPixel * aInputWidth; + const TInt16 startWidthlimit = widthLimit - iComponentsPerPixel + startIndex; + const TUint8* inputDesPtr = aInDes.Ptr(); + TUint8* outputDesPtr = const_cast( aOutDes.Ptr() ); + + for (TInt indX = 0; indX < aOutputWidth; indX++, aStartIndex += iComponentsPerPixel) + { + TInt firstX = *tableIndexesPtr++; + firstX *= iComponentsPerPixel; + firstX += startIndex; + + TInt lastX = *tableIndexesPtr++; + if (lastX >= aInputWidth) + { + lastX = aInputWidth - 1; + } + lastX *= iComponentsPerPixel; + lastX += startIndex; + + // Filtering + TInt sum = 0; + TInt fixTapSum = 0; + for(TInt indFilt = firstX; indFilt <= lastX; indFilt += iComponentsPerPixel) + { + TInt fixTap = *tableCoeffsPtr++; + TInt indIn; + if (indFilt < 0) + { + indIn = startIndex; + } + else if (indFilt >= widthLimit ) + { + indIn = startWidthlimit; + } + else + { + indIn = indFilt; + } + + sum += fixTap * TInt(inputDesPtr[indIn]); + fixTapSum = TInt(fixTapSum + fixTap); + } + + // Let's not divide by zero + if (fixTapSum == 0) + { + outputDesPtr[aStartIndex] = TUint8(sum); + } + else + { + outputDesPtr[aStartIndex] = TUint8(sum/fixTapSum); + } + } + } +#if defined (__SCALING_PROFILING) + RDebug::ProfileEnd(9); +#endif // __SCALING_PROFILING + } + + +/* +* +* InVariant +* Used to determine the state of health of the scaler body +* +*/ +TBool CBitmapScalerPlugin::InVariant() const + { + return ((iCurrentState <= ECleanUpState ) && (iCurrentState >= EInactiveState )); + } + +/** +* +* SetProcessingNeeded +* @param aStatus ETrue EFalse +* +*/ +void CBitmapScalerPlugin::SetProcessingNeeded( TBool aStatus ) + { + if(aStatus) + { + iFlags |= EIsPostProcessingNeeded; + } + else + { + iFlags &= ~EIsPostProcessingNeeded; + } + } + +/** +* +* SetDisablePostProcessing +* @param aStatus ETrue EFalse +* +*/ +void CBitmapScalerPlugin::SetDisablePostProcessing( TBool aIsDisabled ) + { + if(aIsDisabled) + { + iFlags |= EDisablePostProcessing; + } + else + { + iFlags &= ~EDisablePostProcessing; + } + } + +/** +* +* IsPostProcessingDisabled +* @return ETrue, EFalse +* +*/ +TBool CBitmapScalerPlugin::IsPostProcessingDisabled() + { + return ((iFlags & EDisablePostProcessing) != 0); + } + +/** +* +* IsPostProcessingNeeded +* @return TBool scaling process has been completed +* +*/ +TBool CBitmapScalerPlugin::IsPostProcessingNeeded() const + { + return ((iFlags & EIsPostProcessingNeeded) != 0); + } + +/* +* +* HeightAndWidthPreservingAspectRatio +* @param aDestSize +* @param aOrigSize +* Calculates the destination bitmap size based on preserving +* the aspect ratio of the original bitmap +* @pre aOrigSize is not zero in any dimension +*/ +void CBitmapScalerPlugin::HeightAndWidthPreservingAspectRatio( TSize& aDestSize, const TSize& aOrigSize ) const + { + + if ((aOrigSize.iWidth == 0) || (aOrigSize.iHeight == 0)) + { + return; + } + + // no need to optimise, it is not a kernal function which may get called many times + TReal widthRatio = (TReal)aDestSize.iWidth / (TReal)aOrigSize.iWidth; + TReal heightRatio = (TReal)aDestSize.iHeight / (TReal)aOrigSize.iHeight; + + // choose the smaller ratio to use + if(widthRatio < heightRatio) + { + TReal height; + Math::Round(height, aOrigSize.iHeight * widthRatio, 0); + aDestSize.iHeight = static_cast(height); + } + else + { + TReal width; + Math::Round(width, aOrigSize.iWidth * heightRatio, 0); + aDestSize.iWidth = static_cast(width); + } + } + +/* +* +* IsPostProcessingEnabled +* @return 'TBool' +* +*/ +TBool CBitmapScalerPlugin::IsPostProcessingEnabled() const + { + return ((iFlags & EPostProcessingEnabled) != 0); + } + +/* +* +* SetPostProcessingEnabled +* @param aIsEnabled +* +*/ +void CBitmapScalerPlugin::SetPostProcessingEnabled( TBool aIsEnabled ) + { + if(aIsEnabled) + { + iFlags |= EPostProcessingEnabled; + } + else + { + iFlags &= ~EPostProcessingEnabled; + } + } + +/* +* +* CustomCommand +* @param aUid +* @param aParam +* @return 'TInt' an error code indicating success or failure of the +* command +* +*/ +TInt CBitmapScalerPlugin::CustomCommand(TUid aUid, TAny* aParam) + { + TInt status = KErrNotSupported; + if( ( aUid == KICLUidPostProcessCommand ) && ( aParam != NULL ) ) + { + TBool postProcess = *( reinterpret_cast(aParam)); + SetDisablePostProcessing( postProcess ); + status = KErrNone; + } + else if( ( aUid == KICLUidUseLowMemoryAlgorithmCommand ) && ( aParam != NULL ) ) + { + TBool useLowMemAlgorithm = *( reinterpret_cast(aParam) ); + SetUseLowMemoryAlgorithm( useLowMemAlgorithm ); + status = KErrNone; + } + else if( ( aUid == KICLUidSetQualityAlgorithmCommand ) && ( aParam != NULL ) ) + { + CBitmapScaler::TQualityAlgorithm quality = *( reinterpret_cast(aParam) ); + SetQualityAlgorithm(quality); + status = KErrNone; + } + + return status; + } + +/* +* +* UseLowMemoryAlgorithm +* @return 'TBool' +* +*/ +TBool CBitmapScalerPlugin::UseLowMemoryAlgorithm() const + { + return ((iFlags & EUseLowMemoryAlgorithm) != 0); + } + +/* +* +* SetUseLowMemoryAlgorithm +* @param aUseLowMemoryAlgorithm +* +*/ +void CBitmapScalerPlugin::SetUseLowMemoryAlgorithm(TBool aUseLowMemoryAlgorithm) + { + if(aUseLowMemoryAlgorithm) + { + iFlags |= EUseLowMemoryAlgorithm; + } + else + { + iFlags &= ~EUseLowMemoryAlgorithm; + } + } + +/* +* +* SetQualityAlgorithm +* @param CBitmapScaler::TQualityAlgorithm aQuality +* +*/ +void CBitmapScalerPlugin::SetQualityAlgorithm( CBitmapScaler::TQualityAlgorithm aQuality) + { + iQualityLevel = aQuality; + } + +/* +* +* QualityAlgorithm +* @return CBitmapScaler::TQualityAlgorithm +* +*/ +CBitmapScaler::TQualityAlgorithm CBitmapScalerPlugin::QualityAlgorithm() const + { + return iQualityLevel; + } + +/** +* This function is called when the state iCurrentState == EScalingState +* and either the UseMediumQualityAlgorithm or the UseMinimumQualityAlgorithm custom +* commands have been invoked. +* In terms of speed and quality, the normal algorithm, as implemented in Scale(), +* is the slowest / highest quality, whereas the fastest / lowest quality +* implementation is for the case when UseMinimumQualityAlgorithm is specified. +* The difference between the minimum quality and fast algorithms is that +* the fast algorithm forms the destination value from weighted averages of +* the x values prior to and proceeding the value under consideration. +*/ +void CBitmapScalerPlugin::FastScale() + { +#if defined (__SCALING_PROFILING) + RDebug::ProfileStart(7); +#endif // __SCALING_PROFILING + TInt linesLeftPerCall = KLinesPerCall; + + // Height and width to scale to + const TInt maxX = iDestSize.iWidth * iComponentsPerPixel; + const TInt stepY = (iOrigSize.iHeight << KFixedPointScale) / iDestSize.iHeight; + const TInt destBufferLength = iDestDes.Length(); + const TInt divisor = (destBufferLength < iDestSize.iWidth ? destBufferLength : iDestSize.iWidth); + const TInt stepX = (iOrigSize.iWidth << KFixedPointScale) / divisor; + TInt sourceX = 0; // x + const TInt roundVal = 1; + if (iDestSize == iOrigSize) + { + // Same size so just directly copy values. + while(linesLeftPerCall > 0 && iCurOffset < iFinalOffset) + { + iSrcBitmap->GetScanLine(iOrigDes, TPoint(0, iCurOffset), iOrigSize.iWidth, iLocalDisplayMode); + iDestBmpPtr->SetScanLine(iOrigDes, iCurOffset); + linesLeftPerCall--; + iCurOffset++; + } + } + else if (iDestSize.iHeight < iOrigSize.iHeight) + { + TInt currentX = 0; + // For downscaling (destination bitmap size is smaller than the original bitmap size) + while(linesLeftPerCall > 0 && iCurOffset < iFinalOffset) + { + sourceX = 0; + // Fetch the relevant row from the source bitmap + iSrcBitmap->GetScanLine(iOrigDes, TPoint(0, iSrcY), iOrigSize.iWidth, iLocalDisplayMode); + + // Get each colour component of each relevant pixel + for (TInt x = 0; x < maxX; x += iComponentsPerPixel) + { + //Line scaling + ScaleBitmapLine(x,sourceX); + // Increment to the next pixel + currentX += stepX; + // Avoid TInt rounding error + sourceX = ( currentX + (roundVal << KFixedPointScale - 1) ) >> KFixedPointScale; + // Factor for the number of components + sourceX *= iComponentsPerPixel; + // Stay in bounds + if (sourceX >= iOrigDes.Length()) + { + sourceX = iOrigDes.Length() - iComponentsPerPixel; + } + } + currentX = 0; // reset the x counter + iDestBmpPtr->SetScanLine(iDestDes, iCurOffset); // set the scaled line into the target + + // increment to the next row + iCurY += stepY; + // avoiding any rounding errors (using TInt = (TInt + 0.5) ) + iSrcY = (iCurY + (roundVal << KFixedPointScale - 1) ) >> KFixedPointScale; + // Stay in bounds + if (iSrcY >= iOrigSize.iHeight) + { + iSrcY = iOrigSize.iHeight - 1; + } + linesLeftPerCall--; + iCurOffset++; + } + } + else + { + TInt currentX = 0; + //For upscaling (Destination bitmap size is bigger than the original bitmap size) + while(linesLeftPerCall > 0 && iCurOffsetUp !=0 ) + { + sourceX = 0; + // avoiding any rounding errors (using TInt = (TInt + 0.5) ) + iSrcY = (iCurY + (roundVal << KFixedPointScale - 1) ) >> KFixedPointScale; + // Fetch the relevant row from the source bitmap + iSrcBitmap->GetScanLine(iOrigDes, TPoint(0, iSrcY), iOrigSize.iWidth, iLocalDisplayMode); + + // Get each colour component of each relevant pixel + for (TInt x = 0; x < maxX; x += iComponentsPerPixel) + { + //Line scaling + ScaleBitmapLine(x,sourceX); + // Increment to the next pixel + currentX += stepX; + // Avoid TInt rounding error + sourceX = ( currentX + (roundVal << KFixedPointScale - 1) ) >> KFixedPointScale; + // Factor for the number of components + sourceX *= iComponentsPerPixel; + // Stay in bounds + if (sourceX >= iOrigDes.Length()) + { + sourceX = iOrigDes.Length() - iComponentsPerPixel; + } + } + currentX = 0; // reset the x counter + iDestBmpPtr->SetScanLine(iDestDes, iCurOffsetUp); // set the scaled line into the target + // Decrement to the next row from the end + iCurY -= stepY; + // Stay in bounds + if (iSrcY >= iOrigSize.iHeight) + { + iSrcY = iOrigSize.iHeight - 1; + } + linesLeftPerCall--; + iCurOffsetUp--; + } + } + if (IsScalingComplete()) + { + // in case of low memory algo and 1 bitmap we have to resize the resulting bitmap + if (iDestBmpPtr->Handle() == iSrcBitmap->Handle() + && iDestBmpPtr->SizeInPixels() != iDestSize ) + { + TInt err=iDestBmpPtr->Resize(iDestSize); + if (err != KErrNone) + { + ProcessError( err ); + return; + } + } + if( IsPostProcessingNeeded() ) + { + //[ transition to start post processing state] + SetCurrentState( EStartPostProcessState ); + } + else + { + //[ transition to cleanup state ] + SetCurrentState( ECleanUpState ); + } + } + // Start the active object + SelfComplete(KErrNone); +#if defined (__SCALING_PROFILING) + RDebug::ProfileEnd(7); +#endif // __SCALING_PROFILING + } + +/* +* +* ScaleBitmapLine +* @param aX +* @param aSourceX +* +*/ +void CBitmapScalerPlugin::ScaleBitmapLine(TInt aX,TInt aSourceX) + { + TUint8* destDesPtr = const_cast( iDestDes.Ptr() ); + const TUint8* orgDesPtr = iOrigDes.Ptr(); + + if (QualityAlgorithm() == CBitmapScaler::EMinimumQuality) + { + CopyPixel(destDesPtr + aX,orgDesPtr + aSourceX); + } + else // must be CBitmapScaler::EMediumQuality + { + // Better quality + TInt nextValue = 0; + TInt prevValue = 0; + TInt value = 0; + // eg weightedAv. = (w1*v1 + w2*v2 + w3*v3) / (w1 + w2 + w3) + // so if w1=w3=8 and w2=16 + // (v1 * 2<<3 + v2 * 2<<4 + v3 * 2<<3) >> 2 * 5 + const TInt minorShift = 3; // prev and next value weighting + const TInt majorShift = 4; // current value weighting + const TInt backShift = 5; // average + + for (TInt i = 0; i < iComponentsPerPixel; i++) + { + // Get the previous value + TInt index = aSourceX - iComponentsPerPixel; + if (index < 0) + { + index = i; + } + else + { + index += i; + } + prevValue = TInt(orgDesPtr[index]); + + // Weight the previous value + prevValue = prevValue << minorShift; + // Get the next value + index = aSourceX; + if (index + iComponentsPerPixel >= iOrigDes.Length()) + { + index += i; + } + else + { + index += (i + iComponentsPerPixel); + } + nextValue = TInt(orgDesPtr[index]); + + // Weight the next value + nextValue = nextValue << minorShift; + // Weight the current value + value = TInt(orgDesPtr[aSourceX+ i]); + + value = value << majorShift; + // Sum all values + value += prevValue; + value += nextValue; + // Average + value = value >> backShift; + destDesPtr[aX + i] = TUint8(value); + } + } + } +