uifw/AknGlobalUI/OldStyleNotif/Src/AknPrivateImageLoader.cpp
changeset 0 2f259fa3e83a
child 9 0aa5fbdfbc30
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uifw/AknGlobalUI/OldStyleNotif/Src/AknPrivateImageLoader.cpp	Tue Feb 02 01:00:49 2010 +0200
@@ -0,0 +1,489 @@
+/*
+* Copyright (c) 2008 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:  Private image loader to convert binary array into usable image
+*
+*/
+
+
+// INCLUDES
+#include "AknPrivateImageLoader.h"
+#include <imageconversion.h>
+#include <eikenv.h>
+#include <f32file.h>
+#include <e32debug.h>
+#include <eikimage.h>
+#include <AknIconUtils.h>
+#include <SVGEngineInterfaceImpl.h>
+
+/// Max bitmap dimension (heigth or width) which is allowed to be loaded
+const TInt KMaxDecodeSize   = 300;
+
+/// Debug print macro
+#ifdef _DEBUG
+    #define RDEBUG( args... ) RDebug::Printf( args )
+#else
+    #define RDEBUG( args... )  
+#endif
+
+// ======== MEMBER FUNCTIONS ========
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::CAknPrivateImageLoader
+// ---------------------------------------------------------------------------
+//
+CAknPrivateImageLoader::CAknPrivateImageLoader(
+    RFs& aFs,
+    MAknPrivateImageLoaderObserver& aObserver ) : 
+    CActive( EPriorityStandard ),
+    iObserver( aObserver ),
+    iFs( aFs )
+    {
+    CActiveScheduler::Add( this );
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::ConstructL
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::ConstructL()
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::NewL
+// ---------------------------------------------------------------------------
+//
+CAknPrivateImageLoader* CAknPrivateImageLoader::NewL(
+    RFs& aFs,
+    MAknPrivateImageLoaderObserver& aObserver )
+    {
+    CAknPrivateImageLoader* self = 
+        CAknPrivateImageLoader::NewLC( aFs, aObserver );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::NewLC
+// ---------------------------------------------------------------------------
+//
+CAknPrivateImageLoader* CAknPrivateImageLoader::NewLC(
+    RFs& aFs,
+    MAknPrivateImageLoaderObserver& aObserver )
+    {
+    CAknPrivateImageLoader* self = 
+        new( ELeave ) CAknPrivateImageLoader( aFs, aObserver );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::~CAknPrivateImageLoader
+// ---------------------------------------------------------------------------
+//
+CAknPrivateImageLoader::~CAknPrivateImageLoader()
+    {
+    Cancel();
+    delete iDecoder;
+    delete iIcon;
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::LoadIconL
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::LoadIconL( 
+    const TDesC8& aImageData, 
+    TSize aSize )
+    {
+    Cancel();
+
+    // try to load bitmap
+    TRAPD( err, LoadL( aImageData, aSize ) );
+    
+    if( err == KErrTooBig )
+        {
+        User::Leave( err );
+        }
+    else if( err )
+        {
+        // Decoder can't open it -> try to load as SVG image
+        LoadSVGImageL( aImageData, aSize );
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::RunL
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::RunL()
+    {
+    // Compress the heap after image conversion as image decoder
+    // seems to leave heap uncompressed
+    if( iDecoder )
+        {
+        delete iDecoder;
+        iDecoder = NULL;
+        User::Heap().Compress();
+        }
+    
+    // check errors
+    TInt status = iStatus.Int();
+    if( status < KErrNone )
+        {
+        // Image load failed
+        RDEBUG( "CAknPrivateImageLoader::RunL: image load error %d", status );
+
+        delete iIcon;
+        iIcon = NULL;
+
+        iObserver.ImageLoadError( status );
+        // don't use member variables after callback, since this instance
+        // might be deleted
+        }
+    else
+        {
+        // Image load success
+        ASSERT( iIcon );
+        
+        // generate dummy mask if image didn't have one.
+        CFbsBitmap* mask = iIcon->Mask();
+        if( !mask )
+            {
+            CFbsBitmap* genMask = 
+                GenerateMaskLC( iIcon->Bitmap()->SizeInPixels() );
+            iIcon->SetMask( genMask );
+            CleanupStack::Pop( genMask );
+            }
+        
+        // 1 bit masks needs to be inverted
+        else if( mask->DisplayMode() == EGray2 ) 
+            {
+            InvertImageL( *mask );
+            }
+        
+        // create scalable image
+        CAknIcon* icon = iIcon;
+        iIcon = NULL;
+        iIcon = CreateIconL( icon ); // takes ownership (leave safe)
+        
+        CEikImage* image = new(ELeave) CEikImage;
+        image->SetPicture( iIcon->Bitmap(), iIcon->Mask() );
+        
+        // remove ownership from iIcon
+        iIcon->SetBitmap( NULL );
+        iIcon->SetMask( NULL );
+        
+        delete iIcon;
+        iIcon = NULL;
+        
+        iObserver.ImageLoadSuccess( image ); // ownership given
+        // don't use member variables after callback, since this instance
+        // might be deleted
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::RunError
+// ---------------------------------------------------------------------------
+//
+TInt CAknPrivateImageLoader::RunError( TInt aError )
+    {
+    RDEBUG( "CAknPrivateImageLoader::RunError: image load error %d", aError );
+    
+    delete iIcon;
+    iIcon = NULL;
+
+    iObserver.ImageLoadError( aError );
+    // don't use member variables after callback, since this instance
+    // might be deleted
+
+    return KErrNone;
+    }
+
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::DoCancel
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::DoCancel()
+    {
+    if( iDecoder )
+        {
+        iDecoder->Cancel();
+        delete iDecoder;
+        iDecoder = NULL;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::InvertImageL
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::InvertImageL( CFbsBitmap& aBitmap )
+    {
+    TDisplayMode mode = aBitmap.DisplayMode();
+    TInt width = aBitmap.SizeInPixels().iWidth;
+    TInt height = aBitmap.SizeInPixels().iHeight;
+
+    HBufC8* buf = HBufC8::NewLC( aBitmap.ScanLineLength( width, mode ) );
+    TPtr8 bufPtr( buf->Des() );
+    
+    for( TInt i = 0; i < height; ++i )
+        {
+        aBitmap.GetScanLine( bufPtr, TPoint( 0, i ), width, mode );
+        TInt len = bufPtr.Length();
+        for( TInt j = 0; j < len; ++j )
+            {
+            bufPtr[j] ^= 0xff;
+            }
+        aBitmap.SetScanLine( bufPtr, i );
+        }
+    
+    CleanupStack::PopAndDestroy( buf );
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::CreateIconL
+// ---------------------------------------------------------------------------
+//
+CAknIcon* CAknPrivateImageLoader::CreateIconL( CAknIcon* aIcon )
+    {
+    CAknIcon* result = NULL;
+    if( aIcon->Mask() )
+        {
+        result = AknIconUtils::CreateIconL( aIcon ); // deletes icon in case of leave
+        }
+    else
+        {
+        // aMask is NULL
+        CFbsBitmap* image = aIcon->Bitmap();
+        aIcon->SetBitmap( NULL );
+        CleanupStack::PushL( aIcon );
+        image = AknIconUtils::CreateIconL( image ); // deletes image in case of leave
+        CleanupStack::Pop( aIcon );
+        aIcon->SetBitmap( image );
+        result = aIcon;
+        }
+    
+    return result; // ownership given to caller
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::LoadL
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::LoadL( const TDesC8& aImageData, TSize aSize )
+    {
+    delete iDecoder;
+    iDecoder = NULL;
+    
+    // default loader
+    iDecoder = CImageDecoder::DataNewL( iFs, aImageData, 
+        CImageDecoder::EAllowGeneratedMask );
+
+    TFrameInfo info( iDecoder->FrameInfo() ); 
+    TSize& frameSize( info.iFrameSizeInPixels );
+    if( frameSize.iHeight > KMaxDecodeSize || 
+        frameSize.iWidth > KMaxDecodeSize )
+        {
+        RDEBUG("CAknPrivateImageLoader::LoadLC err: image larger than %dx%d",
+            KMaxDecodeSize, KMaxDecodeSize );
+        User::Leave( KErrTooBig );
+        }
+    
+    CAknIcon* icon = CAknIcon::NewL();
+    CleanupStack::PushL( icon );
+    icon->SetBitmap( new( ELeave ) CFbsBitmap() );
+    
+    TSize decodeSize( DecodeSize( info, aSize ) );
+    
+    User::LeaveIfError( icon->Bitmap()->Create( 
+        decodeSize, info.iFrameDisplayMode ) );
+
+    if ( info.iFlags & TFrameInfo::ETransparencyPossible )
+        {
+        // Transparency available -> use mask
+        icon->SetMask( new( ELeave ) CFbsBitmap() );
+        User::LeaveIfError( icon->Mask()->Create( decodeSize,
+            info.iFlags & TFrameInfo::EAlphaChannel ? EGray256 : EGray2 ) );
+        }
+
+    // start processing
+    if( icon->Mask() )
+        {
+        iDecoder->Convert( &iStatus, *icon->Bitmap(), *icon->Mask() );
+        }
+    else
+        {
+        iDecoder->Convert( &iStatus, *icon->Bitmap() );
+        }
+    
+    CleanupStack::Pop( icon );
+    delete iIcon;
+    iIcon = icon;
+
+    // wait for completion
+    SetActive();
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::LoadSVGImageL
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::LoadSVGImageL(
+    const TDesC8& aImageData, 
+    TSize aSize )
+    {
+    CFbsBitmap* dummy = new(ELeave) CFbsBitmap();
+    CleanupStack::PushL( dummy );
+    TFontSpec spec;
+    CSvgEngineInterfaceImpl* svgEngine = 
+        CSvgEngineInterfaceImpl::NewL( dummy, NULL, spec );
+    CleanupStack::PushL( svgEngine );
+
+    TInt handle = KErrNotFound;
+    LeaveIfErrorL( svgEngine->PrepareDom( aImageData, handle ) );
+    
+    CAknIcon* icon = CAknIcon::NewL();
+    CleanupStack::PushL( icon );
+    icon->SetBitmap( new(ELeave) CFbsBitmap() );
+    
+    // create image bitmap
+    TDisplayMode mode( EColor64K ); // default value
+    CEikonEnv* eikon = CEikonEnv::Static();
+    if( eikon )
+        {
+        mode = eikon->ScreenDevice()->DisplayMode();
+        }
+    User::LeaveIfError( icon->Bitmap()->Create( aSize, mode ) ); 
+    
+    // create soft mask
+    icon->SetMask( new(ELeave) CFbsBitmap() );
+    User::LeaveIfError( icon->Mask()->Create( aSize, EGray256 ) );
+
+    // render svg image
+    LeaveIfErrorL( 
+        svgEngine->RenderDom( handle, icon->Bitmap(), icon->Mask() ) );
+
+    CleanupStack::Pop( icon );
+    CleanupStack::PopAndDestroy( 2, dummy ); // svgEngine
+    
+    delete iIcon;
+    iIcon = icon;
+
+    // handle image in RunL
+    TRequestStatus* status = &iStatus;
+    User::RequestComplete( status, KErrNone );
+    SetActive();
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::LeaveIfErrorL
+// ---------------------------------------------------------------------------
+//
+void CAknPrivateImageLoader::LeaveIfErrorL( MSvgError* aError )
+    {
+    if( aError )
+        {
+        if( aError->HasError() && !aError->IsWarning() )
+            {
+            RDEBUG( "CAknPrivateImageLoader::LeaveIfErrorL: "
+                "SVG loading error: %d", aError->ErrorCode() );
+            User::Leave( KErrCorrupt );
+            }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::GenerateMaskLC
+// ---------------------------------------------------------------------------
+//
+CFbsBitmap* CAknPrivateImageLoader::GenerateMaskLC( TSize aSize )
+    {
+    CFbsBitmap* mask = new(ELeave) CFbsBitmap;
+    CleanupStack::PushL( mask );
+    User::LeaveIfError( mask->Create( aSize, EGray2 ) );
+
+    TInt width = aSize.iWidth;
+    TInt height = aSize.iHeight;
+    HBufC8* buf = HBufC8::NewLC( mask->ScanLineLength( width, EGray2 ) );
+    TPtr8 bufPtr( buf->Des() );
+    
+    for( TInt i = 0; i < height; ++i )
+        {
+        mask->GetScanLine( bufPtr, TPoint( 0, i ), width, EGray2 );
+        bufPtr.FillZ();
+        mask->SetScanLine( bufPtr, i );
+        }
+    
+    CleanupStack::PopAndDestroy( buf );
+    User::LeaveIfError( mask->Compress() );
+    return mask;
+    }
+
+// ---------------------------------------------------------------------------
+// CAknPrivateImageLoader::DecodeSize
+// ---------------------------------------------------------------------------
+//
+TSize CAknPrivateImageLoader::DecodeSize( 
+    const TFrameInfo& aFrameInfo, 
+    const TSize& aTargetSize )
+    {
+    TSize imageSize( aFrameInfo.iOverallSizeInPixels );
+    
+    if( aFrameInfo.iFlags & TFrameInfo::EFullyScaleable || 
+        aTargetSize == imageSize )
+        {
+        // decoder can scale to any ratio or the size is already correct
+        return aTargetSize;
+        }
+    
+    if( imageSize.iWidth < aTargetSize.iWidth ||
+        imageSize.iHeight < aTargetSize.iHeight )
+        {
+        // image is smaller than targetsize
+        return imageSize;
+        }
+    
+    // Scale ratio limits
+    const TInt KScaleRatioMin = 2;
+    const TInt KScaleRatioMax = 8;
+    
+    // 1:1 is always valid ratio for decode scaling
+    TInt lastValidRatio( 1 );
+    for( TInt ratio = KScaleRatioMin; ratio <= KScaleRatioMax; ratio <<= 1 )
+        {
+        if( imageSize.iWidth % ratio + imageSize.iHeight % ratio == 0 )
+            {
+            // this ratio is valid
+            if( imageSize.iWidth / ratio < aTargetSize.iWidth || 
+                imageSize.iHeight / ratio < aTargetSize.iHeight )
+                {
+                // the decoded size was smaller in some dimension, 
+                // the last valid ratio should be used
+                break;
+                }
+                
+            // this scale ratio results to greater or equal size
+            lastValidRatio = ratio;
+            }
+        }
+    
+    // return the size scaled with correct ratio
+    return TSize( imageSize.iWidth / lastValidRatio, 
+                  imageSize.iHeight / lastValidRatio );
+    }
+
+// End of file