/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <exception>
#include <w32std.h>
#include <fbs.h>

#include <private/qapplication_p.h>
#include <private/qgraphicssystem_p.h>
#include <private/qt_s60_p.h>
#include <private/qpaintengine_s60_p.h>

#include "qpixmap.h"
#include "qpixmap_raster_p.h"
#include <qwidget.h>
#include "qpixmap_s60_p.h"
#include "qnativeimage_p.h"
#include "qbitmap.h"
#include "qimage.h"
#include "qimage_p.h"

#include <fbs.h>

QT_BEGIN_NAMESPACE

const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08,
                                     0x10, 0x20, 0x40, 0x80 };


/*
    \class QSymbianFbsClient
    \since 4.6
    \internal

    Symbian Font And Bitmap server client that is
    used to lock the global bitmap heap. Only used in
    S60 v3.1 and S60 v3.2.
*/
_LIT(KFBSERVLargeBitmapAccessName,"FbsLargeBitmapAccess");
class QSymbianFbsClient
{
public:

    QSymbianFbsClient() : heapLocked(false)
    {
        heapLock.OpenGlobal(KFBSERVLargeBitmapAccessName);
    }

    ~QSymbianFbsClient()
    {
        heapLock.Close();
    }

    bool lockHeap()
    {
        bool wasLocked = heapLocked;

        if (heapLock.Handle() && !heapLocked) {
            heapLock.Wait();
            heapLocked = true;
        }

        return wasLocked;
    }

    bool unlockHeap()
    {
        bool wasLocked = heapLocked;

        if (heapLock.Handle() && heapLocked) {
            heapLock.Signal();
            heapLocked = false;
        }

        return wasLocked;
    }


private:

    RMutex heapLock;
    bool heapLocked;
};

Q_GLOBAL_STATIC(QSymbianFbsClient, qt_symbianFbsClient);



// QSymbianFbsHeapLock

QSymbianFbsHeapLock::QSymbianFbsHeapLock(LockAction a)
: action(a), wasLocked(false)
{
    QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
    if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3)
        wasLocked = qt_symbianFbsClient()->unlockHeap();
}

QSymbianFbsHeapLock::~QSymbianFbsHeapLock()
{
    // Do nothing
}

void QSymbianFbsHeapLock::relock()
{
    QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
    if (wasLocked && (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3))
        qt_symbianFbsClient()->lockHeap();
}

/*
    \class QSymbianBitmapDataAccess
    \since 4.6
    \internal

    Data access class that is used to locks/unlocks pixel data
    when drawing or modifying CFbsBitmap pixel data.
*/
class QSymbianBitmapDataAccess
{
public:

    bool heapWasLocked;
    QSysInfo::SymbianVersion symbianVersion;

    explicit QSymbianBitmapDataAccess() : heapWasLocked(false)
    {
        symbianVersion = QSysInfo::symbianVersion();
    };

    ~QSymbianBitmapDataAccess() {};

    inline void beginDataAccess(CFbsBitmap *bitmap)
    {
        if (symbianVersion == QSysInfo::SV_9_2)
            heapWasLocked = qt_symbianFbsClient()->lockHeap();
        else
            bitmap->LockHeap(ETrue);
    }

    inline void endDataAccess(CFbsBitmap *bitmap)
    {
        if (symbianVersion == QSysInfo::SV_9_2) {
            if (!heapWasLocked)
                qt_symbianFbsClient()->unlockHeap();
        } else {
            bitmap->UnlockHeap(ETrue);
        }
    }
};


#define UPDATE_BUFFER()     \
    {                       \
    beginDataAccess();      \
    endDataAccess();        \
}


static CFbsBitmap* createSymbianCFbsBitmap(const TSize& size, TDisplayMode mode)
{
    QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);

    CFbsBitmap* bitmap = 0;
    QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap);

    if (bitmap->Create(size, mode) != KErrNone) {
        delete bitmap;
        bitmap = 0;
    }

    lock.relock();

    return bitmap;
}

static CFbsBitmap* uncompress(CFbsBitmap* bitmap)
{
    if(bitmap->IsCompressedInRAM()) {
        QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);

        CFbsBitmap *uncompressed = 0;
        QT_TRAP_THROWING(uncompressed = new (ELeave) CFbsBitmap);

        if (uncompressed->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) {
            delete bitmap;
            bitmap = 0;
            lock.relock();

            return bitmap;
        }

        lock.relock();

        CFbsBitmapDevice* bitmapDevice = 0;
        CFbsBitGc *bitmapGc = 0;
        QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(uncompressed));
        QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL());
        bitmapGc->Activate(bitmapDevice);

        bitmapGc->BitBlt(TPoint(), bitmap);

        delete bitmapGc;
        delete bitmapDevice;

        return uncompressed;
    } else {
        return bitmap;
    }
}

QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h)
{
    CWsScreenDevice* screenDevice = S60->screenDevice();
    TSize screenSize = screenDevice->SizeInPixels();

    TSize srcSize;
    // Find out if this is one of our windows.
    QSymbianControl *sControl;
    sControl = winId->MopGetObject(sControl);
    if (sControl && sControl->widget()->windowType() == Qt::Desktop) {
        // Grabbing desktop widget
        srcSize = screenSize;
    } else {
        TPoint relativePos = winId->PositionRelativeToScreen();
        x += relativePos.iX;
        y += relativePos.iY;
        srcSize = winId->Size();
    }

    TRect srcRect(TPoint(x, y), srcSize);
    // Clip to the screen
    srcRect.Intersection(TRect(screenSize));

    if (w > 0 && h > 0) {
        TRect subRect(TPoint(x, y), TSize(w, h));
        // Clip to the subRect
        srcRect.Intersection(subRect);
    }

    if (srcRect.IsEmpty())
        return QPixmap();

    CFbsBitmap* temporary = createSymbianCFbsBitmap(srcRect.Size(), screenDevice->DisplayMode());

    QPixmap pix;

    if (temporary && screenDevice->CopyScreenToBitmap(temporary, srcRect) == KErrNone) {
        pix = QPixmap::fromSymbianCFbsBitmap(temporary);
    }

    delete temporary;
    return pix;
}

/*!
    \fn CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const
    \since 4.6

    Creates a \c CFbsBitmap that is equivalent to the QPixmap. Internally this
    function will try to duplicate the handle instead of copying the data,
    however in scenarios where this is not possible the data will be copied.
    If the creation fails or the pixmap is null, then this function returns 0.

    It is the caller's responsibility to release the \c CFbsBitmap data
    after use either by deleting the bitmap or calling \c Reset().

    \warning On S60 3.1 and S60 3.2, semi-transparent pixmaps are always copied
             and not duplicated.
    \warning This function is only available on Symbian OS.

    \sa fromSymbianCFbsBitmap()
*/
CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const
{
    QPixmapData *data = pixmapData();
    if (!data || data->isNull())
        return 0;

    return reinterpret_cast<CFbsBitmap*>(data->toNativeType(QPixmapData::FbsBitmap));
}

/*!
    \fn QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap)
    \since 4.6

    Creates a QPixmap from a \c CFbsBitmap \a bitmap. Internally this function
    will try to duplicate the bitmap handle instead of copying the data, however
    in scenarios where this is not possible the data will be copied.
    To be sure that QPixmap does not modify your original instance, you should
    make a copy of your \c CFbsBitmap before calling this function.
    If the CFbsBitmap is not valid this function will return a null QPixmap.

    \warning This function is only available on Symbian OS.

    \sa toSymbianCFbsBitmap(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
*/
QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap)
{
    if (!bitmap)
        return QPixmap();

    QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType));
    data->fromNativeType(reinterpret_cast<void*>(bitmap), QPixmapData::FbsBitmap);
    QPixmap pixmap(data.take());
    return pixmap;
}

QS60PixmapData::QS60PixmapData(PixelType type) : QRasterPixmapData(type),
    symbianBitmapDataAccess(new QSymbianBitmapDataAccess),
    cfbsBitmap(0),
    pengine(0),
    bytes(0),
    formatLocked(false)
{

}

QS60PixmapData::~QS60PixmapData()
{
    release();
    delete symbianBitmapDataAccess;
}

void QS60PixmapData::resize(int width, int height)
{
    if (width <= 0 || height <= 0) {
        w = width;
        h = height;
        is_null = true;

        release();
        return;
    } else if (!cfbsBitmap) {
        TDisplayMode mode;
        if (pixelType() == BitmapType)
            mode = EGray2;
        else
            mode = EColor16MU;

        CFbsBitmap* bitmap = createSymbianCFbsBitmap(TSize(width, height), mode);
        fromSymbianBitmap(bitmap);
    } else {

        TSize newSize(width, height);

        if(cfbsBitmap->SizeInPixels() != newSize) {
            cfbsBitmap->Resize(TSize(width, height));
            if(pengine) {
                delete pengine;
                pengine = 0;
            }
        }

        UPDATE_BUFFER();
    }
}

