diff -r 000000000000 -r 951a5db380a0 videoeditorengine/vedtranscoder/src/Ctrscaler.cpp --- /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 + + +// Debug print macro +#ifdef _DEBUG + #include + #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((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(aTrgSize.iWidth), 4.0 ); + + if ( remainder == 0.0 ) + { + Math::Mod( remainder, static_cast(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(aTrg.Ptr()), yLength, yLength); + tempPtr.Fill((TChar)data, yLength); + + // U,V + data = 127; + tempPtr.Set(const_cast(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( aSrc.Ptr() ); + iTrg = const_cast( 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(aOutput.iSize.iWidth + aOutput.iSize.iHeight) * KTRTimeFactorScale; + + PRINT((_L("CTRScaler::EstimateResampleFrameTime(), resample frame time: %.2f"), time)) + + return time; + } + + +// End of file