--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mobilemessaging/unieditor/utils/src/UniImageProcessor.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,922 @@
+/*
+* Copyright (c) 2006-2007 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:
+* Unified Message Editor - Combined image scaler & compressor
+*
+*/
+
+
+
+// INCLUDE FILES
+#include "UniImageProcessor.h"
+
+#include <imageconversion.h>
+#include <bitmaptransforms.h>
+
+#include <MsgMimeTypes.h>
+
+#include "UniEditorLogging.h"
+
+
+// ==========================================================
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES
+
+// CONSTANTS
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// JPEG quality factor related constants:
+const TInt KHighQualityMaxWidth = 640;
+const TInt KHighQualityMaxHeight = 480;
+const TInt KJpegQualityFactorHigh = 80;
+const TInt KJpegQualityFactorNormal = 65;
+
+// MPix factor is relative to this value
+// 3MPix: Initial factor 2/3 * Qfactor calculated above
+// 5MPix: 2/5*
+// 8MPix: 2/8*
+const TReal KQFMPixfactor( 1600*1200 );
+
+// Count of compressing/scaling efforts
+const TInt KIterationCount( 3 );
+
+const TInt KMPixfactorMin = 1;
+const TInt KMPixfactorMax = 5;
+const TInt KMPixSizeDivider = 4;
+
+// When compressing next QFactor is QFactor * (value from table) / 100
+const TInt KPercentCoefficientsCounts[KMPixfactorMax][KIterationCount] =
+ { { 60, 30, 10 },
+ { 40, 15, 3 },
+ { 25, 10, 2 },
+ { 12, 4, 0 },
+ { 5, 2, 0 }
+ };
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// ================= MEMBER FUNCTIONS =======================
+
+// ---------------------------------------------------------
+// CUniImageProcessor::CUniImageProcessor
+//
+// Constructor.
+// ---------------------------------------------------------
+//
+EXPORT_C CUniImageProcessor::CUniImageProcessor( MUniImageProcessorCallback* aCallback ) :
+ CActive( EPriorityStandard ),
+ iCallback( aCallback ),
+ iMPixFactor ( KMPixfactorMin )
+ {
+ CActiveScheduler::Add( this );
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::~CUniImageProcessor
+//
+// Destructor
+// ---------------------------------------------------------
+//
+CUniImageProcessor::~CUniImageProcessor()
+ {
+ Cancel();
+ Reset();
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::ProcessImageL
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CUniImageProcessor::ProcessImageL(
+ RFile& aSourceFile,
+ RFile& aDestFile,
+ TSize& aTargetSize,
+ const TDesC8& aTargetType,
+ TBool aAspectRatio,
+ TInt aTargetFileSize )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: file-to-file scaling" );
+
+ if ( iDecoder || iScaler || iEncoder )
+ {
+ User::Leave( KErrInUse );
+ }
+
+ iError = KErrNone;
+ iFlags &= ~ECompressOnly;
+ if ( aTargetType.CompareF( KMsgMimeImageJpeg ) == 0 )
+ {
+ iCompressTriesLeft = KIterationCount;
+ User::LeaveIfError( aSourceFile.Size( iOriginalFileSize ) );
+ // Make sure the resulting image won't be larger than the original
+ if ( !aTargetFileSize || iOriginalFileSize < aTargetFileSize )
+ {
+ aTargetFileSize = iOriginalFileSize;
+ iFlags |= EImplicitCompress;
+ }
+ iTargetFileSize = aTargetFileSize;
+ }
+ else
+ {
+ iCompressTriesLeft = 0;
+ iTargetFileSize = 0;
+ iOriginalFileSize = 0;
+ }
+
+ iQFactorCalculated = EFalse;
+ iMPixFactor = KMPixfactorMin;
+ iDestFile = &aDestFile;
+ iAspectRatio = aAspectRatio;
+ iDecoder = CImageDecoder::FileNewL( aSourceFile, ContentAccess::EPeek );
+ iEncoder = CImageEncoder::FileNewL( aDestFile, aTargetType );
+
+ const TFrameInfo& frameInfo = iDecoder->FrameInfo();
+
+ TSize decodeSize( frameInfo.iOverallSizeInPixels );
+ if ( !CalculateDecodeAndTargetSizes( aTargetSize, decodeSize, aAspectRatio ) )
+ {
+ // use bitmap plane scale
+ iScaler = CBitmapScaler::NewL();
+ iSourceBitmap = new ( ELeave ) CFbsBitmap;
+ iFlags |= ESourceBitmapOwned;
+ User::LeaveIfError( iSourceBitmap->Create(
+ decodeSize,
+ frameInfo.iFrameDisplayMode ) );
+ }
+
+ iDestBitmap = new ( ELeave ) CFbsBitmap;
+ iFlags |= EDestBitmapOwned;
+ User::LeaveIfError( iDestBitmap->Create(
+ aTargetSize,
+ frameInfo.iFrameDisplayMode ) );
+
+ CreateImageDataL( aTargetType, aTargetSize );
+
+ DecodeImage();
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::ProcessImageL
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CUniImageProcessor::ScaleImageL(
+ CFbsBitmap* aSourceBitmap,
+ RFile& aDestFile,
+ const TSize& aTargetSize,
+ const TDesC8& aTargetType,
+ TBool aAspectRatio )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: bmp-to-file scaling" );
+ if ( iDecoder || iScaler || iEncoder )
+ {
+ User::Leave( KErrInUse );
+ }
+
+ iError = KErrNone;
+ iFlags &= ~ECompressOnly;
+ iCompressTriesLeft = 0;
+ iTargetFileSize = 0;
+ iOriginalFileSize = 0;
+
+ iQFactorCalculated = EFalse;
+ iMPixFactor = KMPixfactorMin;
+ iDestFile = &aDestFile;
+ iAspectRatio = aAspectRatio;
+ iScaler = CBitmapScaler::NewL();
+ iEncoder = CImageEncoder::FileNewL( aDestFile, aTargetType );
+
+ iSourceBitmap = aSourceBitmap;
+
+ iDestBitmap = new ( ELeave ) CFbsBitmap;
+ iFlags |= EDestBitmapOwned;
+ TInt error = iDestBitmap->Create(
+ aTargetSize,
+ iSourceBitmap->DisplayMode() );
+ User::LeaveIfError( error );
+
+ CreateImageDataL( aTargetType, aTargetSize );
+
+ ScaleImage( EFalse );
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::ScaleImageL
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CUniImageProcessor::ScaleImageL(
+ CFbsBitmap* aSourceBitmap,
+ CFbsBitmap* aDestBitmap,
+ TBool aAspectRatio )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: bmp-to-bmp scaling" );
+ if ( iDecoder || iScaler || iEncoder )
+ {
+ User::Leave( KErrInUse );
+ }
+
+ iError = KErrNone;
+ iFlags &= ~ECompressOnly;
+ iCompressTriesLeft = 0;
+ iTargetFileSize = 0;
+ iOriginalFileSize = 0;
+
+ iAspectRatio = aAspectRatio;
+ iScaler = CBitmapScaler::NewL();
+
+ iSourceBitmap = aSourceBitmap;
+ iDestBitmap = aDestBitmap;
+
+ ScaleImage( EFalse );
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::ScaleImageL
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CUniImageProcessor::ScaleImageL(
+ RFile& aSourceFile,
+ CFbsBitmap*& aDestBitmap,
+ CFbsBitmap*& aDestMask,
+ TSize& aTargetSize,
+ TBool aAspectRatio )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: file-to-bmp scaling" );
+ if ( iDecoder || iScaler || iEncoder )
+ {
+ User::Leave( KErrInUse );
+ }
+
+ iError = KErrNone;
+ iFlags &= ~ECompressOnly;
+ iCompressTriesLeft = 0;
+ iTargetFileSize = 0;
+ iOriginalFileSize = 0;
+
+ iQFactorCalculated = EFalse;
+ iMPixFactor = KMPixfactorMin;
+ iAspectRatio = aAspectRatio;
+ iDecoder = CImageDecoder::FileNewL( aSourceFile, ContentAccess::EPeek );
+
+ const TFrameInfo& frameInfo = iDecoder->FrameInfo();
+
+ TSize decodeSize( frameInfo.iOverallSizeInPixels );
+ TBool optimum = CalculateDecodeAndTargetSizes( aTargetSize, decodeSize, aAspectRatio );
+ // Make sure "compress only flag is not set".
+ iFlags &= ~ECompressOnly;
+
+ if ( !optimum )
+ {
+ // use bitmap plane scale
+ iScaler = CBitmapScaler::NewL();
+ iSourceBitmap = new ( ELeave ) CFbsBitmap;
+ iFlags |= ESourceBitmapOwned;
+ User::LeaveIfError( iSourceBitmap->Create(
+ decodeSize,
+ frameInfo.iFrameDisplayMode ) );
+ }
+
+ iDestBitmap = new ( ELeave ) CFbsBitmap;
+ iFlags |= EDestBitmapOwned;
+ User::LeaveIfError( iDestBitmap->Create(
+ aTargetSize,
+ frameInfo.iFrameDisplayMode ) );
+
+ if ( frameInfo.iFlags & TFrameInfo::ETransparencyPossible )
+ {
+ if ( !optimum )
+ {
+ iSourceMask = new ( ELeave ) CFbsBitmap;
+ iFlags |= ESourceMaskOwned;
+ User::LeaveIfError( iSourceMask->Create(
+ decodeSize,
+ frameInfo.iFlags & TFrameInfo::EAlphaChannel ? EGray256 : EGray2 ) );
+ }
+ iDestMask = new ( ELeave ) CFbsBitmap;
+ iFlags |= EDestMaskOwned;
+ User::LeaveIfError( iDestMask->Create(
+ aTargetSize,
+ frameInfo.iFlags & TFrameInfo::EAlphaChannel ? EGray256 : EGray2 ) );
+ delete aDestMask; // just in case
+ aDestMask = iDestMask;
+ }
+ else
+ {
+ delete aDestMask; // just in case
+ aDestMask = NULL;
+ }
+
+ delete aDestBitmap; //just in case
+ aDestBitmap = iDestBitmap;
+
+ iFlags &= ~EDestBitmapOwned;
+ iFlags &= ~EDestMaskOwned;
+
+ DecodeImage();
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::CalculateDecodeAndTargetSizes
+//
+// ---------------------------------------------------------
+//
+TBool CUniImageProcessor::CalculateDecodeAndTargetSizes(
+ TSize& aTargetSize,
+ TSize& aDecodeSize,
+ TBool aAspectRatio )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: CalculateDecodeAndTargetSizes()" );
+
+ const TFrameInfo& frameInfo = iDecoder->FrameInfo();
+ aDecodeSize = frameInfo.iOverallSizeInPixels;
+
+ if ( aTargetSize.iWidth >= aDecodeSize.iWidth &&
+ aTargetSize.iHeight >= aDecodeSize.iHeight &&
+ !( iFlags & EAllowScalingUp ) )
+ {
+ // No need to scale at all!
+ iFlags |= ECompressOnly;
+ aTargetSize = aDecodeSize;
+ return ETrue;
+ }
+ if ( aTargetSize == aDecodeSize )
+ {
+ // No need to scale at all!
+ iFlags |= ECompressOnly;
+ return ETrue;
+ }
+
+ if ( aAspectRatio )
+ {
+ // Adjust target size if aspect ratio needs to be maintained.
+ TReal scaleRatio = CalculateScaleRatio( aTargetSize, aDecodeSize );
+ aTargetSize.iHeight = scaleRatio * aDecodeSize.iHeight;
+ aTargetSize.iWidth = scaleRatio * aDecodeSize.iWidth;
+ }
+
+ // Width or height should never be zero.
+ aTargetSize.iWidth = Max(aTargetSize.iWidth, 1);
+ aTargetSize.iHeight = Max(aTargetSize.iHeight, 1);
+
+ TInt wRatio = aDecodeSize.iWidth / aTargetSize.iWidth;
+ TInt hRatio = aDecodeSize.iHeight / aTargetSize.iHeight;
+
+ TBool decodeOnly( frameInfo.iFlags & TFrameInfo::EFullyScaleable );
+
+ if ( !decodeOnly &&
+ !( aDecodeSize.iWidth % aTargetSize.iWidth ||
+ aDecodeSize.iHeight % aTargetSize.iHeight ||
+ wRatio != hRatio ||
+ (wRatio != 2 && wRatio != 4 && wRatio != 8) ||
+ (hRatio != 2 && hRatio != 4 && hRatio != 8) ) )
+ {
+ decodeOnly = ETrue;
+ }
+
+ if ( decodeOnly )
+ {
+ // Decoding directly to requested target size.
+ aDecodeSize = aTargetSize;
+ }
+ else
+ {
+ TInt decodeFactor = 8;
+ while ( decodeFactor > 1 )
+ {
+ if ( aDecodeSize.iWidth / decodeFactor >= aTargetSize.iWidth &&
+ aDecodeSize.iHeight / decodeFactor >= aTargetSize.iHeight )
+ {
+ // Smallest 1/2^n factor that creates image larger than target size
+ break;
+ }
+ decodeFactor /= 2;
+ }
+ if ( ( iFlags & EOnlyDecodeTimeScaling ) && decodeFactor < 8 )
+ {
+ // Largest 1/2^n factor that creates image smaller than target size
+ decodeFactor *= 2;
+ decodeOnly = ETrue;
+ }
+ aDecodeSize.iWidth = ( aDecodeSize.iWidth + decodeFactor - 1 ) / decodeFactor;
+ aDecodeSize.iHeight = ( aDecodeSize.iHeight + decodeFactor - 1 ) / decodeFactor;
+ if ( decodeOnly )
+ {
+ // Decoding to target size. Make sure target & decode sizes match.
+ // Target size may need to be changed was iOnlyDecodeTimeScaling is used.
+ aTargetSize = aDecodeSize;
+ }
+ }
+ return decodeOnly;
+ }
+
+
+// ---------------------------------------------------------
+// CUniImageProcessor::DecodeImage
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::DecodeImage()
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: DecodeImage()" );
+
+ iStatus = KRequestPending;
+ if ( iScaler )
+ {
+ if ( iSourceMask )
+ {
+ iDecoder->Convert( &iStatus, *iSourceBitmap, *iSourceMask );
+ }
+ else
+ {
+ iDecoder->Convert( &iStatus, *iSourceBitmap );
+ }
+ }
+ else
+ {
+ if ( iDestMask )
+ {
+ iDecoder->Convert( &iStatus, *iDestBitmap, *iDestMask );
+ }
+ else
+ {
+ iDecoder->Convert( &iStatus, *iDestBitmap );
+ }
+ }
+ SetActive();
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::ScaleImage
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::ScaleImage( TBool aMask )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: ScaleImage()" );
+
+ iStatus = KRequestPending;
+ if ( aMask )
+ {
+ iScaler->Scale( &iStatus, *iSourceMask, *iDestMask, iAspectRatio );
+ }
+ else
+ {
+ iScaler->Scale( &iStatus, *iSourceBitmap, *iDestBitmap, iAspectRatio );
+ }
+ SetActive();
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::EncodeImageL
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::EncodeImageL( TBool aCompressing )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: EncodeImageL()" );
+
+ if ( aCompressing )
+ {
+ delete iEncoder;
+ iEncoder = NULL;
+ // Make sure that the file is empty.
+ User::LeaveIfError( iDestFile->SetSize( 0 ) );
+ iEncoder = CImageEncoder::FileNewL( *iDestFile, KMsgMimeImageJpeg );
+ }
+ iCompressTriesLeft--;
+ iStatus = KRequestPending;
+ iEncoder->Convert( &iStatus, *iDestBitmap, iFrameImageData );
+ SetActive();
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::DoCancel
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::DoCancel()
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: DoCancel()" );
+ if ( iDecoder )
+ {
+ iDecoder->Cancel();
+ }
+ if ( iScaler )
+ {
+ iScaler->Cancel();
+ }
+ if ( iEncoder )
+ {
+ iEncoder->Cancel();
+ }
+
+ iError = KErrCancel;
+ iCallback->ImageProcessingReady( TSize(), 0, EFalse );
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::RunL
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::RunL()
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: RunL()" );
+ UNILOGGER_WRITEF( _L("iStatus: %d"), iStatus.Int() );
+ TSize size( 0, 0 );
+ if ( iStatus < 0 )
+ {
+ iError = iStatus.Int();
+ }
+ if ( iError )
+ {
+ UNILOGGER_WRITE( "Error occured. Scaling aborted." );
+ TBool compressed( EFalse );
+ if ( iTargetFileSize &&
+ iCompressTriesLeft != KIterationCount &&
+ !( iFlags & EImplicitCompress ) )
+ {
+ compressed = ETrue;
+ }
+ Reset();
+ iCallback->ImageProcessingReady( size, 0, compressed );
+ }
+ else if ( iDecoder )
+ {
+ delete iDecoder;
+ iDecoder = NULL;
+ if ( iScaler )
+ {
+ if ( iSourceMask )
+ {
+ //scale mask first
+ ScaleImage( ETrue );
+ }
+ else
+ {
+ ScaleImage( EFalse );
+ }
+ }
+ else if ( iEncoder )
+ {
+ if ( iFlags & ECompressOnly )
+ {
+ ResolveQFactorL();
+ }
+ // In "compress only" case the "aCompressing" flag is
+ // EFalse for first time because we do not want
+ // to delete & recreate CImageEncoder unnecessarily.
+ EncodeImageL( EFalse );
+ }
+ else
+ {
+ UNILOGGER_WRITE( "Scaling ready (no scaling nor endocing)." );
+ size = iDestBitmap->SizeInPixels();
+ Reset();
+ iCallback->ImageProcessingReady( size, iResultFileSize, EFalse );
+ }
+ }
+ else if ( iScaler )
+ {
+ if ( iSourceMask )
+ {
+ delete iSourceMask;
+ iSourceMask = NULL;
+ iFlags &= ~ESourceMaskOwned;
+ //mask already scaled, scale bitmap
+ ScaleImage( EFalse );
+ }
+ else
+ {
+ delete iScaler;
+ iScaler = NULL;
+ if ( iEncoder )
+ {
+ EncodeImageL( EFalse );
+ }
+ else
+ {
+ UNILOGGER_WRITE( "Scaling ready (no endocing)." );
+ size = iDestBitmap->SizeInPixels();
+ Reset();
+ iCallback->ImageProcessingReady( size, iResultFileSize, EFalse );
+ }
+ }
+ }
+ else
+ {
+ UNILOGGER_WRITE( "Scaling ready." );
+ if ( CheckEncodedFileSize() ||
+ ( iCompressTriesLeft <= 0 ) )
+ {
+ size = iDestBitmap->SizeInPixels();
+ TBool compressed( EFalse );
+ if ( iTargetFileSize &&
+ iCompressTriesLeft != KIterationCount &&
+ !( iFlags & EImplicitCompress ) )
+ {
+ compressed = ETrue;
+ }
+ Reset();
+ iCallback->ImageProcessingReady( size, iResultFileSize, compressed );
+ }
+ else
+ {
+ ResolveQFactorL();
+ EncodeImageL( ETrue );
+ }
+ }
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::RunError
+//
+// ---------------------------------------------------------
+//
+TInt CUniImageProcessor::RunError( TInt aError )
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: RunError()" );
+ UNILOGGER_WRITEF( _L("aError: %d"), aError );
+ iError = aError;
+ Cancel();
+ //Complete self.
+ iStatus = KRequestPending;
+ TRequestStatus* pStatus = &iStatus;
+ SetActive();
+ User::RequestComplete( pStatus, KErrNone );
+ return KErrNone;
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::Reset
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CUniImageProcessor::Reset()
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: Reset()" );
+ iDestFile = NULL;
+
+ delete iDecoder;
+ iDecoder = NULL;
+
+ delete iScaler;
+ iScaler = NULL;
+
+ delete iEncoder;
+ iEncoder = NULL;
+
+ delete iFrameImageData;
+ iFrameImageData = NULL;
+
+ if ( iFlags & ESourceBitmapOwned )
+ {
+ delete iSourceBitmap;
+ }
+
+ iSourceBitmap = NULL;
+ iFlags &= ~ESourceBitmapOwned;
+
+ if ( iFlags & EDestBitmapOwned )
+ {
+ delete iDestBitmap;
+ }
+
+ iDestBitmap = NULL;
+ iFlags &= ~EDestBitmapOwned;
+
+ if ( iFlags & ESourceMaskOwned )
+ {
+ delete iSourceMask;
+ }
+
+ iSourceMask = NULL;
+ iFlags &= ~ESourceMaskOwned;
+
+ if ( iFlags & EDestMaskOwned )
+ {
+ delete iDestMask;
+ }
+
+ iDestMask = NULL;
+ iFlags &= ~EDestMaskOwned;
+ }
+
+
+// ---------------------------------------------------------
+// CUniImageProcessor::CreateImageDataL
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::CreateImageDataL( const TDesC8& aTargetType, const TSize& aTargetSize )
+ {
+ if ( aTargetType.CompareF( KMsgMimeImageJpeg ) == 0 )
+ {
+ TInt targetQF( KJpegQualityFactorNormal );
+ if ( aTargetSize.iWidth <= KHighQualityMaxWidth &&
+ aTargetSize.iHeight <= KHighQualityMaxHeight )
+ {
+ //Use higher quality factor for small images
+ targetQF = KJpegQualityFactorHigh;
+ }
+
+ // Use original image data if available
+ if ( iDecoder )
+ {
+ iFrameImageData = iDecoder->FrameData().AllocL();
+
+ TInt imageDataCount = iFrameImageData->ImageDataCount();
+ while ( imageDataCount-- )
+ {
+ TImageDataBlock* data = iFrameImageData->GetImageData( imageDataCount );
+ const TUid type = data->DataType();
+ if ( type == KJPGQTableUid )
+ {
+ TJpegQTable* qdata = static_cast<TJpegQTable*>( data );
+ if ( qdata->iTableIndex != TJpegQTable::ELumaTable &&
+ qdata->iTableIndex != TJpegQTable::EChromaTable )
+ {
+ // Jpeg encoder supports only two QTables. Others (probably
+ // another chroma table) must be removed. Otherwise Jpeg
+ // encoder leaves.
+ iFrameImageData->RemoveImageData( imageDataCount );
+ delete qdata;
+ }
+ }
+ else if ( type == KJPGImageDataUid )
+ {
+ iJpegImageData = static_cast<TJpegImageData*>( data );
+ // iSourceBitmap is NULL, if image is in right size
+ iOriginalQualityFactor = iJpegImageData->iQualityFactor;
+ if ( iSourceBitmap )
+ {
+ CalculateMPixfactorL();
+ iJpegImageData->iQualityFactor = iOriginalQualityFactor / iMPixFactor ;
+ iQFactorCalculated = ETrue;
+ }
+ }
+ }
+ }
+ if ( !iJpegImageData )
+ {
+ // Delete possibly existing iFrameImageData. There maybe one at least
+ // when converting PNG images to JPEG.
+ delete iFrameImageData;
+ iFrameImageData = NULL;
+ iFrameImageData = CFrameImageData::NewL();
+ TJpegImageData* jpegImageData = new ( ELeave ) TJpegImageData;
+ CleanupStack::PushL( jpegImageData );
+ jpegImageData->iSampleScheme = TJpegImageData::EColor420;
+
+ iOriginalQualityFactor = targetQF;
+ if ( iSourceBitmap )
+ {
+ iMPixFactor = Max ( KMPixfactorMin,
+ ( static_cast<TReal>( iSourceBitmap->SizeInPixels().iWidth ) *
+ static_cast<TReal>( iSourceBitmap->SizeInPixels().iHeight ) ) / KQFMPixfactor );
+ iMPixFactor = Min( iMPixFactor, KMPixfactorMax );
+ jpegImageData->iQualityFactor = iOriginalQualityFactor / iMPixFactor ;
+ iQFactorCalculated = ETrue;
+ }
+ else
+ {
+ jpegImageData->iQualityFactor = iOriginalQualityFactor ;
+ }
+ User::LeaveIfError( iFrameImageData->AppendImageData( jpegImageData ) );
+ iJpegImageData = jpegImageData;
+ CleanupStack::Pop(); // jpegFormat, ownership transferred
+ }
+ }
+ }
+
+// ----------------------------------------------------------------------------
+// CUniImageProcessor::CalculateScaleRatio
+//
+// Calculates correct scaling ratio when aspect ratio needs to be preserved.
+// Correct scaling ratio is the smaller of the width and height original image
+// size and target image size ratios.
+// ----------------------------------------------------------------------------
+//
+TReal CUniImageProcessor::CalculateScaleRatio( const TSize& aTargetSize,
+ const TSize& aOriginalSize ) const
+ {
+ TReal widthRatio = static_cast<TReal>( aTargetSize.iWidth ) /
+ static_cast<TReal>( aOriginalSize.iWidth );
+ TReal heightRatio = static_cast<TReal>( aTargetSize.iHeight ) /
+ static_cast<TReal>( aOriginalSize.iHeight );
+ return ( widthRatio < heightRatio ) ? widthRatio : heightRatio;
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::ResolveQFactorL
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::ResolveQFactorL()
+ {
+ UNILOGGER_ENTERFN( "CUniImageProcessor: ResolveQFactorL()" );
+
+ // Leave if "JPGImageData" was not found.
+ if ( !iJpegImageData )
+ {
+ User::Leave( KErrNotSupported );
+ }
+
+ if ( !iQFactorCalculated ) // First try
+ {
+ if ( iSourceBitmap )
+ {
+ CalculateMPixfactorL();
+ iJpegImageData->iQualityFactor = iOriginalQualityFactor / iMPixFactor ;
+ }
+ else
+ {
+ iJpegImageData->iQualityFactor = iOriginalQualityFactor *
+ KPercentCoefficientsCounts[ iMPixFactor - 1 ][Min( KIterationCount-iCompressTriesLeft, KIterationCount )] / 100;
+
+ }
+ iQFactorCalculated = ETrue;
+ }
+ else
+ {
+ iJpegImageData->iQualityFactor = iOriginalQualityFactor *
+ KPercentCoefficientsCounts[ iMPixFactor - 1 ][Min( KIterationCount-iCompressTriesLeft, KIterationCount )] / 100;
+ }
+
+ if ( iJpegImageData->iQualityFactor <= 0 )
+ {
+ iJpegImageData->iQualityFactor = 0;
+ iCompressTriesLeft = 0; // No use to iterate more than this.
+ }
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::CheckEncodedFileSize
+//
+// ---------------------------------------------------------
+//
+TBool CUniImageProcessor::CheckEncodedFileSize()
+ {
+ TBool retVal = ETrue;
+
+ // Get encoded file size.
+ // Store possible error to "iError".
+ iError = iDestFile->Size( iResultFileSize );
+
+ if ( iTargetFileSize )
+ {
+ // Compression requested. Check whether the size is small enough.
+ retVal = ( iResultFileSize <= iTargetFileSize );
+ }
+ return retVal;
+ }
+
+// ---------------------------------------------------------
+// CUniImageProcessor::CalculateMPixfactorL
+//
+// ---------------------------------------------------------
+//
+void CUniImageProcessor::CalculateMPixfactorL()
+ {
+ if ( iSourceBitmap )
+ {
+ iMPixFactor = Max ( KMPixfactorMin,
+ ( static_cast<TReal>( iSourceBitmap->SizeInPixels().iWidth ) *
+ static_cast<TReal>( iSourceBitmap->SizeInPixels().iHeight ) ) / KQFMPixfactor );
+ }
+ if (iMPixFactor == KMPixfactorMin )
+ {
+ // Sometimes on the emulator bitmap dimensions show completely wrong values.
+ // Whether this happens also on the device is unknown
+ // Just in case use files size as second option
+ // As image size changes more rapidly as image dimension use extra divider
+ if ( iTargetFileSize >= 0 )
+ {
+ iMPixFactor = Max( KMPixfactorMin, ( iOriginalFileSize / iTargetFileSize ) / KMPixSizeDivider );
+ }
+ }
+ iMPixFactor = Min( iMPixFactor, KMPixfactorMax );
+ }
+
+// End of File