void QS60PixmapData::release()
{
    if (cfbsBitmap) {
        QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
        delete cfbsBitmap;
        lock.relock();
    }

    delete pengine;
    image = QImage();
    cfbsBitmap = 0;
    pengine = 0;
    bytes = 0;
}

/*!
 * Takes ownership of bitmap. Used by window surface
 */
void QS60PixmapData::fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat)
{
    Q_ASSERT(bitmap);

    release();

    cfbsBitmap = bitmap;
    formatLocked = lockFormat;

    setSerialNumber(cfbsBitmap->Handle());

    UPDATE_BUFFER();

    // Create default palette if needed
    if (cfbsBitmap->DisplayMode() == EGray2) {
        image.setColorCount(2);
        image.setColor(0, QColor(Qt::color0).rgba());
        image.setColor(1, QColor(Qt::color1).rgba());

        //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
        //So invert mono bitmaps so that masks work correctly.
        image.invertPixels();
    } else if (cfbsBitmap->DisplayMode() == EGray256) {
        for (int i=0; i < 256; ++i)
            image.setColor(i, qRgb(i, i, i));
    } else if (cfbsBitmap->DisplayMode() == EColor256) {
        const TColor256Util *palette = TColor256Util::Default();
        for (int i=0; i < 256; ++i)
            image.setColor(i, (QRgb)(palette->Color256(i).Value()));
    }
}

QImage QS60PixmapData::toImage(const QRect &r) const
{
    QS60PixmapData *that = const_cast<QS60PixmapData*>(this);
    that->beginDataAccess();
    QImage copy = that->image.copy(r);
    that->endDataAccess();

    return copy;
}

void QS60PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags)
{
    release();

    QImage sourceImage;

    if (pixelType() == BitmapType) {
        sourceImage = img.convertToFormat(QImage::Format_MonoLSB);
    } else {
        if (img.depth() == 1) {
            sourceImage = img.hasAlphaChannel()
                        ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied)
                        : img.convertToFormat(QImage::Format_RGB32);
        } else {

            QImage::Format opaqueFormat = QNativeImage::systemFormat();
            QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;

            if (!img.hasAlphaChannel()
                || ((flags & Qt::NoOpaqueDetection) == 0
                    && !const_cast<QImage &>(img).data_ptr()->checkForAlphaPixels())) {
                sourceImage = img.convertToFormat(opaqueFormat);
            } else {
                sourceImage = img.convertToFormat(alphaFormat);
            }
        }
    }


    QImage::Format destFormat = sourceImage.format();
    TDisplayMode mode;
    switch (destFormat) {
    case QImage::Format_MonoLSB:
        mode = EGray2;
        break;
    case QImage::Format_RGB32:
        mode = EColor16MU;
        break;
    case QImage::Format_ARGB32_Premultiplied:
        if (S60->supportsPremultipliedAlpha) {
            mode = Q_SYMBIAN_ECOLOR16MAP;
            break;
        } else {
            destFormat = QImage::Format_ARGB32;
        }
        // Fall through intended
    case QImage::Format_ARGB32:
        mode = EColor16MA;
        break;
    case QImage::Format_Invalid:
        return;
    default:
        qWarning("Image format not supported: %d", image.format());
        return;
    }

    cfbsBitmap = createSymbianCFbsBitmap(TSize(sourceImage.width(), sourceImage.height()), mode);
    if (!cfbsBitmap) {
        qWarning("Could not create CFbsBitmap");
        release();
        return;
    }

    setSerialNumber(cfbsBitmap->Handle());

    const uchar *sptr = const_cast<const QImage &>(sourceImage).bits();
    symbianBitmapDataAccess->beginDataAccess(cfbsBitmap);
    uchar *dptr = (uchar*)cfbsBitmap->DataAddress();
    Mem::Copy(dptr, sptr, sourceImage.byteCount());
    symbianBitmapDataAccess->endDataAccess(cfbsBitmap);

    UPDATE_BUFFER();

    if (destFormat == QImage::Format_MonoLSB) {
		image.setColorCount(2);
		image.setColor(0, QColor(Qt::color0).rgba());
		image.setColor(1, QColor(Qt::color1).rgba());
	} else {
		image.setColorTable(sourceImage.colorTable());
	}
}

