scrsaver/scrsaverplugins/BmpAnimScrPlugin/src/CBmpAnimScrPlugin.cpp
branchRCL_3
changeset 26 e8d784ac1a4b
parent 0 040fcad49f44
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scrsaver/scrsaverplugins/BmpAnimScrPlugin/src/CBmpAnimScrPlugin.cpp	Wed Sep 01 12:30:40 2010 +0100
@@ -0,0 +1,1073 @@
+/*
+* Copyright (c) 2003 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:   Main code file for plugin
+*
+*/
+
+
+
+#include <eikenv.h>
+#include <e32math.h>
+#include <bitdev.h>
+#include <aknutils.h>
+#include <akniconutils.h>
+#include <mifconvdefs.h>
+
+#include "CBmpAnimScrPlugin.h"
+#include "BmpAnimUtils.h"
+
+#include <AknQueryDialog.h>
+#include <avkon.rsg>
+// "BmpAnimScrPlugin.rsg"
+
+const TInt KDefaultViewTime = 1000000;
+
+//
+// CBmpAnimSrcPlugin
+//
+
+// Creates and returns a new instance of CBmpAnimScrPlugin
+CBmpAnimScrPlugin* CBmpAnimScrPlugin::NewL()
+    {
+    CBmpAnimScrPlugin *plugin = new (ELeave) CBmpAnimScrPlugin();
+
+    // Initialize settings object so that the plugin name can be retrieved
+    iSettings = CBmpAnimSettings::NewL();
+    
+    return plugin;
+    }
+
+    
+// Default constructor
+CBmpAnimScrPlugin::CBmpAnimScrPlugin()
+    : iState(EPluginStateLoading),
+      iStopDisplaying(EFalse),
+      iLoadedAnimation(ENone)
+    {
+    BMALOGGER_CREATE;
+
+    }
+    
+
+// Destructor
+CBmpAnimScrPlugin::~CBmpAnimScrPlugin()
+    {
+    delete iModel;
+    delete iSettings;
+
+    StopDisplayTimer();
+    delete iDisplayTimer;
+    
+    BMALOGGER_DELETE;
+    }
+
+    
+// --- from Screensaverplugin ---
+
+
+// Initialization function. Must be called before anything but
+// name query can be done
+TInt CBmpAnimScrPlugin::InitializeL(MScreensaverPluginHost *aHost) 
+    {
+    ASSERT(aHost);
+    
+    // Sanity check
+    if (!aHost)
+        {
+        return KErrArgument;
+        }
+    
+    // Save the host interface
+    iHost = aHost;
+
+    // Start state
+    iState = EPluginStateLoading;
+
+    // Initial timing (may be overridden by settings)
+    iHost->SetRefreshTimerValue(KDefaultViewTime);
+
+    // Lie that we'll show indicators so that host does not prevent
+    // plugin to be run if there are any to show. We'll stop after a
+    // while anyway and the indicators are shown by normal Screensaver
+    iHost->OverrideStandardIndicators();
+    
+    // Grab hold of the environment (this could be in the plugin host interface)
+    iEikEnv = CEikonEnv::Static();
+
+    // Create the model to store the animation in
+    iModel = new(ELeave) CBmpAnimModel();
+    iModel->ConstructL(iSettings);
+
+    // Get screen info
+    UpdateDisplayInfo();
+    
+    // Load the animation (Reload figures out which graphics should be used)
+    ReloadAnimationL();
+
+    // Create display timer
+    iDisplayTimer = CPeriodic::NewL(CActive::EPriorityStandard);
+
+    return KErrNone;
+    }
+
+    
+// Draw function being called repeatedly by the host
+TInt CBmpAnimScrPlugin::Draw(CWindowGc& aGc) 
+    {
+    // If initializing, start the timer and move on to animation state
+    if (iState == EPluginStateInitializing)
+        {
+        BMALOGGER_WRITE("First draw, initializing");
+        
+        StartDisplayTimer();
+        HandlePluginState();
+        SetDisplayMode();
+        TInt nLights = iModel->Settings()->Lights();
+
+        if (nLights > 0)
+            {
+            Lights(nLights);
+            }
+
+        // Make sure the animation sequence starts from the beginning
+        iModel->SetCurrentItemIndex(0);
+        }
+
+    // Retrieve the next image in sequence
+    TBool endOfSequence = EFalse;
+    CBmpAnimItem* pItem = iModel->NextItem(endOfSequence);
+
+    if ((endOfSequence) || (!pItem))
+        {
+        // End of sequence reached, see if we've shown enough (1 minute)
+        if (iStopDisplaying)
+            {
+            // Stop the timer
+            StopDisplayTimer();
+
+            // Back to square 1
+            iState = EPluginStateInitializing;
+
+			// Set a lower refresh rate while plugin is suspended.
+            // This allows the Screensaver to stop Window Server heartbeat
+            // and the system is able to sleep normally
+            // NOTE: Not needed anymore, Screensaver now shuts down
+            // WSERV heartbeat for suspended plugins
+            // iHost->SetRefreshTimerValue(KDefaultViewTime);
+
+            TInt suspendTime = iModel->Settings()->SuspendTime();
+            
+            BMALOGGER_WRITEF(_L("BMA: Done drawing, suspending for %d"),
+                             suspendTime);
+            
+            iHost->Suspend(suspendTime);
+
+            return KErrNone;
+            }
+        }
+
+    if (pItem)
+        {
+        // Make sure the window is empty in case the bitmap doesn't
+        // fill the whole screen
+        aGc.Clear();
+
+        DrawCentered(aGc, pItem);
+        }
+
+// Activate code if centering INI-controllable
+#if 0
+        // Retrieve drawing information
+        CGulIcon* pIcon = pItem->Icon();
+        TPoint position = pItem->Position();
+        CFbsBitmap* bitmap = pIcon->Bitmap();
+        CFbsBitmap* mask = pIcon->Mask();
+
+        // Draw the whole bitmap at position
+        TRect rect(position, bitmap->SizeInPixels());
+        
+        if (mask)
+            {
+            // Looks like a real icon - draw masked
+            aGc.BitBltMasked(position, bitmap, rect, mask, ETrue);
+            }
+        else
+            {
+            // Just the bitmap - no masked draw
+            aGc.BitBlt(position, bitmap, rect);
+            }
+
+        // Wait for the specified time until next image
+        //
+        // TODO: The new wk28 Screensaver crashes if the next call
+        // is uncommented. Maybe the timer is not stopped
+        // before starting again? Hmm... doesn't seem to happen
+        // anymore. I wonder what changed. Anyway, I'll have it
+        // commented out for the time being, in order to control
+        // all frames' rate with the single setting
+        // 
+        // NOTE: There was a flaw in Screensaver where it would try to
+        // start refresh timer twice, if plugin changes the value during
+        // the first draw. The fix is released for 2.6_wk40_FB4
+        // iHost->SetRefreshTimerValue(pItem->Timing());
+        }
+/*
+    aGc.SetPenColor(TRgb(255,0,0));
+    aGc.SetPenStyle(CGraphicsContext::ESolidPen);
+    aGc.DrawRect(TRect(30, 30, 100, 100));
+
+    aGc.SetPenColor(TRgb(255,0,0));
+    aGc.SetPenSize(TSize(3,3));
+    aGc.DrawRect(TRect(120, 100, 200, 150));
+*/
+#endif
+    return KErrNone;
+    }
+
+
+// Return the name of the plugin
+const TDesC16& CBmpAnimScrPlugin::Name() const
+    {
+    if (iSettings)
+        {
+        return iSettings->PluginName();
+        }
+
+    return KPluginName;
+    }
+
+
+// Handles events sent by the screensaver
+TInt CBmpAnimScrPlugin::HandleScreensaverEventL(
+    TScreensaverEvent aEvent,
+    TAny* /* aData */)
+    {
+    switch (aEvent)
+        {
+        case EScreensaverEventStarting:
+            BMALOGGER_WRITE("Start event");
+            break;
+        case EScreensaverEventStopping:
+            BMALOGGER_WRITE("Stop event");
+            StopDisplayTimer();
+            iState = EPluginStateInitializing;
+            break;
+        case EScreensaverEventDisplayChanged:
+            BMALOGGER_WRITE("Display changed event");
+            // Grab current screen info
+            UpdateDisplayInfo();
+            // Reload animation, if needed
+            ReloadAnimationL();
+            break;
+        default:
+            break;
+        }
+
+    return KErrNone;
+    }
+
+
+// Return plugin capabilities (configurable)
+TInt CBmpAnimScrPlugin::Capabilities()
+    {
+    return EScpCapsConfigure;
+    }
+
+
+// Perform a plugin function
+TInt CBmpAnimScrPlugin::PluginFunction(TScPluginCaps aFunction, TAny* aParam)
+    {
+    switch (aFunction)
+        {
+        case EScpCapsConfigure:
+            {
+            TRAPD(err, err = ConfigureL(aParam));
+            return err;
+            }
+            break;
+        default:
+            return KErrNotSupported;
+            break;
+        }
+    }
+    
+
+// --- private functions ---
+
+// Draws centered items
+void CBmpAnimScrPlugin::DrawCentered(CWindowGc& aGc, CBmpAnimItem* aItem)
+    {
+    CGulIcon* pIcon = aItem->Icon();
+    CFbsBitmap* bitmap = pIcon->Bitmap();
+    CFbsBitmap* mask = pIcon->Mask();
+
+    if (!bitmap)
+        return;
+
+    // Center the bitmap horizontally and vertically (crop off excess)    
+    TPoint pos;
+    TRect rectToDraw;
+    TSize sizeBmp = bitmap->SizeInPixels();
+    TInt screenWidth = iDi.iRect.Width();
+    TInt screenHeight = iDi.iRect.Height();
+
+    // Horizontally
+    if (sizeBmp.iWidth <= screenWidth)
+        {
+        // Width fits on screen - center xpos
+        pos.iX = (screenWidth - sizeBmp.iWidth) / 2;
+        
+        // Whole width of bmp can be drawn
+        rectToDraw.SetWidth(sizeBmp.iWidth);
+        }
+    else
+        {
+        // Bmp wider than screen - xpos top left
+        pos.iX = 0;
+        
+        // Adjust draw rect position and width
+        rectToDraw.iTl.iX = (sizeBmp.iWidth - screenWidth) / 2;
+        rectToDraw.SetWidth(screenWidth);
+        }
+
+    // Vertically
+    if (sizeBmp.iHeight <= screenHeight)
+        {
+        // Height fits on screen - center ypos
+        pos.iY = (screenHeight - sizeBmp.iHeight) / 2;
+        
+        // Whole height of bmp can be drawn
+        rectToDraw.SetHeight(sizeBmp.iHeight);
+        }
+    else
+        {
+        // Bmp higher than screen - ypos top left
+        pos.iY = 0;
+        
+        // Adjust draw rect position and height
+        rectToDraw.iTl.iY = (sizeBmp.iHeight - screenHeight) / 2;
+        rectToDraw.SetHeight(screenHeight);
+        }
+
+    // Do the drawing
+    if (mask)
+        {
+        // Looks like a real icon - draw masked
+        aGc.BitBltMasked(pos, bitmap, rectToDraw, mask, ETrue);
+        }
+    else
+        {
+        // Just the bitmap - no masked draw
+        aGc.BitBlt(pos, bitmap, rectToDraw);
+        }
+    }
+
+
+// Loads the animation into the model
+void CBmpAnimScrPlugin::LoadAnimationL(TBool aLandscape, TBool aRotate)
+    {
+    // Rotated landscape not supported
+    ASSERT(!(aLandscape && aRotate));
+    
+    // Start by getting rid of a possible loaded animation
+    iModel->DeleteAll();
+    
+    // Bitmap index. If negative, loading is finished.
+    TInt nIndex = KMifIdFirst;
+    
+    TFileName fileName;
+
+    if (aLandscape)
+        {
+        fileName = iModel->Settings()->BitmapFilenameLandscape();
+        }
+    else
+        {
+        fileName = iModel->Settings()->BitmapFilename();
+        }
+
+    BMALOGGER_WRITEF(_L("BMA: Loading from: %S"), &(fileName));
+            
+    while (nIndex > 0)
+        {
+        CFbsBitmap* pBmp = NULL;
+        
+        TRAPD(err, pBmp = AknIconUtils::CreateIconL(fileName, nIndex));
+
+        if ((pBmp) && (err == KErrNone))
+            {
+            // Got bitmap, push and set size
+            CleanupStack::PushL(pBmp);
+            TInt scaleErr = ScaleBitmap(pBmp, aRotate);
+            if (scaleErr == KErrNone)
+                {
+                // Create an item with the bitmap and store it in the model
+                CBmpAnimItem* pItem = new(ELeave) CBmpAnimItem();
+                CleanupStack::PushL(pItem);
+            
+                pItem->SetIconL(pBmp);
+                iModel->AppendItemL(pItem);
+                
+                CleanupStack::Pop(2); // pBmp, pItem
+            
+                BMALOGGER_WRITEF(_L("BMA: Loaded bmp %d"), nIndex);
+                
+                // Try loading next bitmap (skip mask IDs)
+                nIndex += 2;
+                }
+            else
+                {
+                BMALOGGER_WRITEF(_L("BMA: Bmp %d scale err %d"),
+                                 nIndex, scaleErr);
+
+                // Apparently SVG icon was not found, this is not caught
+                // in CreateIconL(). Assume last image was loaded.
+                CleanupStack::PopAndDestroy();  // pBmp
+                nIndex = -1;
+                }
+            }
+        else
+            {
+            // Loading failed - maybe reached end of bitmaps 
+            nIndex = -1;
+            
+            BMALOGGER_WRITEF(_L("BMA: Bmp load failed: %d"), err);
+            }
+        }
+
+    // Save the type of loaded animation
+    if (aLandscape)
+        {
+        iLoadedAnimation = ELandscape;
+        }
+    else if (aRotate)
+        {
+        iLoadedAnimation = EPortraitRotated;
+        }
+    else
+        {
+        iLoadedAnimation = EPortrait;
+        }
+
+    // On to next state
+    HandlePluginState();
+    
+    // Start animating, when appropriate
+    iHost->SetRefreshTimerValue(iModel->Settings()->Timing());
+
+    BMALOGGER_WRITE("BMA: Animation loaded");
+    }
+
+
+// Re-loads the animation into the model, if needed
+void CBmpAnimScrPlugin::ReloadAnimationL()
+    {
+    // Check if the correct graphics are already loaded
+    if (!ReloadNeeded())
+        {
+        // Done! That was easy :)
+        return;
+        }
+
+    // Load correct graphics
+    LoadAnimationL(LoadLandscape(), RotateNeeded());
+    }
+
+    
+// Starts the display timer
+void CBmpAnimScrPlugin::StartDisplayTimer()
+    {
+    ASSERT(iDisplayTimer);
+
+    TInt time = iModel->Settings()->RunningTime();
+
+    BMALOGGER_WRITEF(_L("BMA: Start display timer for %d"), time);
+
+    iStopDisplaying = EFalse;
+    iDisplayTimer->Start(
+        time,
+        time,
+        TCallBack(DisplayTimerCallback, this));
+    }
+
+    
+// Stops the display timer
+void CBmpAnimScrPlugin::StopDisplayTimer()
+    {
+    BMALOGGER_WRITE("BMA: Stop display timer");
+
+    if (iDisplayTimer)
+        {
+        iDisplayTimer->Cancel();
+        }
+    
+    iStopDisplaying = EFalse;
+    }
+
+
+// Display timer callback - sets animation stop flag
+TInt CBmpAnimScrPlugin::DisplayTimerCallback(TAny* aPtr)
+    {
+    BMALOGGER_WRITE("BMA: Display timer timeout");
+
+    CBmpAnimScrPlugin* _this = REINTERPRET_CAST(CBmpAnimScrPlugin*, aPtr);
+    _this->iStopDisplaying = ETrue;
+    return KErrNone;
+    }
+
+    
+// Changes the internal state flag    
+void CBmpAnimScrPlugin::HandlePluginState()
+    {
+    switch (iState)
+        {
+        case EPluginStateLoading:
+            iState = EPluginStateInitializing;
+            break;
+        case EPluginStateInitializing:          
+            iState = EPluginStateAnimation;
+            break;
+        case EPluginStateAnimation:
+             break; 
+        case EPluginStateStoppingAnimation:
+             iHost->SetRefreshTimerValue(KDefaultViewTime);
+             iState = EPluginStateInitializing;
+             break;
+        }
+    }
+
+
+// Requests display mode from host
+void CBmpAnimScrPlugin::SetDisplayMode()
+    {
+    if (!iHost)
+        {
+        return;
+        }
+    
+    // Exit partial mode
+    iHost->ExitPartialMode();
+    }
+
+
+void CBmpAnimScrPlugin::Lights(TInt aSecs)
+    {
+    BMALOGGER_WRITEF(_L("BMA: Request lights for %d secs"), aSecs);
+    iHost->RequestLights(aSecs);
+    }
+
+    
+// Configure the plugin
+TInt CBmpAnimScrPlugin::ConfigureL(TAny* aParam)
+    {
+    if (!iSettings)
+        {
+        return KErrNotFound;
+        }
+    
+    // Grab the parameter (CEikonEnv in this case)
+    CEikonEnv* eikEnv = NULL;
+
+    if (aParam)
+        {
+        // The host was kind enough to provide us with a param - use it
+        eikEnv = REINTERPRET_CAST(CEikonEnv*, aParam);
+        }
+    else if (iEikEnv)
+        {
+        // Use own env, if initialized
+        eikEnv = iEikEnv;
+        }
+
+    TInt setting = iSettings->Lights();
+    
+    CAknNumberQueryDialog* dlg = CAknNumberQueryDialog::NewL(setting);
+    CleanupStack::PushL(dlg);
+    _LIT(KPrompt, "Lights time (sec)");
+    dlg->SetPromptL(KPrompt);
+    dlg->SetMinimumAndMaximum(0, 30);
+    CleanupStack::Pop();
+    
+    if (dlg->ExecuteLD(R_AVKON_DIALOG_QUERY_VALUE_NUMBER))
+        {
+        iSettings->SetLights(setting);
+        iSettings->SaveSettingsL();
+        }
+    
+    // All was swell!
+    return KErrNone;
+    }
+
+
+// Scale bitmap to screen size, set size of SVG bitmaps
+TInt CBmpAnimScrPlugin::ScaleBitmap(CFbsBitmap* aBmp, TBool aRotate)
+    {
+    TInt ret = KErrNone;
+
+    // SVG size always screen size
+    TSize size = iDi.iRect.Size();
+
+    if (!AknIconUtils::IsMifIcon(aBmp))
+        {
+        // Bitmaps maintain their original size, unless scaling requested, in which
+        // case screen size is OK 
+        if (!iSettings->ScaleBmps())
+            {
+            // No scaling, use original size
+            size = aBmp->SizeInPixels();
+
+            if (aRotate)
+                {
+                // Lie the target size, otherwise IconUitls will think
+                // the image needs scaling (this won't work perfectly
+                // either, the image gets clipped a little :(
+                //size.SetSize(size.iHeight, size.iHeight);
+
+                // Flip size for rotation
+                size.SetSize(size.iHeight, size.iWidth);
+                }
+            }
+        }
+    
+    if (aRotate)
+        {
+        // Set image to screen size and rotate 90 deg left (270 right)
+        // ret = SetSizeAndRotation(aBmp, size, 270);
+        ret = AknIconUtils::SetSizeAndRotation(
+            aBmp, size, EAspectRatioPreservedSlice, 270);
+        }
+    else
+        {
+        // Just set image to size
+        ret = AknIconUtils::SetSize(aBmp, size, EAspectRatioPreserved);
+        }
+
+    return ret;
+    }
+
+
+// Returns ETrue if reload of the animation is needed
+TBool CBmpAnimScrPlugin::ReloadNeeded()
+    {
+    // Assume reload needed
+    TBool needed = ETrue;
+
+    switch (iLoadedAnimation)
+        {
+        case EPortrait:
+            // No reload if display portrait
+            if (!DisplayIsLandscape())
+                {
+                needed = EFalse;
+                }
+            break;
+
+        case ELandscape:
+        case EPortraitRotated:
+            // No reload if display landscape
+            if (DisplayIsLandscape())
+                {
+                needed = EFalse;
+                }
+            break;
+            
+        case ENone:
+        default:
+            // Reload
+            break;
+        }
+
+    return needed;
+    }
+
+    
+// Returns ETrue if display in landscape
+TBool CBmpAnimScrPlugin::DisplayIsLandscape()
+    {
+    // Should actually check the rotation and stuff, but what the hey...
+    return (iDi.iRect.Width() > iDi.iRect.Height());
+    }
+
+
+// Returns ETrue if graphics should be rotated
+TBool CBmpAnimScrPlugin::RotateNeeded()
+    {
+    // Rotate needed, if only portrait graphics are used, and
+    // display is landscape
+    return ((!iSettings->UseLandscape()) && (DisplayIsLandscape()));
+    }
+
+
+// Returns ETrue if landscape graphics should be loaded
+TBool CBmpAnimScrPlugin::LoadLandscape()
+    {
+    // Landscape, if only available and display is landscape
+    return ((iSettings->UseLandscape()) && (DisplayIsLandscape()));
+    }
+    
+    
+// Updates the saved information about display
+void CBmpAnimScrPlugin::UpdateDisplayInfo()
+    {
+    iDi.iSize = sizeof(TScreensaverDisplayInfo);
+    iHost->DisplayInfo(&iDi);
+    }
+
+#if 0
+// Rotates and scales a source bitmap into target bitmap (non-leaving wrapper)
+TInt CBmpAnimScrPlugin::SetSizeAndRotation(
+    CFbsBitmap* aBmp, TSize aSize, TInt aAngle)
+    {
+    // Anything to do?
+    if ((aBmp) && (aBmp->SizeInPixels() == aSize) && ((aAngle % 360) == 0))
+        {
+        // Duh, the bitmap is already as requested
+        return KErrNone;
+        }
+
+    // Call the actual workhorse
+    TRAPD(err, SetSizeAndRotationL(aBmp, aSize, aAngle));
+
+    return err;
+    }
+
+
+// Rotates and scales a source bitmap into target bitmap (leaving version)
+void CBmpAnimScrPlugin::SetSizeAndRotationL(
+    CFbsBitmap* aBmp, TSize aSize, TInt aAngle)
+    {
+    // Make a copy of the source bitmap, and use the original source as target
+    CFbsBitmap* tmpBmp = new (ELeave) CFbsBitmap;
+    CleanupStack::PushL(tmpBmp);
+
+    User::LeaveIfError(tmpBmp->Duplicate(aBmp->Handle()));
+    
+    // Discard original bitmap
+    aBmp->Reset();
+
+    // Create new target bitmap in the original object
+    User::LeaveIfError(aBmp->Create(aSize, tmpBmp->DisplayMode()));
+
+    // Let the workhorse do its work
+    RotateAndScaleBitmapL(TRect(aSize), aBmp, tmpBmp, aAngle);
+
+    // Not interested in original anymore
+    CleanupStack::PopAndDestroy(tmpBmp);
+    }
+    
+
+// Rotates and scales a source bitmap into target bitmap
+void CBmpAnimScrPlugin::RotateAndScaleBitmapL(
+    const TRect& aTrgRect,
+    CFbsBitmap* aTrgBitmap, 
+    CFbsBitmap* aSrcBitmap,
+    TInt aAngle)
+    {
+    aAngle = aAngle % 360;
+    if (aAngle < 0)
+        {
+        aAngle+=360;
+        }
+
+    if (!aSrcBitmap) User::Leave(KErrArgument);
+    if (!aTrgBitmap) User::Leave(KErrArgument);
+    if (aSrcBitmap->DisplayMode() != aTrgBitmap->DisplayMode()) 
+        User::Leave(KErrArgument);
+    
+    TSize trgBitmapSize = aTrgBitmap->SizeInPixels();
+    if ((trgBitmapSize.iHeight < aTrgRect.iBr.iY) ||
+        (trgBitmapSize.iWidth < aTrgRect.iBr.iX))
+        {
+        User::Leave(KErrArgument);
+        }
+
+    if (aTrgRect.IsEmpty())
+        return;
+
+    TSize srcSize = aSrcBitmap->SizeInPixels();
+
+    TInt centerX = srcSize.iWidth / 2;
+    TInt centerY = srcSize.iHeight / 2;
+
+    TInt trgWidth = aTrgRect.Width();
+    TInt trgHeight = aTrgRect.Height();
+    
+    TInt scalefactor = 65536;
+    TInt xscalefactor = (srcSize.iWidth << 16) / trgWidth;
+    TInt yscalefactor = (srcSize.iHeight << 16) / trgHeight;
+
+    // Check if rotating 90 left or right, no need to scale
+    if (((aAngle == 270) || (aAngle == 90)) &&
+        (srcSize.iWidth == trgHeight) &&
+        (srcSize.iHeight == trgWidth))
+            {
+            scalefactor = 65535;
+            }
+        else
+            {
+            if (xscalefactor < yscalefactor)
+                {
+                scalefactor = yscalefactor;
+                }
+            else
+                {
+                scalefactor = xscalefactor;
+                }
+            }
+    
+        TBool srcTemporary = EFalse;
+        TBool hardMask = EFalse;
+        if (aSrcBitmap->IsRomBitmap())
+            {
+            srcTemporary = ETrue;
+            }
+        if (aSrcBitmap->IsCompressedInRAM())
+            {
+            srcTemporary = ETrue;
+            }
+
+        TBool fallbackOnly = EFalse;
+        TDisplayMode displayMode = aSrcBitmap->DisplayMode();
+        TUint8 fillColor = 0;
+        
+        switch(displayMode)
+            {
+            case EGray2:
+                srcTemporary = ETrue;
+                hardMask = ETrue;
+                fillColor = 0xff; // white
+                break;
+            case EGray4:
+            case EGray16:
+            case EColor16:
+            case EColor16M:
+            case ERgb:
+                fallbackOnly = ETrue;
+                break;
+            case EColor256:
+                fillColor = 0xff; // should be black in our indexed palette....
+            case EGray256:
+            case EColor4K:
+            case EColor64K:
+
+            case EColor16MU:
+            // These are the supported modes
+                break;
+            default:
+                fallbackOnly = ETrue;
+            }
+
+        if (fallbackOnly)
+            {
+            // Color mode not supported
+            User::Leave(KErrNotSupported);
+            }
+
+        CFbsBitmap* realSource = aSrcBitmap;
+        CFbsBitmap* realTarget = aTrgBitmap;
+        if (srcTemporary)
+            {
+            realSource = new (ELeave) CFbsBitmap();
+            CleanupStack::PushL(realSource);
+            if (hardMask)
+                {
+                realTarget = new (ELeave) CFbsBitmap();
+                CleanupStack::PushL(realTarget);
+                User::LeaveIfError(realSource->Create(srcSize, EGray256));
+                displayMode = EGray256;
+                User::LeaveIfError(realTarget->Create(
+                    aTrgBitmap->SizeInPixels(), EGray256));
+                }
+            else
+                {
+                User::LeaveIfError(realSource->Create(
+                    srcSize, aSrcBitmap->DisplayMode()));
+                }
+
+            CFbsBitmapDevice* dev = CFbsBitmapDevice::NewL(realSource);
+            CleanupStack::PushL(dev);
+            CFbsBitGc* gc = NULL;
+            User::LeaveIfError(dev->CreateContext(gc));
+            CleanupStack::PushL(gc);
+            gc->BitBlt(TPoint(0,0), aSrcBitmap);
+            CleanupStack::PopAndDestroy(2); // dev, gc
+            }
+
+        // Heap lock for FBServ large chunk is only needed with large bitmaps.
+        if (realSource->IsLargeBitmap() || realTarget->IsLargeBitmap())
+            {
+            realTarget->LockHeapLC(ETrue); // fbsheaplock
+            }
+        else
+            {
+            // Bogus push so we can pop() anyway
+            CleanupStack::PushL((TAny*)NULL);
+            }
+
+        TUint32* srcAddress = realSource->DataAddress();
+        TUint32* trgAddress = realTarget->DataAddress();
+
+        TReal realsin;
+        TReal realcos;
+        TInt sin;
+        TInt cos;
+
+        User::LeaveIfError(Math::Sin(realsin, ((2*KPi)/360) * aAngle));
+        User::LeaveIfError(Math::Cos(realcos, ((2*KPi)/360) * aAngle));
+
+        sin = ((TInt)(realsin * scalefactor));
+        cos = ((TInt)(realcos * scalefactor));
+
+        TInt xx = ((trgWidth)/2) - ((srcSize.iWidth/2) - centerX);
+        TInt yy = ((trgHeight)/2) - ((srcSize.iHeight/2) - centerY);
+
+        TInt x = 0;
+        TInt y = 0;
+        TInt u = 0;
+        TInt v = 0;
+
+        if( (displayMode==EGray256) || (displayMode==EColor256) )
+            {
+            TInt srcScanLen8 = CFbsBitmap::ScanLineLength(
+                srcSize.iWidth, displayMode);
+            TInt trgScanLen8 = CFbsBitmap::ScanLineLength(
+                trgBitmapSize.iWidth, displayMode);
+            TUint8* srcAddr8 = reinterpret_cast<TUint8*>(srcAddress);
+            TUint8* trgAddr8 = reinterpret_cast<TUint8*>(trgAddress);
+
+            // skip left and top margins in the beginning
+            trgAddr8 += trgScanLen8 * aTrgRect.iTl.iY + aTrgRect.iTl.iX;
+
+            for (y = 0; y < trgHeight; y++)
+                {
+                u = (-xx) * cos + (y-yy) * sin + (centerX<<16);
+                v = (y-yy) * cos - (-xx) * sin + (centerY<<16);
+                for (x = 0; x < trgWidth; x++)
+                    {
+                    if (((u>>16)>=srcSize.iWidth) ||
+                         ((v>>16)>=srcSize.iHeight) ||
+                         ((u>>16)<0) ||
+                         ((v>>16)<0)) 
+                        {
+                        *trgAddr8++ = fillColor;
+                        }
+                    else
+                        {            	
+                        *trgAddr8++ = srcAddr8[(u>>16)+(((v>>16))*srcScanLen8)];
+                        }
+                    u += cos;
+                    v -= sin;
+                    }
+                trgAddr8 += trgScanLen8 - trgWidth;
+                }
+            }
+        else if( displayMode == EColor64K || displayMode == EColor4K)
+            {
+            TInt srcScanLen16 = CFbsBitmap::ScanLineLength(
+                srcSize.iWidth, displayMode) / 2;
+            TInt trgScanLen16 = CFbsBitmap::ScanLineLength(
+                trgBitmapSize.iWidth, displayMode) / 2;
+            TUint16* srcAddr16 = reinterpret_cast<TUint16*>(srcAddress);
+            TUint16* trgAddr16 = reinterpret_cast<TUint16*>(trgAddress);
+
+            // skip left and top margins in the beginning
+            trgAddr16 += trgScanLen16 * aTrgRect.iTl.iY + aTrgRect.iTl.iX;
+
+            for (y = 0; y < trgHeight; y++)
+                {
+                u = (-xx) * cos + (y-yy) * sin + (centerX<<16);
+                v = (y-yy) * cos - (-xx) * sin + (centerY<<16);
+                for (x = 0; x < trgWidth; x++)
+                    {
+                    if (((u>>16)>=srcSize.iWidth) ||
+                        ((v>>16)>=srcSize.iHeight) ||
+                        ((u>>16)<0) ||
+                        ((v>>16)<0)) 
+                        {
+                        *trgAddr16++ = 0;
+                        }
+                    else
+                        {
+                        *trgAddr16++ =
+                            srcAddr16[(u>>16)+(((v>>16))*srcScanLen16)];
+                        }
+                    u += cos;
+                    v -= sin;
+                    }
+                trgAddr16 += trgScanLen16 - trgWidth;
+                }        
+            }
+        else if(displayMode == EColor16MU)
+            {
+            TInt srcScanLen32 = CFbsBitmap::ScanLineLength(
+                srcSize.iWidth, displayMode) / 4;
+            TInt trgScanLen32 = CFbsBitmap::ScanLineLength(
+                trgBitmapSize.iWidth, displayMode) / 4;
+            TUint32* srcAddr32 = srcAddress;
+            TUint32* trgAddr32 = trgAddress;
+
+            // skip left and top margins in the beginning
+            trgAddr32 += trgScanLen32 * aTrgRect.iTl.iY + aTrgRect.iTl.iX;
+
+            for (y = 0; y < trgHeight; y++)
+                {
+                u = (-xx) * cos + (y-yy) * sin + (centerX<<16);
+                v = (y-yy) * cos - (-xx) * sin + (centerY<<16);
+                for (x = 0; x < trgWidth; x++)
+                    {
+                    if (((u>>16)>=srcSize.iWidth) ||
+                        ((v>>16)>=srcSize.iHeight) ||
+                        ((u>>16)<0) ||
+                        ((v>>16)<0)) 
+                        {
+                        *trgAddr32++ = 0;
+                        }
+                    else
+                        {
+                        *trgAddr32++ =
+                            srcAddr32[(u>>16)+(((v>>16))*srcScanLen32)];
+                        } 
+                    u += cos;
+                    v -= sin;
+                    }
+                trgAddr32 += trgScanLen32 - trgWidth;
+                }
+            }
+        else
+            {
+            // Display mode not supported - but this should've been caught
+            // already earlier
+            User::Leave(KErrUnknown);
+            }
+
+        CleanupStack::PopAndDestroy(); // fbsheaplock
+
+        if (srcTemporary)
+            {
+            if (hardMask)
+                {
+                CFbsBitmapDevice* dev = CFbsBitmapDevice::NewL(aTrgBitmap);
+                CleanupStack::PushL(dev);
+                CFbsBitGc* gc = NULL;
+                User::LeaveIfError(dev->CreateContext(gc));
+                CleanupStack::PushL(gc);  
+                gc->BitBlt(TPoint(0,0), realTarget);
+                CleanupStack::PopAndDestroy(3); // dev, gc, realtarget
+                }
+            CleanupStack::PopAndDestroy(); // realSource
+            }
+        }
+#endif
+    
+// End of file