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