void QS60PixmapData::copy(const QPixmapData *data, const QRect &rect)
{
    const QS60PixmapData *s60Data = static_cast<const QS60PixmapData*>(data);
    fromImage(s60Data->toImage(rect), Qt::AutoColor | Qt::OrderedAlphaDither);
}

bool QS60PixmapData::scroll(int dx, int dy, const QRect &rect)
{
    beginDataAccess();
    bool res = QRasterPixmapData::scroll(dx, dy, rect);
    endDataAccess();
    return res;
}

int QS60PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
{
    if (!cfbsBitmap)
        return 0;

    switch (metric) {
    case QPaintDevice::PdmWidth:
        return cfbsBitmap->SizeInPixels().iWidth;
    case QPaintDevice::PdmHeight:
        return cfbsBitmap->SizeInPixels().iHeight;
    case QPaintDevice::PdmWidthMM: {
        TInt twips = cfbsBitmap->SizeInTwips().iWidth;
        return (int)(twips * (25.4/KTwipsPerInch));
    }
    case QPaintDevice::PdmHeightMM: {
        TInt twips = cfbsBitmap->SizeInTwips().iHeight;
        return (int)(twips * (25.4/KTwipsPerInch));
    }
    case QPaintDevice::PdmNumColors:
        return TDisplayModeUtils::NumDisplayModeColors(cfbsBitmap->DisplayMode());
    case QPaintDevice::PdmDpiX:
    case QPaintDevice::PdmPhysicalDpiX: {
        TReal inches = cfbsBitmap->SizeInTwips().iWidth / (TReal)KTwipsPerInch;
        TInt pixels = cfbsBitmap->SizeInPixels().iWidth;
        return pixels / inches;
    }
    case QPaintDevice::PdmDpiY:
    case QPaintDevice::PdmPhysicalDpiY: {
        TReal inches = cfbsBitmap->SizeInTwips().iHeight / (TReal)KTwipsPerInch;
        TInt pixels = cfbsBitmap->SizeInPixels().iHeight;
        return pixels / inches;
    }
    case QPaintDevice::PdmDepth:
        return TDisplayModeUtils::NumDisplayModeBitsPerPixel(cfbsBitmap->DisplayMode());
    default:
        qWarning("QPixmap::metric: Invalid metric command");
    }
    return 0;

}

void QS60PixmapData::fill(const QColor &color)
{
    if (color.alpha() != 255) {
        QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
        im.fill(PREMUL(color.rgba()));
        release();
        fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
    } else {
        beginDataAccess();
        QRasterPixmapData::fill(color);
        endDataAccess();
    }
}

void QS60PixmapData::setMask(const QBitmap &mask)
{
    if (mask.size().isEmpty()) {
        if (image.depth() != 1) {
            QImage newImage = image.convertToFormat(QImage::Format_RGB32);
            release();
            fromImage(newImage,  Qt::AutoColor | Qt::OrderedAlphaDither);
        }
    } else if (image.depth() == 1) {
        beginDataAccess();
        QRasterPixmapData::setMask(mask);
        endDataAccess();
    } else {
        const int w = image.width();
        const int h = image.height();

        const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB);
        QImage newImage = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
        for (int y = 0; y < h; ++y) {
            const uchar *mscan = imageMask.scanLine(y);
            QRgb *tscan = (QRgb *)newImage.scanLine(y);
            for (int x = 0; x < w; ++x) {
                if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7]))
                    tscan[x] = 0;
            }
        }
        release();
        fromImage(newImage,  Qt::AutoColor | Qt::OrderedAlphaDither);
    }
}

void QS60PixmapData::setAlphaChannel(const QPixmap &alphaChannel)
{
    QImage img(toImage());
    img.setAlphaChannel(alphaChannel.toImage());
    release();
    fromImage(img, Qt::OrderedDither | Qt::OrderedAlphaDither);
}

QImage QS60PixmapData::toImage() const
{
    return toImage(QRect());
}

QPaintEngine* QS60PixmapData::paintEngine() const
{
    if (!pengine) {
        QS60PixmapData *that = const_cast<QS60PixmapData*>(this);
        that->pengine = new QS60PaintEngine(&that->image, that);
    }
    return pengine;
}

