diff -r 000000000000 -r 1918ee327afb src/gui/image/qpixmap_s60.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gui/image/qpixmap_s60.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,973 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui 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 +#include +#include + +#include +#include +#include +#include + +#include "qpixmap.h" +#include "qpixmap_raster_p.h" +#include +#include "qpixmap_s60_p.h" +#include "qnativeimage_p.h" +#include "qbitmap.h" +#include "qimage.h" +#include "qimage_p.h" + +#include + +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. +*/ +class QSymbianFbsClient +{ +public: + + QSymbianFbsClient() : heapLock(0), heapLocked(false) + { + QT_TRAP_THROWING(heapLock = new(ELeave) CFbsBitmap); + heapLock->Create(TSize(0,0), S60->screenDevice()->DisplayMode()); + } + + ~QSymbianFbsClient() + { + delete heapLock; + } + + bool lockHeap() + { + bool wasLocked = heapLocked; + + if (heapLock && !heapLocked) { + heapLock->LockHeap(ETrue); + heapLocked = true; + } + + return wasLocked; + } + + bool unlockHeap() + { + bool wasLocked = heapLocked; + + if (heapLock && heapLocked) { + heapLock->UnlockHeap(ETrue); + heapLocked = false; + } + + return wasLocked; + } + + +private: + + CFbsBitmap *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 || symbianVersion == QSysInfo::SV_9_3) + heapWasLocked = qt_symbianFbsClient()->lockHeap(); + else + bitmap->LockHeap(ETrue); + } + + inline void endDataAccess(CFbsBitmap *bitmap) + { + if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3) { + 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->DrawBitmap(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->isNull()) + return 0; + + return reinterpret_cast(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(); + + QPixmap pixmap; + pixmap.pixmapData()->fromNativeType(reinterpret_cast(bitmap), QPixmapData::FbsBitmap); + return pixmap; +} + +QS60PixmapData::QS60PixmapData(PixelType type) : QRasterPixmapData(type), + symbianBitmapDataAccess(new QSymbianBitmapDataAccess), + cfbsBitmap(0), + bitmapDevice(0), + bitmapGc(0), + pengine(0), + bytes(0) +{ + +} + +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)); + bitmapDevice->Resize(TSize(width, height)); + bitmapGc->Resized(); + if(pengine) { + delete pengine; + pengine = 0; + } + } + + UPDATE_BUFFER(); + } +} + +bool QS60PixmapData::initSymbianBitmapContext() +{ + QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(cfbsBitmap)); + QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL()); + bitmapGc->Activate(bitmapDevice); + + return true; +} + +void QS60PixmapData::release() +{ + if (cfbsBitmap) { + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + delete bitmapGc; + delete bitmapDevice; + delete cfbsBitmap; + lock.relock(); + } + + delete pengine; + image = QImage(); + cfbsBitmap = 0; + bitmapGc = 0; + bitmapDevice = 0; + pengine = 0; + bytes = 0; +} + +/*! + * Takes ownership of bitmap + */ +void QS60PixmapData::fromSymbianBitmap(CFbsBitmap* bitmap) +{ + cfbsBitmap = bitmap; + + if(!initSymbianBitmapContext()) { + qWarning("Could not create CBitmapContext"); + release(); + return; + } + + setSerialNumber(cfbsBitmap->Handle()); + + UPDATE_BUFFER(); + + // Create default palette if needed + if (cfbsBitmap->DisplayMode() == EGray2) { + image.setNumColors(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())); + } +} + +void QS60PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags) +{ + 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(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 && initSymbianBitmapContext())) { + qWarning("Could not create CFbsBitmap and/or CBitmapContext"); + release(); + return; + } + + setSerialNumber(cfbsBitmap->Handle()); + + const uchar *sptr = const_cast(sourceImage).bits(); + symbianBitmapDataAccess->beginDataAccess(cfbsBitmap); + uchar *dptr = (uchar*)cfbsBitmap->DataAddress(); + Mem::Copy(dptr, sptr, sourceImage.numBytes()); + symbianBitmapDataAccess->endDataAccess(cfbsBitmap); + + UPDATE_BUFFER(); + + if (destFormat == QImage::Format_MonoLSB) { + image.setNumColors(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) +{ + if (data->pixelType() == BitmapType) { + QBitmap::fromImage(data->toImage().copy(rect)); + return; + } + + const QS60PixmapData *s60Data = static_cast(data); + + resize(rect.width(), rect.height()); + cfbsBitmap->SetDisplayMode(s60Data->cfbsBitmap->DisplayMode()); + + bitmapGc->BitBlt(TPoint(0, 0), s60Data->cfbsBitmap, qt_QRect2TRect(rect)); +} + +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 +{ + QS60PixmapData *that = const_cast(this); + that->beginDataAccess(); + QImage copy = that->image.copy(); + that->endDataAccess(); + + return copy; +} + +QPaintEngine* QS60PixmapData::paintEngine() const +{ + if (!pengine) { + QS60PixmapData *that = const_cast(this); + that->pengine = new QS60PaintEngine(&that->image, that); + } + return pengine; +} + +void QS60PixmapData::beginDataAccess() +{ + if(!cfbsBitmap) + return; + + symbianBitmapDataAccess->beginDataAccess(cfbsBitmap); + + uchar* newBytes = (uchar*)cfbsBitmap->DataAddress(); + + if (newBytes == bytes) + 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 + if (format == QImage::Format_ARGB32) + format = QImage::Format_ARGB32_Premultiplied; // pixel data is actually in premultiplied format + + TSize size = cfbsBitmap->SizeInPixels(); + + QVector 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(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(); + + QPixmap pixmap; + pixmap.pixmapData()->fromNativeType(reinterpret_cast(sgImage), QPixmapData::SgImage); + + 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(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. + image.invertPixels(); + needsCopy = true; + } + + if (needsCopy) { + QImage source; + + if (convertToArgb32) { + source = image.convertToFormat(QImage::Format_ARGB32); + 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.numBytes()); + + 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 + image.invertPixels(); + } + + return reinterpret_cast(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(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); + da.endDataAccess(sourceBitmap); + + fromImage(img, Qt::AutoColor); + + if(deleteSourceBitmap) + delete 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. + image.invertPixels(); + } + } 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; + } + } +} + +QT_END_NAMESPACE