imagehandlingutilities/thumbnailmanager/thumbnailserver/src/thumbnailscaletask.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 15:18:05 +0200
changeset 0 2014ca87e772
child 5 82749d516180
child 14 2edacbf5d3f9
permissions -rw-r--r--
Revision: 201004

/*
* 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:  Task for scaling thumbnails.
 *
*/


#include <e32base.h>
#include <fbs.h>
#include <e32math.h>
#include <bitdev.h>
#include <bitstd.h>

#include "thumbnailscaletask.h"
#include "thumbnailprovider.h"
#include "thumbnailserver.h"
#include "thumbnailmanagerconstants.h"
#include "thumbnaillog.h"
#include "thumbnailpanic.h"


// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// CThumbnailScaleTask::NewL()
// Two-phased constructor.
// ---------------------------------------------------------------------------
//
CThumbnailScaleTask* CThumbnailScaleTask::NewL( CThumbnailTaskProcessor&
    aProcessor, CThumbnailServer& aServer, const TDesC& aFilename, CFbsBitmap*
    aBitmap, const TSize& aOriginalSize, const TSize& aTargetSize, TBool aCrop,
    TDisplayMode aDisplayMode, TInt aPriority, const TDesC& aTargetUri,
    const TThumbnailSize aThumbnailSize, const TThumbnailId aThumbnailId,
    TBool aBitmapToPool, const TBool aEXIF)
    {
    // We take ownership of aBitmap
    CleanupStack::PushL( aBitmap );
    CThumbnailScaleTask* self = new( ELeave )CThumbnailScaleTask( aProcessor,
        aServer, aFilename, aBitmap, aOriginalSize, aTargetSize, aCrop,
        aDisplayMode, aPriority, aTargetUri, aThumbnailSize, aThumbnailId,
        aBitmapToPool, aEXIF);
    CleanupStack::Pop( aBitmap );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }


// ---------------------------------------------------------------------------
// CThumbnailScaleTask::CThumbnailScaleTask()
// C++ default constructor can NOT contain any code, that might leave.
// ---------------------------------------------------------------------------
//
CThumbnailScaleTask::CThumbnailScaleTask( CThumbnailTaskProcessor& aProcessor,
    CThumbnailServer& aServer, const TDesC& aFilename, CFbsBitmap* aBitmap,
    const TSize& aOriginalSize, const TSize& aTargetSize, TBool aCrop,
    TDisplayMode aDisplayMode, TInt aPriority, const TDesC& aTargetUri,
    const TThumbnailSize aThumbnailSize, const TThumbnailId aThumbnailId,
    TBool aBitmapToPool, const TBool aEXIF):
    CThumbnailTask( aProcessor, aPriority ), iServer( aServer ), iOwnBitmap( aBitmap ),
    iOriginalSize( aOriginalSize ), iTargetSize( aTargetSize ), iCrop( aCrop ),
    iDisplayMode( aDisplayMode ), iFilename( aFilename ), iTargetUri( aTargetUri ),
    iThumbnailSize(aThumbnailSize), iThumbnailId(aThumbnailId),
    iBitmapToPool(aBitmapToPool), iEXIF(aEXIF)
    {
    TN_DEBUG2( "CThumbnailScaleTask(0x%08x)::CThumbnailScaleTask()", this );
    }


// ---------------------------------------------------------------------------
// CThumbnailScaleTask::ConstructL()
// Symbian 2nd phase constructor can leave.
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::ConstructL()
    {
    iServer.AddBitmapToPoolL( NULL, iOwnBitmap );

    // Successfully added bitmap to pool, we are no longer responsible for
    // deleting it directly.
    iBitmap = iOwnBitmap;
    iOwnBitmap = NULL;
    iBitmapInPool = ETrue;
    
    iScaledBitmap = NULL;
    iScaledBitmapHandle = 0;
    }