void QS60PixmapData::beginDataAccess()
{
    if(!cfbsBitmap)
        return;

    symbianBitmapDataAccess->beginDataAccess(cfbsBitmap);

    uchar* newBytes = (uchar*)cfbsBitmap->DataAddress();

    TSize size = cfbsBitmap->SizeInPixels();

    if (newBytes == bytes && image.width() == size.iWidth && image.height() == size.iHeight)
        return;

    bytes = newBytes;
    TDisplayMode mode = cfbsBitmap->DisplayMode();
    QImage::Format format = qt_TDisplayMode2Format(mode);
    // On S60 3.1, premultiplied alpha pixels are stored in a bitmap with 16MA type.
    // S60 window surface needs backing store pixmap for transparent window in ARGB32 format.
    // In that case formatLocked is true.
    if (!formatLocked && format == QImage::Format_ARGB32)
        format = QImage::Format_ARGB32_Premultiplied; // pixel data is actually in premultiplied format

    QVector<QRgb> savedColorTable;
    if (!image.isNull())
        savedColorTable = image.colorTable();

    image = QImage(bytes, size.iWidth, size.iHeight, format);

    // Restore the palette or create a default
    if (!savedColorTable.isEmpty()) {
        image.setColorTable(savedColorTable);
    }

    w = size.iWidth;
    h = size.iHeight;
    d = image.depth();
    is_null = (w <= 0 || h <= 0);

    if (pengine) {
        QS60PaintEngine *engine = static_cast<QS60PaintEngine *>(pengine);
        engine->prepare(&image);
    }
}

void QS60PixmapData::endDataAccess(bool readOnly) const
{
    Q_UNUSED(readOnly);

    if(!cfbsBitmap)
        return;

    symbianBitmapDataAccess->endDataAccess(cfbsBitmap);
}

/*!
  \since 4.6

  Returns a QPixmap that wraps given \a sgImage graphics resource.
  The data should be valid even when original RSgImage handle has been
  closed.

  \warning This function is only available on Symbian OS.

  \sa toSymbianRSgImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
*/

QPixmap QPixmap::fromSymbianRSgImage(RSgImage *sgImage)
{
    // It is expected that RSgImage will
    // CURRENTLY be used in conjuction with
    // OpenVG graphics system
    //
    // Surely things might change in future

    if (!sgImage)
        return QPixmap();

    QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType));
    data->fromNativeType(reinterpret_cast<void*>(sgImage), QPixmapData::SgImage);
    QPixmap pixmap(data.take());
    return pixmap;
}

/*!
\since 4.6

Returns a \c RSgImage that is equivalent to the QPixmap by copying the data.

It is the caller's responsibility to close/delete the \c RSgImage after use.

\warning This function is only available on Symbian OS.

\sa fromSymbianRSgImage()
*/

RSgImage *QPixmap::toSymbianRSgImage() const
{
    // It is expected that RSgImage will
    // CURRENTLY be used in conjuction with
    // OpenVG graphics system
    //
    // Surely things might change in future

    if (isNull())
        return 0;

    RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmapData()->toNativeType(QPixmapData::SgImage));

    return sgImage;
}

void* QS60PixmapData::toNativeType(NativeType type)
{
    if (type == QPixmapData::SgImage) {
        return 0;
    } else if (type == QPixmapData::FbsBitmap) {

        if (isNull() || !cfbsBitmap)
            return 0;

        bool convertToArgb32 = false;
        bool needsCopy = false;

        QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion();
        if (!(S60->supportsPremultipliedAlpha)) {
            // Convert argb32_premultiplied to argb32 since Symbian 9.2 does
            // not support premultipied format.

            if (image.format() == QImage::Format_ARGB32_Premultiplied) {
                needsCopy = true;
                convertToArgb32 = true;
            }
        }

        CFbsBitmap *bitmap = 0;

        TDisplayMode displayMode = cfbsBitmap->DisplayMode();

        if(displayMode == EGray2) {
            //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
            //So invert mono bitmaps so that masks work correctly.
            beginDataAccess();
            image.invertPixels();
            endDataAccess();
            needsCopy = true;
        }

        if (needsCopy) {
            QImage source;

            if (convertToArgb32) {
                beginDataAccess();
                source = image.convertToFormat(QImage::Format_ARGB32);
                endDataAccess();
                displayMode = EColor16MA;
            } else {
                source = image;
            }

            CFbsBitmap *newBitmap = createSymbianCFbsBitmap(TSize(source.width(), source.height()), displayMode);
            const uchar *sptr = source.bits();
            symbianBitmapDataAccess->beginDataAccess(newBitmap);

            uchar *dptr = (uchar*)newBitmap->DataAddress();
            Mem::Copy(dptr, sptr, source.byteCount());

            symbianBitmapDataAccess->endDataAccess(newBitmap);

            bitmap = newBitmap;
        } else {

            QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap);

            TInt err = bitmap->Duplicate(cfbsBitmap->Handle());
            if (err != KErrNone) {
                qWarning("Could not duplicate CFbsBitmap");
                delete bitmap;
                bitmap = 0;
            }
        }

        if(displayMode == EGray2) {
            // restore pixels
            beginDataAccess();
            image.invertPixels();
            endDataAccess();
        }

        return reinterpret_cast<void*>(bitmap);

    }

    return 0;
}

