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