// ---------------------------------------------------------------------------
// CThumbnailScaleTask::~CThumbnailScaleTask()
// Destructor.
// ---------------------------------------------------------------------------
//
CThumbnailScaleTask::~CThumbnailScaleTask()
    {
    iServer.CancelScale();
    
    if ( iBitmapInPool && iBitmap )
        {
        TN_DEBUG1("CThumbnailScaleTask()::~CThumbnailScaleTask() delete original bitmap from pool");
        
        // Original bitmap is owned by server, decrease reference count
        iServer.DeleteBitmapFromPool( iBitmap->Handle());
        }

    if ( iScaledBitmapHandle )
        {
        TN_DEBUG1("CThumbnailScaleTask()::~CThumbnailScaleTask() delete scaled bitmap from pool");
        
        // Scaled bitmap is owned by server, decrease reference count
        iServer.DeleteBitmapFromPool( iScaledBitmapHandle );
        }

    // Scaled bitmap is owned by us, delete now
    delete iScaledBitmap;
    }


// ---------------------------------------------------------------------------
// CThumbnailScaleTask::StartL()
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::StartL()
    {
    TN_DEBUG2( "CThumbnailScaleTask(0x%08x)::StartL()", this );

    CThumbnailTask::StartL();

    if ( !iCrop )
        {
        // target size at max, keep aspect ratio
        CalculateTargetSize();
        }
    else
        {
        // exact target size, crop excess
        CalculateCropRectangle();
        }
    
#ifdef _DEBUG
    aStart.UniversalTime();
#endif
    
    delete iScaledBitmap;
    iScaledBitmap = NULL;
    iScaledBitmap = new( ELeave )CFbsBitmap();
    
    TSize bitmapSize = iBitmap->SizeInPixels();
    
    if(bitmapSize.iHeight == iTargetSize.iHeight && bitmapSize.iWidth == iTargetSize.iWidth)
        {
        // copy bitmap 1:1
        User::LeaveIfError( iScaledBitmap->Create( bitmapSize, iBitmap->DisplayMode() ));
        CFbsBitmapDevice* device = CFbsBitmapDevice::NewL(iScaledBitmap);
        CleanupStack::PushL(device);
        CFbsBitGc* gc = NULL;
        User::LeaveIfError(device->CreateContext(gc));
        CleanupStack::PushL(gc);
        gc->BitBlt(TPoint(0, 0), iBitmap);
        CleanupStack::PopAndDestroy(2, device); // gc
        
        TN_DEBUG2( "CThumbnailScaleTask(0x%08x)::StartL() - no need for scaling", this);
        TRAPD( err, StoreAndCompleteL());
        Complete( err );
        ResetMessageData();
        }
    else
        {
        TN_DEBUG2( "CThumbnailScaleTask(0x%08x)::StartL() - scaling", this);
        User::LeaveIfError( iScaledBitmap->Create( iTargetSize, iBitmap->DisplayMode() ));
        iServer.ScaleBitmapL( iStatus, * iBitmap, * iScaledBitmap, iCropRectangle );
        SetActive();
        }
   
    }


// ---------------------------------------------------------------------------
// CThumbnailScaleTask::RunL()
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::RunL()
    {
    TInt err = iStatus.Int();

    TN_DEBUG3( "CThumbnailScaleTask(0x%08x)::RunL() err=%d)", this, err );

    #ifdef _DEBUG
    aStop.UniversalTime();
    TN_DEBUG2( "CThumbnailScaleTask::RunL() scale took %d ms", (TInt)aStop.MicroSecondsFrom(aStart).Int64()/1000);
    #endif

    if ( !err )
        {
        TRAP( err, StoreAndCompleteL());
        }
    
    Complete( err );
    ResetMessageData();
    }


// ---------------------------------------------------------------------------
// CThumbnailScaleTask::DoCancel()
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::DoCancel()
    {
    TN_DEBUG2( "CThumbnailScaleTask(0x%08x)::DoCancel()", this );
    iServer.CancelScale();
    }