void QS60PixmapData::fromNativeType(void* pixmap, NativeType nativeType)
{
    if (nativeType == QPixmapData::SgImage) {
        return;
    } else if (nativeType == QPixmapData::FbsBitmap && pixmap) {

        CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap);

        bool deleteSourceBitmap = false;
        bool needsCopy = false;

#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE

        // Rasterize extended bitmaps

        TUid extendedBitmapType = bitmap->ExtendedBitmapType();
        if (extendedBitmapType != KNullUid) {
            CFbsBitmap *rasterBitmap = createSymbianCFbsBitmap(bitmap->SizeInPixels(), EColor16MA);

            CFbsBitmapDevice *rasterBitmapDev = 0;
            QT_TRAP_THROWING(rasterBitmapDev = CFbsBitmapDevice::NewL(rasterBitmap));

            CFbsBitGc *rasterBitmapGc = 0;
            TInt err = rasterBitmapDev->CreateContext(rasterBitmapGc);
            if (err != KErrNone) {
                delete rasterBitmap;
                delete rasterBitmapDev;
                rasterBitmapDev = 0;
                return;
            }

            rasterBitmapGc->BitBlt(TPoint( 0, 0), bitmap);

            bitmap = rasterBitmap;

            delete rasterBitmapDev;
            delete rasterBitmapGc;

            rasterBitmapDev = 0;
            rasterBitmapGc = 0;

            deleteSourceBitmap = true;
        }
#endif


        deleteSourceBitmap = bitmap->IsCompressedInRAM();
        CFbsBitmap *sourceBitmap = uncompress(bitmap);

        TDisplayMode displayMode = sourceBitmap->DisplayMode();
        QImage::Format format = qt_TDisplayMode2Format(displayMode);

        QImage::Format opaqueFormat = QNativeImage::systemFormat();
        QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied;

        if (format != opaqueFormat && format != alphaFormat && format != QImage::Format_MonoLSB)
            needsCopy = true;


        type = (format != QImage::Format_MonoLSB)
                    ? QPixmapData::PixmapType
                    : QPixmapData::BitmapType;

        if (needsCopy) {

            TSize size = sourceBitmap->SizeInPixels();

            QSymbianBitmapDataAccess da;
            da.beginDataAccess(sourceBitmap);
            uchar *bytes = (uchar*)sourceBitmap->DataAddress();
            QImage img = QImage(bytes, size.iWidth, size.iHeight, format);
            img = img.copy();
            da.endDataAccess(sourceBitmap);

            if(displayMode == EGray2) {
                //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid
                //So invert mono bitmaps so that masks work correctly.
                img.invertPixels();
            } else if(displayMode == EColor16M) {
                img = img.rgbSwapped(); // EColor16M is BGR
            }

            fromImage(img, Qt::AutoColor);

            if(deleteSourceBitmap)
                delete sourceBitmap;
        } else {
            CFbsBitmap* duplicate = 0;
            QT_TRAP_THROWING(duplicate = new (ELeave) CFbsBitmap);

            TInt err = duplicate->Duplicate(sourceBitmap->Handle());
            if (err != KErrNone) {
                qWarning("Could not duplicate CFbsBitmap");

                if(deleteSourceBitmap)
                    delete sourceBitmap;

                delete duplicate;
                return;
            }

            fromSymbianBitmap(duplicate);

            if(deleteSourceBitmap)
                delete sourceBitmap;
        }
    }
}

QPixmapData *QS60PixmapData::createCompatiblePixmapData() const
{
    return new QS60PixmapData(pixelType());
}

QT_END_NAMESPACE
