--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/videoeditorengine/vedtranscoder/src/Ctrscaler.cpp Fri Jan 29 14:08:33 2010 +0200
@@ -0,0 +1,791 @@
+/*
+* Copyright (c) 2010 Ixonos Plc.
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the "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:
+* Ixonos Plc
+*
+* Description:
+* Resampling framework for YUV 4.2.0.
+*
+*/
+
+
+
+// INCLUDES
+#include "ctrscaler.h"
+#include "ctrsettings.h"
+#include "ctrhwsettings.h"
+#include <e32math.h>
+
+
+// Debug print macro
+#ifdef _DEBUG
+ #include <e32svr.h>
+ #define PRINT(x) RDebug::Print x;
+#else
+ #define PRINT(x)
+#endif
+
+
+// An assertion macro wrapper to clean up the code a bit
+#define VPASSERT(x) __ASSERT_DEBUG(x, User::Panic(_L("CTRScaler"), KErrAbort))
+
+
+// Macros for fixed point math
+#define FP_BITS 15 // Number of bits to use for FP decimals
+#define FP_FP(x) (static_cast<TInt>((x) * 32768.0))
+#define FP_ONE (1 << FP_BITS)
+#define FP_MUL(x,y) (((x) * (y)) >> FP_BITS)
+#define FP_FRAC(x) ((x) & (FP_ONE - 1))
+#define FP_INT(x) ((x) >> FP_BITS)
+
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CTRScaler::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CTRScaler* CTRScaler::NewL()
+ {
+ // Standard two phase construction
+ CTRScaler* self = new (ELeave) CTRScaler();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+
+ return self;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CTRScaler::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::ConstructL()
+ {
+ }
+
+
+// -----------------------------------------------------------------------------
+// CTRScaler::CTRScaler
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+CTRScaler::CTRScaler()
+ {
+ // Scaler does not perform any operation before initializing
+ iOperation = EOperationNone;
+ iTrgBuffer = NULL;
+ }
+
+
+// ---------------------------------------------------------
+// CTRScaler::~CTRScaler()
+// Destructor
+// ---------------------------------------------------------
+//
+CTRScaler::~CTRScaler()
+ {
+ }
+
+// ---------------------------------------------------------
+// CTRScaler::IsWideAspectRatio()
+// Checks if aspect ratio is wide
+// ---------------------------------------------------------
+//
+TBool CTRScaler::IsWideAspectRatio(TSize aSize)
+ {
+ return ( TReal(aSize.iWidth) / TReal(aSize.iHeight) ) > KTRWideThreshold;
+ }
+
+// -----------------------------------------------------------------------------
+// CTRScaler::SetupScalerL
+// Sets the scaler options (src buffer, dest buffer (could be the same as a src), src resolution, trg resolution)
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::SetScalerOptionsL(TPtr8& aSrc, TPtr8& aTrg, TSize& aSrcSize, TSize& aTrgSize )
+ {
+
+
+ PRINT((_L("CTRScaler::SetScalerOptionsL, src = (%d, %d), trg = (%d, %d)"),
+ aSrcSize.iWidth, aSrcSize.iHeight, aTrgSize.iWidth, aTrgSize.iHeight));
+
+ // Check settings
+ if ( ( !aSrc.Ptr() ) || ( !aTrg.Ptr() ) ||
+ ( aSrcSize.iWidth == 0) || ( aSrcSize.iHeight == 0 ) ||
+ ( aTrgSize.iWidth == 0) || ( aTrgSize.iHeight == 0 ) ||
+ ( aSrc.MaxLength() < ( aSrcSize.iWidth * aSrcSize.iHeight * 3 / 2 ) ) ||
+ ( aTrg.MaxLength() < ( aTrgSize.iWidth * aTrgSize.iHeight * 3 / 2 ) )
+ )
+ {
+ PRINT((_L("CTRScaler::SetupScalerL(), Given options are not supported")))
+ User::Leave(KErrNotSupported);
+ }
+ else
+ {
+ TReal remainder = 0.0;
+ iTrgBuffer = NULL;
+
+ // We don't support non-multiple output yet
+ Math::Mod( remainder, static_cast<TReal>(aTrgSize.iWidth), 4.0 );
+
+ if ( remainder == 0.0 )
+ {
+ Math::Mod( remainder, static_cast<TReal>(aTrgSize.iHeight), 4.0 );
+ }
+
+ if ( remainder != 0.0 )
+ {
+ PRINT((_L("CTRScaler::SetupScalerL(), Scaler does not support output resolution that is not multiple by 4")))
+ User::Leave(KErrNotSupported);
+ }
+
+ TSize targetSize = aTrgSize;
+ // check if black boxing is needed
+ TBool srcWide = IsWideAspectRatio(aSrcSize);
+ TBool dstWide = IsWideAspectRatio(aTrgSize);
+
+ iBlackBoxing = TSize(0,0);
+
+ TBool doScaling = ETrue;
+ if (srcWide != dstWide)
+ {
+ TSize resolution(0,0);
+
+ doScaling = GetIntermediateResolution(aSrcSize, aTrgSize, resolution, iBlackBoxing);
+
+ // Set the whole image to black
+ TUint yLength = aTrgSize.iWidth * aTrgSize.iHeight;
+ TUint uvLength = yLength >> 1;
+
+ // Y
+ TInt data = 0;
+ TPtr8 tempPtr(0,0);
+ tempPtr.Set(const_cast<TUint8*>(aTrg.Ptr()), yLength, yLength);
+ tempPtr.Fill((TChar)data, yLength);
+
+ // U,V
+ data = 127;
+ tempPtr.Set(const_cast<TUint8*>(aTrg.Ptr()) + yLength, uvLength, uvLength);
+ tempPtr.Fill((TChar)data, uvLength);
+
+ aTrgSize = resolution;
+ PRINT((_L("CTRScaler::SetScalerOptionsL, blackboxing width = %d, height = %d"), iBlackBoxing.iWidth, iBlackBoxing.iHeight));
+ }
+
+ if ( !doScaling )
+ {
+ // No need to perform resampling operation, copy data with black boxing
+ iOperation = EOperationCopyWithBB;
+ }
+
+ else if ( (aTrgSize.iWidth == aSrcSize.iWidth) && (aTrgSize.iHeight == aSrcSize.iHeight) )
+ {
+ // No need to perform resampling operation, just copy data
+ iOperation = EOperationCopy;
+ }
+ else if ( (aTrgSize.iWidth == aSrcSize.iWidth * 2) && (aTrgSize.iHeight == aSrcSize.iHeight * 2) )
+ {
+ // Resolution is doubled
+ iOperation = EDoubleSize;
+ }
+ else if ( (aTrgSize.iWidth * 2 == aSrcSize.iWidth) && (aTrgSize.iHeight * 2 == aSrcSize.iHeight) )
+ {
+ // Resolution is halved
+ iOperation = EHalveSize;
+ }
+ else if ( (aTrgSize.iWidth > aSrcSize.iWidth) && (aTrgSize.iHeight > aSrcSize.iHeight) )
+ {
+ // Resolution is increased
+ iOperation = EUpSampling;
+ }
+ else if ( (aTrgSize.iWidth < aSrcSize.iWidth) && (aTrgSize.iHeight < aSrcSize.iHeight) )
+ {
+ // Resolution is decreased
+ iOperation = EDownSampling;
+ }
+ else
+ {
+ // The image is streched ie. vertical resolution increases and horizontal decreases or vice versa
+ iOperation = EUpDownSampling;
+ }
+
+ // Set given settings
+ iSrc = const_cast<TUint8*>( aSrc.Ptr() );
+ iTrg = const_cast<TUint8*>( aTrg.Ptr() );
+ iSrcSize = aSrcSize;
+ iTrgSize = aTrgSize;
+ iSrcInit = iSrc;
+ iTrgInit = iTrg;
+ aTrgSize = targetSize; // recover target size since it's a reference
+ iTrgDataSize = aTrgSize.iWidth * aTrgSize.iHeight * 3 / 2;
+ iTrgBuffer = &aTrg;
+ }
+ }
+
+// ---------------------------------------------------------
+// CTRScaler::GetIntermediateResolution()
+// Calculates intermediate resolution for use with black boxing
+// ---------------------------------------------------------
+//
+TBool CTRScaler::GetIntermediateResolution(TSize aSrcSize, TSize aTrgSize,
+ TSize& aTargetResolution, TSize& aBlackBoxing)
+ {
+
+ TSize resolution;
+ TBool doScaling = ETrue;
+
+ TBool srcWide = IsWideAspectRatio(aSrcSize);
+ TBool dstWide = IsWideAspectRatio(aTrgSize);
+
+ VPASSERT(srcWide != dstWide);
+
+ if (dstWide)
+ {
+ // Pillarboxing
+
+ // scale height to destination
+ TReal factor = TReal(aTrgSize.iHeight) / TReal(aSrcSize.iHeight);
+
+ resolution.iWidth = TInt( aSrcSize.iWidth * factor );
+
+ if (resolution.iWidth & 0x1 > 0)
+ resolution.iWidth++;
+
+ resolution.iHeight = aTrgSize.iHeight;
+
+ while ( (aTrgSize.iWidth - resolution.iWidth) % 4 != 0 )
+ {
+ resolution.iWidth += 2;
+ }
+
+ aBlackBoxing.iWidth = (aTrgSize.iWidth - resolution.iWidth) / 2;
+
+ if ( factor == 1.0 )
+ {
+ // source and destination heights are the same,
+ // meaning source width is smaller and we don't
+ // have to scale, just do pillarboxing
+ doScaling = EFalse;
+
+ // set target width
+ resolution.iWidth = aTrgSize.iWidth;
+ }
+
+ }
+ else
+ {
+ // Letterboxing
+
+ // scale width to destination
+ TReal factor = TReal(aTrgSize.iWidth) / TReal(aSrcSize.iWidth);
+
+ resolution.iHeight = TInt( aSrcSize.iHeight * factor );
+
+ if (resolution.iHeight & 0x1 > 0)
+ resolution.iHeight++;
+
+ resolution.iWidth = aTrgSize.iWidth;
+
+ while ( (aTrgSize.iHeight - resolution.iHeight) % 4 != 0 )
+ {
+ resolution.iHeight += 2;
+ }
+
+ aBlackBoxing.iHeight = (aTrgSize.iHeight - resolution.iHeight) / 2;
+
+ if ( factor == 1.0 )
+ {
+ // source and destination widths are the same,
+ // meaning source height is smaller and we don't
+ // have to scale, just do letterboxing
+ doScaling = EFalse;
+
+ // set target height
+ resolution.iHeight = aTrgSize.iHeight;
+ }
+ }
+
+ PRINT((_L("CTRScaler::GetIntermediateResolution, resolution = (%d, %d), bb = (%d, %d)"),
+ resolution.iWidth, resolution.iHeight, aBlackBoxing.iWidth, aBlackBoxing.iHeight));
+
+ aTargetResolution = resolution;
+
+ return doScaling;
+
+
+}
+
+
+// -----------------------------------------------------------------------------
+// CTRScaler::Scale()
+// Scale the image
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::Scale()
+ {
+ TSize srcSizeUV = TSize( iSrcSize.iWidth / 2, iSrcSize.iHeight / 2 );
+ TSize trgSizeUV = TSize( iTrgSize.iWidth / 2, iTrgSize.iHeight / 2 );
+ TSize blackBoxingUV = TSize( iBlackBoxing.iWidth / 2, iBlackBoxing.iHeight / 2 );
+
+ switch( iOperation )
+ {
+ case EOperationCopy:
+ {
+ // Src / Trg resolutions are the same, no needs to perform resampling
+ if ( iSrc != iTrg )
+ {
+ // Copy data, if different memory areas are specified
+ Mem::Copy( iTrg, iSrc, iTrgDataSize );
+ }
+ else
+ {
+ // The same memory fragment is specified for the output; Keep it without changes;
+ }
+ }
+ break;
+
+ case EOperationCopyWithBB:
+ {
+ // Copy with black boxing
+ CopyWithBlackBoxing(iSrcSize, iTrgSize, iBlackBoxing);
+ CopyWithBlackBoxing(srcSizeUV, trgSizeUV, blackBoxingUV);
+ CopyWithBlackBoxing(srcSizeUV, trgSizeUV, blackBoxingUV);
+ }
+ break;
+
+ case EDownSampling:
+ {
+ TInt error = KErrNoMemory;
+
+ // If scaling to less than 50% of the source size
+ if ( (iTrgSize.iWidth * 2 < iSrcSize.iWidth) && (iTrgSize.iHeight * 2 < iSrcSize.iHeight) )
+ {
+ // Try to do the scaling in two steps
+ TRAP( error, DoHalveAndBilinearResampleL() );
+ }
+
+ // If the above failed or scaling to 51% or higher
+ if ( error != KErrNone )
+ {
+ // Resample the Y, U & V components
+ ResampleBilinear(iSrcSize, iTrgSize, iBlackBoxing);
+ ResampleBilinear(srcSizeUV, trgSizeUV, blackBoxingUV);
+ ResampleBilinear(srcSizeUV, trgSizeUV, blackBoxingUV);
+ }
+ }
+ break;
+
+ case EUpSampling:
+ case EUpDownSampling:
+ {
+ // Resample the Y, U & V components
+ ResampleBilinear(iSrcSize, iTrgSize, iBlackBoxing);
+ ResampleBilinear(srcSizeUV, trgSizeUV, blackBoxingUV);
+ ResampleBilinear(srcSizeUV, trgSizeUV, blackBoxingUV);
+ }
+ break;
+
+ case EDoubleSize:
+ {
+ // Resample the Y, U & V components to double size
+ ResampleDouble(iSrcSize, iTrgSize);
+ ResampleDouble(srcSizeUV, trgSizeUV);
+ ResampleDouble(srcSizeUV, trgSizeUV);
+ }
+ break;
+
+ case EHalveSize:
+ {
+ // Resample the Y, U & V components to half size
+ ResampleHalve(iSrcSize, iTrgSize, iBlackBoxing);
+ ResampleHalve(srcSizeUV, trgSizeUV, blackBoxingUV);
+ ResampleHalve(srcSizeUV, trgSizeUV, blackBoxingUV);
+ }
+ break;
+
+ case EOperationNone:
+ {
+ PRINT((_L("CTRScaler::Scale(), Scaler was not initialized yet to perform any operation")))
+ return;
+ }
+// break;
+
+ default:
+ {
+ }
+ }
+
+ // Recover source and target data pointers
+ iSrc = iSrcInit;
+ iTrg = iTrgInit;
+
+ // Set Dsc length
+ if (iTrgBuffer)
+ {
+ iTrgBuffer->SetLength(iTrgDataSize);
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CTRScaler::CopyWithBlackBoxing()
+// Copies frame to target buffer applying black borders
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::CopyWithBlackBoxing(TSize& aSrcSize, TSize& aTrgSize, TSize& aBlackBoxing)
+{
+
+ if (aBlackBoxing.iHeight != 0)
+ {
+
+ TInt copyLength = aSrcSize.iWidth * aSrcSize.iHeight;
+
+ iTrg += aBlackBoxing.iHeight * aTrgSize.iWidth;
+ Mem::Copy(iTrg, iSrc, copyLength);
+
+ iTrg += copyLength;
+ iTrg += aBlackBoxing.iHeight * aTrgSize.iWidth;
+ iSrc += copyLength;
+
+ }
+
+ else if (aBlackBoxing.iWidth != 0)
+ {
+
+ TInt i;
+ iTrg += aBlackBoxing.iWidth;
+
+ for (i = 0; i < iTrgSize.iHeight; i++)
+ {
+ // copy one row
+ Mem::Copy(iTrg, iSrc, aSrcSize.iWidth);
+ iSrc += aSrcSize.iWidth;
+ iTrg += aSrcSize.iWidth;
+ iTrg += aBlackBoxing.iWidth * 2;
+ }
+
+ // subtract the width of one pillar
+ iTrg -= aBlackBoxing.iWidth;
+
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// CTRScaler::DoHalveAndBilinearResampleL()
+// First resamples an image to half size and then uses bilinear resample to
+// scale it to requested size.
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::DoHalveAndBilinearResampleL()
+ {
+ // Make sure the scale factor is correct
+ VPASSERT( (iTrgSize.iWidth * 2 < iSrcSize.iWidth) &&
+ (iTrgSize.iHeight * 2 < iSrcSize.iHeight) );
+
+ TSize srcSizeUV = TSize( iSrcSize.iWidth / 2, iSrcSize.iHeight / 2 );
+ TSize trgSizeUV = TSize( iTrgSize.iWidth / 2, iTrgSize.iHeight / 2 );
+ TSize blackBoxingUV = TSize( iBlackBoxing.iWidth / 2, iBlackBoxing.iHeight / 2 );
+
+ // Calculate the size for the temporary image where we store the intermediate result
+ TSize tempSize = TSize( iSrcSize.iWidth / 2, iSrcSize.iHeight / 2 );
+ TSize tempSizeUV = TSize( tempSize.iWidth / 2, tempSize.iHeight / 2 );
+
+ // Allocate memory for the temporary image
+ TUint8* tempBuffer = (TUint8*) User::AllocLC(tempSize.iWidth * tempSize.iHeight * 3 / 2);
+
+ // Set the temporary image as the target
+ iTrg = tempBuffer;
+
+ TSize zeroBlackBox = TSize(0,0);
+ // Resample the Y, U & V components to half size
+ ResampleHalve(iSrcSize, tempSize, zeroBlackBox);
+ ResampleHalve(srcSizeUV, tempSizeUV, zeroBlackBox);
+ ResampleHalve(srcSizeUV, tempSizeUV, zeroBlackBox);
+
+ // Set the temporary image as the source and recover the original target
+ iSrc = tempBuffer;
+ iTrg = iTrgInit;
+
+ // Resample the Y, U & V components
+ ResampleBilinear(tempSize, iTrgSize, iBlackBoxing);
+ ResampleBilinear(tempSizeUV, trgSizeUV, blackBoxingUV);
+ ResampleBilinear(tempSizeUV, trgSizeUV, blackBoxingUV);
+
+ // Release the temporary buffer
+ CleanupStack::PopAndDestroy(tempBuffer);
+ }
+
+// -----------------------------------------------------------------------------
+// CTRScaler::ResampleBilinear()
+// Resamples an image with bilinear filtering. The target pixel is generated by
+// linearly interpolating the four nearest source pixels in x- and y-directions.
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::ResampleBilinear(TSize& aSrcSize, TSize& aTrgSize, TSize& aBlackBoxing)
+ {
+ TInt i = 0, j = 0;
+ TInt x = 0, y = 0;
+ TInt fx = 0, fy = 0;
+ TInt weightFactor = 0;
+
+ // Pointers to the source memory
+ TUint8* srcRowPosition = 0;
+ TUint8* srcPixelPosition = 0;
+
+ // Calculate the scale factor using the max indices of the source and target images
+ TReal scaleX = TReal(aSrcSize.iWidth - 1) / TReal(aTrgSize.iWidth - 1);
+ TReal scaleY = TReal(aSrcSize.iHeight - 1) / TReal(aTrgSize.iHeight - 1);
+
+ // Convert the scale factor to fixed point
+ iScaleXInt = FP_FP(scaleX) - 1; // subtract 1 so we don't go outside the source image
+ iScaleYInt = FP_FP(scaleY) - 1;
+
+ if ( aBlackBoxing.iWidth != 0 )
+ {
+ // increment target pointer over first 'pillar'
+ iTrg += aBlackBoxing.iWidth;
+ }
+ else if ( aBlackBoxing.iHeight != 0 )
+ {
+ // increment target pointer over top letterboxed area
+ iTrg += aTrgSize.iWidth * aBlackBoxing.iHeight;
+ }
+
+ // Loop target rows
+ for( i = 0, y = 0; i < aTrgSize.iHeight; i++ )
+ {
+ // Calculate the row position of the source
+ srcRowPosition = iSrc + FP_INT(y) * aSrcSize.iWidth;
+
+ fy = FP_FRAC(y); // Fractational part of the row position
+
+ // Loop target columns
+ for( j = 0, x = 0; j < aTrgSize.iWidth; j++ )
+ {
+ // Calculate the pixel position in the source
+ srcPixelPosition = srcRowPosition + FP_INT(x);
+
+ // Calculate the weight factor for blending
+ fx = FP_FRAC(x);
+ weightFactor = FP_MUL(fx, fy);
+
+ // Blend using the four nearest pixels
+ *(iTrg) = FP_INT(
+ *(srcPixelPosition) * (weightFactor + FP_ONE - fx - fy) +
+ *(srcPixelPosition + 1) * (fx - weightFactor) +
+ *(srcPixelPosition + aSrcSize.iWidth) * (fy - weightFactor) +
+ *(srcPixelPosition + 1 + aSrcSize.iWidth) * weightFactor );
+
+ iTrg++; // Move on to the next target pixel
+ x += iScaleXInt; // Calculate the column for the next source pixel
+ }
+
+ y += iScaleYInt; // Calculate the row for the next source pixels
+
+ if ( aBlackBoxing.iWidth != 0 )
+ {
+ // increment target pointer over two pillars, one at the end of this row,
+ // other one at the beginning of the next row
+ iTrg += aBlackBoxing.iWidth * 2;
+ }
+ }
+
+ // Update pointers
+ iSrc += aSrcSize.iWidth * aSrcSize.iHeight;
+
+ if ( aBlackBoxing.iWidth != 0 )
+ {
+ // subtract the width of one pillar
+ iTrg -= aBlackBoxing.iWidth;
+ }
+ else if ( aBlackBoxing.iHeight != 0 )
+ {
+ // increment over bottom letterboxed area
+ iTrg += aBlackBoxing.iHeight * aTrgSize.iWidth;
+ }
+
+ }
+
+// -----------------------------------------------------------------------------
+// CTRScaler::ResampleHalve()
+// Resamples an image to half size. For each target pixel a 2x2 pixel area is
+// read from the source and blended together to produce the target color.
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::ResampleHalve(TSize& aSrcSize, TSize& aTrgSize, TSize& aBlackBoxing)
+ {
+ TInt i = 0, j = 0;
+
+ // Make sure the scale factor is correct
+ VPASSERT( (aTrgSize.iWidth * 2 == aSrcSize.iWidth) &&
+ (aTrgSize.iHeight * 2 == aSrcSize.iHeight) );
+
+ if ( aBlackBoxing.iHeight != 0 )
+ {
+ // increment target pointer over top letterboxed area
+ iTrg += aTrgSize.iWidth * aBlackBoxing.iHeight;
+ }
+
+ // Loop target rows
+ for( i = 0; i < aTrgSize.iHeight; i++ )
+ {
+ // Loop target columns
+ for( j = 0; j < aTrgSize.iWidth; j++ )
+ {
+ // Calculate the target pixel by blending the 4 nearest source pixels
+ *(iTrg) = (
+ *(iSrc) +
+ *(iSrc + 1) +
+ *(iSrc + aSrcSize.iWidth) +
+ *(iSrc + 1 + aSrcSize.iWidth)
+ ) >> 2; // divide by 4
+
+ iTrg++; // Move on to the next target pixel
+ iSrc += 2; // Sample every second column from the source
+ }
+
+ iSrc += aSrcSize.iWidth; // Sample every second row from the source
+ }
+
+ if ( aBlackBoxing.iHeight != 0 )
+ {
+ // increment over bottom letterboxed area
+ iTrg += aBlackBoxing.iHeight * aTrgSize.iWidth;
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CTRScaler::ResampleDouble()
+// Resamples an image to double size. A 2x2 pixel area is generated using
+// the four nearest pixels from the source and written to the target image.
+// -----------------------------------------------------------------------------
+//
+void CTRScaler::ResampleDouble(TSize& aSrcSize, TSize& aTrgSize)
+ {
+ TInt i = 0, j = 0;
+
+ // Make sure the scale factor is correct
+ VPASSERT( (aTrgSize.iWidth == aSrcSize.iWidth * 2) &&
+ (aTrgSize.iHeight == aSrcSize.iHeight * 2) );
+
+ // Generate 2x2 target pixels in each loop
+
+ // Loop every second target row
+ for( i = 0; i < aTrgSize.iHeight - 2; i += 2 )
+ {
+ // Loop every second target column
+ for( j = 0; j < aTrgSize.iWidth - 2; j += 2 )
+ {
+ // Top-left pixel: Copy as it is
+ *(iTrg) = *(iSrc);
+
+ // Top-right pixel: Blend the pixels on the left and right
+ *(iTrg + 1) = (*(iSrc) + *(iSrc + 1)) >> 1;
+
+ // Bottom-left pixel: Blend the above and below pixels
+ *(iTrg + aTrgSize.iWidth) = (*(iSrc) + *(iSrc + aSrcSize.iWidth)) >> 1;
+
+ // Bottom-right pixel: Blend the four nearest pixels
+ *(iTrg + 1 + aTrgSize.iWidth) = (
+ *(iSrc) +
+ *(iSrc + 1) +
+ *(iSrc + aSrcSize.iWidth) +
+ *(iSrc + 1 + aSrcSize.iWidth)
+ ) >> 2;
+
+ iTrg += 2; // Move on to the next 2x2 group of pixels
+ iSrc++; // Sample the next pixel from source
+ }
+
+ // The last 2x2 pixels on this row need to be handled separately
+
+ // Top-left and top-right pixels: Copy as it is
+ *(iTrg) = *(iTrg + 1) = *(iSrc);
+
+ // Bottom-left and bottom-right pixels: Blend the above and below pixels
+ *(iTrg + aTrgSize.iWidth) = *(iTrg + 1 + aTrgSize.iWidth) = (
+ *(iSrc) +
+ *(iSrc + aSrcSize.iWidth)
+ ) >> 1;
+
+ iTrg += 2 + aTrgSize.iWidth; // Move on to the beginning of the next row
+ iSrc++; // Sample the next pixel from source
+ }
+
+ // Handle the last row
+ for( j = 0; j < aTrgSize.iWidth - 2; j += 2 )
+ {
+ // Top-left and bottom-left pixels: Copy as it is
+ *(iTrg) = *(iTrg + aTrgSize.iWidth) = *(iSrc);
+
+ // Top-right and bottom-right pixels: Blend the pixels on the left and right
+ *(iTrg + 1) = *(iTrg + 1 + aTrgSize.iWidth) = (
+ *(iSrc) +
+ *(iSrc + 1)
+ ) >> 1;
+
+ iTrg += 2; // Move on to the next 2x2 group of pixels
+ iSrc++; // Sample the next pixel from source
+ }
+
+ // Handle the last 2x2 group of pixels
+
+ // Copy all four pixels
+ *(iTrg) = *(iTrg + 1) = *(iTrg + aTrgSize.iWidth) = *(iTrg + 1 + aTrgSize.iWidth) = *(iSrc);
+
+ // Update pointers to the beginning of the next image
+ iTrg += 2 + aTrgSize.iWidth;
+ iSrc++;
+ }
+
+// -----------------------------------------------------------------------------
+// CTRScaler::EstimateResampleFrameTime
+// Returns a time estimate of how long it takes to resample a frame
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TReal CTRScaler::EstimateResampleFrameTime(const TTRVideoFormat& aInput, const TTRVideoFormat& aOutput)
+ {
+ // Assume bilinear filtering is used by default
+ TReal time = KTRResampleTimeFactorBilinear;
+
+ if ( (aOutput.iSize.iWidth == aInput.iSize.iWidth) && (aOutput.iSize.iHeight == aInput.iSize.iHeight) )
+ {
+ // No need for resampling
+ return 0.0;
+ }
+ else if ( (aOutput.iSize.iWidth == aInput.iSize.iWidth * 2) && (aOutput.iSize.iHeight == aInput.iSize.iHeight * 2) )
+ {
+ // Resolution is doubled
+ time = KTRResampleTimeFactorDouble;
+ }
+ else if ( (aOutput.iSize.iWidth * 2 == aInput.iSize.iWidth) && (aOutput.iSize.iHeight * 2 == aInput.iSize.iHeight) )
+ {
+ // Resolution is halved
+ time = KTRResampleTimeFactorHalve;
+ }
+
+ // Multiply the time by the resolution of the output frame
+ time *= static_cast<TReal>(aOutput.iSize.iWidth + aOutput.iSize.iHeight) * KTRTimeFactorScale;
+
+ PRINT((_L("CTRScaler::EstimateResampleFrameTime(), resample frame time: %.2f"), time))
+
+ return time;
+ }
+
+
+// End of file