// ---------------------------------------------------------------------------
// Calculates target size to be used for the thumbnail
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::CalculateTargetSize()
    {
    __ASSERT_DEBUG( iOriginalSize.iHeight && iTargetSize.iHeight,
        ThumbnailPanic( EThumbnailBadSize ));
    
    if ( (iThumbnailSize == EFullScreenThumbnailSize ||
          iThumbnailSize == EImageFullScreenThumbnailSize ||
          iThumbnailSize == EVideoFullScreenThumbnailSize ||
          iThumbnailSize == EAudioFullScreenThumbnailSize) &&
          iOriginalSize.iHeight < iTargetSize.iHeight && 
          iOriginalSize.iWidth < iTargetSize.iWidth )
        {
        // do not upscale fullscreen thumbs
        iTargetSize = iOriginalSize;
        }
    else if ( iOriginalSize.iHeight && iTargetSize.iHeight )
        {
        const TReal32 srcAspect = static_cast < TReal32 > (
            iOriginalSize.iWidth ) / iOriginalSize.iHeight;

        // scale to maximum size within target size 
        if ( (iTargetSize.iHeight * srcAspect) <= iTargetSize.iWidth )
            {
            TReal trg = 0;
            TReal src( iTargetSize.iHeight * srcAspect );
            Math::Round( trg, src, 0 );
            iTargetSize.SetSize( trg, iTargetSize.iHeight );
            }
        else
            {
            TReal trg;
            TReal src( iTargetSize.iWidth / srcAspect );
            Math::Round( trg, src, 0 );
            iTargetSize.SetSize( iTargetSize.iWidth, trg );
            }
        }
    else
        {
        iTargetSize.SetSize( 0, 0 );
        }
    iCropRectangle.SetRect( TPoint(), iBitmap->SizeInPixels());
    }


// ---------------------------------------------------------------------------
// Calculates target size to be used for the thumbnail
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::CalculateCropRectangle()
    {
    const TSize srcSize = iBitmap->SizeInPixels();

    __ASSERT_DEBUG( srcSize.iHeight && iTargetSize.iHeight, ThumbnailPanic(
        EThumbnailBadSize ));

    if ( srcSize.iHeight && iTargetSize.iHeight )
        {
        const TReal32 srcAspect = static_cast < TReal32 > ( srcSize.iWidth ) /
            srcSize.iHeight;
        const TReal32 reqAspect = static_cast < TReal32 > ( iTargetSize.iWidth )
            / iTargetSize.iHeight;

        if ( (iTargetSize.iHeight * srcAspect) > iTargetSize.iWidth )
            {
            // Thumbnail is wider than requested and we crop
            // some of the right and left parts.
            TReal trg;
            TReal src( srcSize.iHeight* reqAspect );
            Math::Round( trg, src, 0 );
            const TSize cropSize( trg, srcSize.iHeight );
            iCropRectangle.iTl.SetXY(( srcSize.iWidth - cropSize.iWidth ) / 2, 0 );
            iCropRectangle.SetSize( cropSize );
            }
        else
            {
            // Thumbnail is taller than requested and we crop
            // some of the top and bottom parts.
            TReal trg;
            TReal src( srcSize.iWidth / reqAspect );
            Math::Round( trg, src, 0 );
            const TSize cropSize( srcSize.iWidth, trg );
            iCropRectangle.iTl.SetXY(0, ( srcSize.iHeight - cropSize.iHeight ) / 2 );
            iCropRectangle.SetSize( cropSize );
            }

        }
    else
        {
        iTargetSize.SetSize( 0, 0 );
        }
    }

