Merge docml changeset with recent Nokia delivery.
/*
* Copyright (c) 2008-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: Utility for creating bitmaps from video files
*
*/
/**
* @internal reviewed 31/07/2007 by Simon Brooks
*/
#include "glxtnimageutility.h"
#include <MIHLScaler.h> // MIHLScaler
#include <fbs.h>
#include <glxtracer.h>
#include <glxpanic.h>
#include <glxthumbnail.h>
#include <imageconversion.h>
#include "glxtnimagedecoderfactory.h"
// -----------------------------------------------------------------------------
// Constructor
// -----------------------------------------------------------------------------
//
CGlxtnImageUtility::CGlxtnImageUtility(RFs& aFs) : iFs(aFs)
{
TRACER("CGlxtnImageUtility::CGlxtnImageUtility()");
}
// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
//
CGlxtnImageUtility::~CGlxtnImageUtility()
{
TRACER("CGlxtnImageUtility::~CGlxtnImageUtility()");
delete iDecoder;
delete iScaler;
delete iBitGc;
}
// -----------------------------------------------------------------------------
// Cancel
// -----------------------------------------------------------------------------
//
void CGlxtnImageUtility::Cancel()
{
TRACER("void CGlxtnImageUtility::Cancel()");
if ( iDecoder )
{
iDecoder->Cancel();
}
if ( iScaler && iScaler->IsBusy())
{
iScaler->CancelProcess();
}
}
// -----------------------------------------------------------------------------
// AdjustSize
// -----------------------------------------------------------------------------
//
void CGlxtnImageUtility::AdjustSize(TSize& aTargetSize, const TSize& aSourceSize)
{
TRACER("void CGlxtnImageUtility::AdjustSize()");
if ( aTargetSize.iHeight * aSourceSize.iWidth
< aTargetSize.iWidth * aSourceSize.iHeight )
{
// Source has taller aspect than target so reduce target width
aTargetSize.iWidth = ( ( aTargetSize.iHeight * aSourceSize.iWidth )
/ ( aSourceSize.iHeight ) );
}
else
{
// Source has wider aspect than target so reduce target height
aTargetSize.iHeight = (aTargetSize.iWidth * aSourceSize.iHeight)
/ aSourceSize.iWidth;
}
}
// -----------------------------------------------------------------------------
// DecodeImageL
// -----------------------------------------------------------------------------
//
CFbsBitmap* CGlxtnImageUtility::DecodeImageL(TRequestStatus& aStatus,
const TDesC& aFileName, RArray<TSize>& aTargetSizes, TDisplayMode aMode)
{
TRACER("CFbsBitmap* CGlxtnImageUtility::DecodeImageL()");
delete iDecoder;
iDecoder = NULL;
iDecoder = GlxtnImageDecoderFactory::NewL( iFs, aFileName );
iOriginalSize = iDecoder->FrameInfo().iOverallSizeInPixels;
if ( 0 == iOriginalSize.iWidth || 0 == iOriginalSize.iHeight )
{
User::Leave(KErrCorrupt);
}
TInt count = aTargetSizes.Count();
TSize testSize;
TSize targetSize;
for ( TInt i = 0 ; i < count ; i++ )
{
testSize = aTargetSizes[i];
AdjustSize(testSize, iOriginalSize);
if( ( testSize.iWidth > targetSize.iWidth ) || ( testSize.iHeight > targetSize.iHeight ) )
{
targetSize = testSize;
}
}
// Find max scaling factor which won't make image smaller than target size
TInt shift = 3;
while ( shift
&& ((iOriginalSize.iWidth >> shift) < targetSize.iWidth
|| (iOriginalSize.iHeight >> shift) < targetSize.iHeight ) )
{
shift--;
}
TInt roundUp = (1 << shift) - 1;
TSize loadSize((iOriginalSize.iWidth + roundUp) >> shift,
(iOriginalSize.iHeight + roundUp) >> shift);
CFbsBitmap* image = new (ELeave) CFbsBitmap;
CleanupStack::PushL(image);
User::LeaveIfError(image->Create(loadSize, aMode));
iDecoder->Convert(&aStatus, *image);
CleanupStack::Pop(image);
return image;
}
// -----------------------------------------------------------------------------
// FreeDecoder
// -----------------------------------------------------------------------------
//
void CGlxtnImageUtility::FreeDecoder()
{
TRACER("void CGlxtnImageUtility::FreeDecoder()");
delete iDecoder;
iDecoder = NULL;
}
// -----------------------------------------------------------------------------
// OriginalSize
// -----------------------------------------------------------------------------
//
const TSize& CGlxtnImageUtility::OriginalSize() const
{
TRACER("TSize& CGlxtnImageUtility::OriginalSize()");
return iOriginalSize;
}
//---------------------------------------------------------------------------
/**
K-Nearest neighbour scaling for EColor64K.
Code originally inherited from t_display.cpp by Toni Hemminki.
Added windowing and further optimized by Jarmo Nikula 2006-2007.
@param aSrc Source buffer pointer
@param aSrcStride Source buffer stride in bytes
@param aSrcCols Source columns
@param aSrcRows Source rows
@param aX Source window top-left X-coordinate
@param aY Source window top-left Y-coordinate
@param aW Source window width
@param aH Source window height
@param aDst Destination buffer pointer
@param aDstStride Destination buffer stride in bytes
@param aDstCols Destination columns, must be even
@param aDstRows Destination rows
*/
// OPTION's for MMP file:
// Interleave assembly code with C, output can be found on txt-files on some BUILD directory:
// OPTION ARMCC --asm --interleave
// Modify optimization for ARM insturcion set and for maximum speed
// OPTION_REPLACE ARMCC --arm -Otime
/* aSource->LockHeap();
TUint16* sourceAddress = (TUint16*)aSource->DataAddress();
aSource->UnlockHeap();
aTarget->LockHeap();
TUint16* targetAddress = (TUint16*)aTarget->DataAddress();
aTarget->UnlockHeap();
ScaleColor64K(sourceAddress, aSource->DataStride(),
sourceSize.iWidth, sourceSize.iHeight, 0, 0, sourceSize.iWidth, sourceSize.iHeight,
targetAddress, aTarget->DataStride(),
targetSize.iWidth, targetSize.iHeight);
*/
void CGlxtnImageUtility::FilterImageL(TRequestStatus* aStatus, CFbsBitmap* aSource, CFbsBitmap*& aFilteredSource, CFbsBitmap* aTarget)
{
TRACER("void CGlxtnImageUtility::FilterImageL()");
TSize sourceSize(aSource->SizeInPixels());
TSize targetSize(aTarget->SizeInPixels());
TInt scalePercent = ((100*sourceSize.iWidth)/targetSize.iWidth) - 100;
// no need to filter if close enough or smaller
if ( scalePercent >= 2 && ( aTarget->DisplayMode() == EColor64K ) )
{
FilterL(aSource, aFilteredSource, scalePercent);
}
*aStatus = KRequestPending;
User::RequestComplete(aStatus, KErrNone);
}
// -----------------------------------------------------------------------------
// ScaleImageL
// -----------------------------------------------------------------------------
//
void CGlxtnImageUtility::ScaleImageL(TRequestStatus& aStatus, CFbsBitmap& aSrcBitmap,
const TRect& aSrcRect, CFbsBitmap& aDstBitmap,
const TRect& aDstRect)
{
TRACER("void CGlxtnImageUtility::ScaleImageL(IHL)");
if (!iScaler)
{
TInt option = MIHLScaler::EOptionUseBilinearInterpolation;
iScaler = IHLScaler::CreateL(option);
}
User::LeaveIfError(iScaler->Scale(aStatus, aSrcBitmap, aSrcRect, aDstBitmap, aDstRect));
}
void CGlxtnImageUtility::ScaleImage64kL(TRequestStatus* aStatus, CFbsBitmap* aSource, CFbsBitmap* aFilteredSource, CFbsBitmap* aTarget)
{
TRACER("void CGlxtnImageUtility::ScaleImage64kL()");
TSize sourceSize(aSource->SizeInPixels());
TSize targetSize(aTarget->SizeInPixels());
TInt scalePercent = ((100*sourceSize.iWidth)/targetSize.iWidth) - 100;
TRect targetRect(targetSize);
TRect sourceRect(sourceSize);
CFbsBitmap* sourceImage = aFilteredSource;
if ( !aFilteredSource )
{
sourceImage = aSource;
}
if ( scalePercent < 2 )
{
if ( scalePercent > 0 )
{
sourceRect = targetRect;
}
sourceImage = aSource;
}
sourceImage->LockHeap();
TUint16* sourceAddress = (TUint16*)sourceImage->DataAddress();
sourceImage->UnlockHeap();
aTarget->LockHeap();
TUint16* targetAddress = (TUint16*)aTarget->DataAddress();
aTarget->UnlockHeap();
ScaleColor64K(sourceAddress, sourceImage->DataStride(),
sourceImage->SizeInPixels().iWidth, sourceImage->SizeInPixels().iHeight, 0, 0, sourceImage->SizeInPixels().iWidth, sourceImage->SizeInPixels().iHeight,
targetAddress, aTarget->DataStride(),
targetSize.iWidth, targetSize.iHeight);
/* if ( !iBitGc )
{
iBitGc = CFbsBitGc::NewL();
}
CFbsBitmapDevice *bitmapDevice = CFbsBitmapDevice::NewL(aTarget);
CleanupStack::PushL(bitmapDevice);
iBitGc->Activate(bitmapDevice);
iBitGc->DrawBitmap(targetRect, sourceImage, sourceRect);
CleanupStack::PopAndDestroy(bitmapDevice);
*/
*aStatus = KRequestPending;
User::RequestComplete(aStatus, KErrNone);
}
void CGlxtnImageUtility::ScaleColor64K( TUint16* aSrc, TInt aSrcStride, TInt /*aSrcCols*/, TInt /*aSrcRows*/,
TInt aX, TInt aY, TInt aW, TInt aH,
TUint16* aDst, TInt aDstStride, TInt aDstCols, TInt aDstRows )
{
TRACER("void CGlxtnImageUtility::ScaleColor64K()");
const TUint KPrecision = 16;
TUint hInc = ( aW<< KPrecision ) / aDstCols;
TUint vInc = ( aH << KPrecision ) / aDstRows;
TUint v = 0;
for ( TUint row = aDstRows; row > 0; --row )
{
TInt h = hInc * (aDstCols - 1);
TUint linestart = aSrcStride * ( aY + ( v >> KPrecision ) ) / sizeof(TUint16) + aX;
TUint16* src = &aSrc[ linestart ];
TUint32* dst = (TUint32*)aDst + aDstCols / 2;
TUint32 pxl2x;
// This loop generates 11 assembly instructions per round
// when using "--arm --Otime" options. Since two pixels are handled per round,
// it means 5.5 instructions per pixel on average.
do {
pxl2x = src[ h >> KPrecision ]<<16;
h -= hInc;
pxl2x |= src[ h >> KPrecision ];
*--dst = pxl2x;
// Compiler eliminates CMP instruction when substraction
// is done inside the while () statement.
} while ( ( h -= hInc ) >= 0 );
v += vInc;
aDst += aDstStride / sizeof(TUint16);
}
}
void CGlxtnImageUtility::FilterL( CFbsBitmap* aSource, CFbsBitmap*& aFilteredSource, TInt aFilterPercent )
{
TRACER("void CGlxtnImageUtility::FilterL()");
if ( !aFilteredSource )
{
TSize imageSize = aSource->SizeInPixels();
aFilteredSource = new (ELeave) CFbsBitmap;
aFilteredSource->Create(imageSize, aSource->DisplayMode());
CFbsBitmapDevice *bitmapDevice = CFbsBitmapDevice::NewL(aFilteredSource);
if ( !iBitGc )
{
iBitGc = CFbsBitGc::NewL();
}
iBitGc->Activate(bitmapDevice);
iBitGc->BitBlt(TPoint(), aSource, imageSize);
delete bitmapDevice;
}
TSize imageSize = aFilteredSource->SizeInPixels();
aFilteredSource->LockHeap();
TUint16* sourceAddress = (TUint16*)aFilteredSource->DataAddress();
aFilteredSource->UnlockHeap();
const TInt KGlxScalingNeeds4Filtering = 80; // if scaling more than 1.8 need to filter by 4
const TInt KGlxScalingNeeds8Filtering = 260; // if scaling more than 3.6 need to filter by 8
if ( KGlxScalingNeeds8Filtering < aFilterPercent )
{
FIRFiltering8(
sourceAddress, aFilteredSource->DataStride() / sizeof(TUint16),
imageSize.iWidth, imageSize.iHeight );
}
else if ( KGlxScalingNeeds4Filtering < aFilterPercent )
{
FIRFiltering4(
sourceAddress, aFilteredSource->DataStride() / sizeof(TUint16),
imageSize.iWidth, imageSize.iHeight );
}
else
{
FIRFiltering(
sourceAddress, aFilteredSource->DataStride() / sizeof(TUint16),
imageSize.iWidth, imageSize.iHeight );
}
}
#define rmask565 0xf800
#define gmask565 0x07e0
#define bmask565 0x001f
#define rbmask565 (rmask565|bmask565)
#define rm565( rgb ) ((rgb) & rmask565)
#define gm565( rgb ) ((rgb) & gmask565)
#define bm565( rgb ) ((rgb) & bmask565)
#define rbm565( rgb ) ((rgb) & rbmask565)
#define r565( rgb ) ((rgb)>>11)
#define g565( rgb ) (gm565(rgb)>>5)
#define b565( rgb ) (bm565(rgb))
#define rgb565( r, g, b ) (((r)<<11)|((g)<<5)|(b))
#define mask32gbr655 0x07e0f81f
// Keep below three defs in sync with each other!
#define KFIRLen 2
#define KFIRCen (KFIRLen / 2)
#define incFIRIndex( i ) i = (i + 1) & (KFIRLen - 1)
// Keep above three defs in sync with each other!
void CGlxtnImageUtility::FIRFiltering(
TUint16* aDst, TUint aDstStridep, TUint aDstCols, TUint aDstRows )
{
TRACER("void CGlxtnImageUtility::FIRFiltering( )");
TUint firFifo[ KFIRLen ];
TUint i; // index for fifo in&out
TUint16 *p;
TUint32 px;
TInt row, col;
TUint FIRsum;
// Horizontal scan.
p = aDst;
for ( row = aDstRows - 1; row >= 0; row-- )
{
// read for cache
//for ( col = aDstCols - 1; col >= 0; col-- ) TInt temp = p[ col ];
// Fill in the FIR first.
// TODO: Fill in with extrapolated values at edges!
FIRsum = ((KFIRLen / 2)<<21) | ((KFIRLen / 2)<<11) | (KFIRLen / 2); // for correct rounding
i = 0;
TUint32 mask1 = mask32gbr655;
for ( col = 0; col < KFIRLen; col++ )
{
px = p[ col ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex( i );
}
TUint32 ave;
for ( ; col < aDstCols; col++ )
{
ave = ( FIRsum / KFIRLen ) & mask1;
p[ col - KFIRCen ] = TUint16( ave | (ave>>16) );
FIRsum -= firFifo[ i ];
px = p[ col ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex( i );
}
p += aDstStridep;
}
// Vertical scan.
for ( col = aDstCols - 1; col >= 0; col-- )
{
// Fill in the FIR first.
FIRsum = ((KFIRLen / 2)<<21) | ((KFIRLen / 2)<<11) | (KFIRLen / 2); // for correct rounding
TUint32 mask1 = mask32gbr655;
i = 0;
p = aDst + col;
for ( row = 0; row < KFIRLen; row++ )
{
px = *p;
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex( i );
p += aDstStridep;
}
TUint32 ave;
p -= aDstStridep * KFIRCen;
for ( ; row < aDstRows; row++ )
{
ave = ( FIRsum / KFIRLen ) & mask1;
p[0] = TUint16( ave | (ave>>16) );
FIRsum -= firFifo[ i ];
px = p[ aDstStridep * KFIRCen ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex( i );
p += aDstStridep;
}
}
}
//
// Keep below three defs in sync with each other!
#define KFIRLen4 4
#define KFIRCen4 (KFIRLen4 / 2)
#define incFIRIndex4( i ) i = (i + 1) & (KFIRLen4 - 1)
// Keep above three defs in sync with each other!
void CGlxtnImageUtility::FIRFiltering4(
TUint16* aDst, TUint aDstStridep, TUint aDstCols, TUint aDstRows )
{
TRACER("void CGlxtnImageUtility::FIRFiltering4()");
TUint firFifo[ KFIRLen4 ];
TUint i; // index for fifo in&out
TUint16 *p;
TUint32 px;
TInt row, col;
TUint FIRsum;
// Horizontal scan.
p = aDst;
for ( row = aDstRows - 1; row >= 0; row-- )
{
// read for cache
//for ( col = aDstCols - 1; col >= 0; col-- ) TInt temp = p[ col ];
// Fill in the FIR first.
// TODO: Fill in with extrapolated values at edges!
FIRsum = ((KFIRLen4 / 2)<<21) | ((KFIRLen4 / 2)<<11) | (KFIRLen4 / 2); // for correct rounding
i = 0;
TUint32 mask1 = mask32gbr655;
for ( col = 0; col < KFIRLen4; col++ )
{
px = p[ col ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex4( i );
}
TUint32 ave;
for ( ; col < aDstCols; col++ )
{
ave = ( FIRsum / KFIRLen4 ) & mask1;
p[ col - KFIRCen4 ] = TUint16( ave | (ave>>16) );
FIRsum -= firFifo[ i ];
px = p[ col ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex4( i );
}
p += aDstStridep;
}
// Vertical scan.
for ( col = aDstCols - 1; col >= 0; col-- )
{
// Fill in the FIR first.
FIRsum = ((KFIRLen4 / 2)<<21) | ((KFIRLen4 / 2)<<11) | (KFIRLen4 / 2); // for correct rounding
TUint32 mask1 = mask32gbr655;
i = 0;
p = aDst + col;
for ( row = 0; row < KFIRLen4; row++ )
{
px = *p;
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex4( i );
p += aDstStridep;
}
TUint32 ave;
p -= aDstStridep * KFIRCen4;
for ( ; row < aDstRows; row++ )
{
ave = ( FIRsum / KFIRLen4 ) & mask1;
p[0] = TUint16( ave | (ave>>16) );
FIRsum -= firFifo[ i ];
px = p[ aDstStridep * KFIRCen4 ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex4( i );
p += aDstStridep;
}
}
}
// Keep below three defs in sync with each other!
#define KFIRLen8 8
#define KFIRCen8 (KFIRLen8 / 2)
#define incFIRIndex8( i ) i = (i + 1) & (KFIRLen8 - 1)
// Keep above three defs in sync with each other!
void CGlxtnImageUtility::FIRFiltering8(
TUint16* aDst, TUint aDstStridep, TUint aDstCols, TUint aDstRows )
{
TRACER("void CGlxtnImageUtility::FIRFiltering8()");
TUint firFifo[ KFIRLen8 ];
TUint i; // index for fifo in&out
TUint16 *p;
TUint32 px;
TInt row, col;
TUint FIRsum;
// Horizontal scan.
p = aDst;
for ( row = aDstRows - 1; row >= 0; row-- )
{
// read for cache
//for ( col = aDstCols - 1; col >= 0; col-- ) TInt temp = p[ col ];
// Fill in the FIR first.
// TODO: Fill in with extrapolated values at edges!
FIRsum = ((KFIRLen8 / 2)<<21) | ((KFIRLen8 / 2)<<11) | (KFIRLen8 / 2); // for correct rounding
i = 0;
TUint32 mask1 = mask32gbr655;
for ( col = 0; col < KFIRLen8; col++ )
{
px = p[ col ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex8( i );
}
TUint32 ave;
for ( ; col < aDstCols; col++ )
{
ave = ( FIRsum / KFIRLen8 ) & mask1;
p[ col - KFIRCen8 ] = TUint16( ave | (ave>>16) );
FIRsum -= firFifo[ i ];
px = p[ col ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex8( i );
}
p += aDstStridep;
}
// Vertical scan.
for ( col = aDstCols - 1; col >= 0; col-- )
{
// Fill in the FIR first.
FIRsum = ((KFIRLen8 / 2)<<21) | ((KFIRLen8 / 2)<<11) | (KFIRLen8 / 2); // for correct rounding
TUint32 mask1 = mask32gbr655;
i = 0;
p = aDst + col;
for ( row = 0; row < KFIRLen8; row++ )
{
px = *p;
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex8( i );
p += aDstStridep;
}
TUint32 ave;
p -= aDstStridep * KFIRCen8;
for ( ; row < aDstRows; row++ )
{
ave = ( FIRsum / KFIRLen8 ) & mask1;
p[0] = TUint16( ave | (ave>>16) );
FIRsum -= firFifo[ i ];
px = p[ aDstStridep * KFIRCen8 ];
px = ((px<<16) | px) & mask1;
firFifo[ i ] = px;
FIRsum += px;
incFIRIndex8( i );
p += aDstStridep;
}
}
}