// ---------------------------------------------------------------------------
// CThumbnailScaleTask::StoreAndCompleteL()
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::StoreAndCompleteL()
    {
    TN_DEBUG5( "CThumbnailScaleTask(0x%08x)::StoreAndCompleteL() iFilename=%S, iBitmap=0x%08x, iScaledBitmap=0x%08x)", 
               this, &iFilename, iBitmap, iScaledBitmap );
		 
    // do not store TN if quality is too low eg. orignal size of image is smaller than requested size
    // (do not store upscaled images)
    if ( iTargetSize.iWidth >= iOriginalSize.iWidth && 
         iTargetSize.iHeight >= iOriginalSize.iHeight && iEXIF)
        {
        TN_DEBUG1("CThumbnailScaleTask()::StoreAndCompleteL() too low quality");
        //don't store preview image
        iDoStore = EFalse;
        }
    
    TN_DEBUG3("CThumbnailScaleTask(0x%08x)::StoreAndCompleteL() iDoStore = %d", this, iDoStore);
    
    if ( iDoStore )
        {
        if (iTargetUri != KNullDesC)
            {
            if (iFilename != KNullDesC && iFilename.CompareF(iTargetUri) == 0)
                {
                // filename and target URI match, so thumb created from associated path
                iServer.StoreThumbnailL( iTargetUri, iScaledBitmap, iOriginalSize, iCrop, iThumbnailSize, iThumbnailId, ETrue );
                }
            else
                {
                // thumb not created from associated path
                iServer.StoreThumbnailL( iTargetUri, iScaledBitmap, iOriginalSize, iCrop, iThumbnailSize, iThumbnailId, EFalse, EFalse );
                }  
            }
        else if (iFilename != KNullDesC)
            {
            iServer.StoreThumbnailL( iFilename, iScaledBitmap, iOriginalSize, iCrop, iThumbnailSize, iThumbnailId, ETrue );
            }
        }    
    
    if ( iMessage.Handle() )
        {
        TN_DEBUG1("CThumbnailScaleTask()::StoreAndCompleteL() scaled bitmap handle to params");
        
    	TThumbnailRequestParams& params = iParamsBuf();
		iMessage.ReadL( 0, iParamsBuf );
		params.iBitmapHandle = iScaledBitmap->Handle();
	   
        // if need to add scaled bitmap to pool
        if (iBitmapToPool)
            {
            TN_DEBUG1("CThumbnailScaleTask()::StoreAndCompleteL() scaled bitmap to pool");
            
            iServer.AddBitmapToPoolL( iRequestId.iSession, iScaledBitmap );
            iScaledBitmapHandle = params.iBitmapHandle;
            }    
		
	    if( params.iQualityPreference == CThumbnailManager::EOptimizeForQualityWithPreview
	        && iEXIF && !iDoStore)
	        {
		    // this is upscaled preview image
	        params.iControlFlags = EThumbnailPreviewThumbnail;
	        TN_DEBUG1("CThumbnailScaleTask()::StoreAndCompleteL() EThumbnailPreviewThumbnail");
	        }

        // Server owns the bitmap now. If the code below leaves, we will
        // release the bitmap reference in destructor using iScaledBitmapHandle.
        if (iBitmapToPool)
           {
           iScaledBitmap = NULL;
           }
	    
        TN_DEBUG1("CThumbnailScaleTask()::StoreAndCompleteL() write params to message");
        
	    // pass bitmap handle to client
	    iMessage.WriteL( 0, iParamsBuf );
	    
	    // Successfully completed the message. The client will send
	    // EReleaseBitmap message later to delete the bitmap from pool.
	    // CThumbnailScaleTask is no longer responsible for that.
	    iScaledBitmapHandle = 0;
        }
    
    TN_DEBUG1("CThumbnailScaleTask()::StoreAndCompleteL() - end");
    }


// ---------------------------------------------------------------------------
// CThumbnailScaleTask::StoreAndCompleteL()
// Changes priority of the task.
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::ChangeTaskPriority( TInt /*aNewPriority*/ )
    {
    // The priority of scale tasks is fixed. Do nothing.
    }

// ---------------------------------------------------------------------------
// CThumbnailScaleTask::SetDoStore()
// Changes the store flag
// ---------------------------------------------------------------------------
//
void CThumbnailScaleTask::SetDoStore( TBool aDoStore )
    {
    iDoStore = aDoStore;
    }