diff options
author | Jani Hautakangas <ext-jani.hautakangas@nokia.com> | 2009-09-18 11:33:10 (GMT) |
---|---|---|
committer | Jani Hautakangas <ext-jani.hautakangas@nokia.com> | 2009-09-18 11:33:10 (GMT) |
commit | c78dabc55943b76479f7a84bae146f52cbc7bbbf (patch) | |
tree | 5ea4392cf14c1126c82603b9ee614c384f28bd9e /src/gui/image/qpixmap_s60.cpp | |
parent | 6454aca1b273daa2e54a77f83e1f6d4bae83427d (diff) | |
download | Qt-c78dabc55943b76479f7a84bae146f52cbc7bbbf.zip Qt-c78dabc55943b76479f7a84bae146f52cbc7bbbf.tar.gz Qt-c78dabc55943b76479f7a84bae146f52cbc7bbbf.tar.bz2 |
Introduce native Symbian bitmap support to QPixmap
This is done to reduce heap consumption and to give
a possibility to share bitmaps across process. QPixmap
maps to Symbian CFbsBitmap which is stored in Symbian
font and bitmap server.
Reviewed-by: Jason Barron
Diffstat (limited to 'src/gui/image/qpixmap_s60.cpp')
-rw-r--r-- | src/gui/image/qpixmap_s60.cpp | 891 |
1 files changed, 773 insertions, 118 deletions
diff --git a/src/gui/image/qpixmap_s60.cpp b/src/gui/image/qpixmap_s60.cpp index 666e557..8a6ecb0 100644 --- a/src/gui/image/qpixmap_s60.cpp +++ b/src/gui/image/qpixmap_s60.cpp @@ -43,17 +43,218 @@ #include <fbs.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 -QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h ) +const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 }; + + + +/*! + \since 4.6 + + 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(); +} + +/*! + \since 4.6 + + 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(); + + CBitmapContext *bitmapContext = 0; + CFbsBitmapDevice* bitmapDevice = 0; + QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(uncompressed)); + TInt err = bitmapDevice->CreateBitmapContext(bitmapContext); + if (err != KErrNone) { + delete bitmap; + delete bitmapDevice; + bitmap = 0; + bitmapDevice = 0; + + lock.relock(); + + return bitmap; + } + + bitmapContext->DrawBitmap(TPoint(), bitmap); + + delete bitmapContext; + 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; @@ -67,178 +268,632 @@ QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h ) 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(); - - TDisplayMode displayMode = screenDevice->DisplayMode(); - CFbsBitmap* temporary = q_check_ptr(new CFbsBitmap()); // CBase derived object needs check on new - TInt error = temporary->Create(srcRect.Size(), displayMode); - if (error == KErrNone) - error = screenDevice->CopyScreenToBitmap(temporary, srcRect); - - if (error != KErrNone) { - CBase::Delete(temporary); - return QPixmap(); + + CFbsBitmap* temporary = createSymbianCFbsBitmap(srcRect.Size(), screenDevice->DisplayMode()); + + QPixmap pix; + + if (temporary && screenDevice->CopyScreenToBitmap(temporary, srcRect) == KErrNone) { + pix = QPixmap::fromSymbianCFbsBitmap(temporary); } - - QPixmap pixmap = QPixmap::fromSymbianCFbsBitmap(temporary); - CBase::Delete(temporary); - return pixmap; + + delete temporary; + return pix; } /*! -\since 4.6 + \enum QPixmap::ConversionMode + + \bold{Symbian only:} This enum defines how the conversion between \c + CFbsBitmap and QPixmap is performed. -Returns a \c CFbsBitmap that is equivalent to the QPixmap by copying the data. + \warning This enum is only available on Symbian. -It is the caller's responsibility to delete the \c CFbsBitmap after use. + \value CopyData Copied CFbsBitmap data. -\warning This function is only available on Symbian OS. + \value DuplicateHandle Duplicates CFbsBitmap handle. This also means + that pixmap data will be explicitly shared. -\sa fromSymbianCFbsBitmap() + \sa fromSymbianCFbsBitmap(), toSymbianCFbsBitmap() */ -CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const + +/*! + \fn CFbsBitmap *QPixmap::toSymbianCFbsBitmap(ConversionMode mode) + \since 4.6 + + Creates \c CFbsBitmap that is equivalent to the QPixmap, based on + the given \a mode. If the creation 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 conversion mode will always be CopyData + if QPixmap pixels have alpha values. + \warning This function is only available on Symbian OS. + + \sa fromSymbianCFbsBitmap() +*/ +CFbsBitmap *QPixmap::toSymbianCFbsBitmap(ConversionMode mode) const { - if (isNull()) + QS60PixmapData *s60data = static_cast<QS60PixmapData *>(data.data()); + + if (isNull() || !s60data->cfbsBitmap) return 0; + + bool convertToArgb32 = false; + + QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion(); + if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3) { + // Convert argb32_premultiplied to argb32 since Symbian 9.2 and Symbian 9.3 do + // not support premultipied format. + + if (s60data->image.format() == QImage::Format_ARGB32_Premultiplied) { + mode = CopyData; + convertToArgb32 = true; + } + } + + CFbsBitmap *bitmap = 0; + + TDisplayMode displayMode = s60data->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. + s60data->image.invertPixels(); + mode = CopyData; + } + + if (mode == CopyData) { + QImage source; + + if (convertToArgb32) { + source = s60data->image.convertToFormat(QImage::Format_ARGB32); + displayMode = EColor16MA; + } else { + source = s60data->image; + } + + CFbsBitmap *newBitmap = createSymbianCFbsBitmap(TSize(source.width(), source.height()), displayMode); + const uchar *sptr = source.bits(); + s60data->symbianBitmapDataAccess->beginDataAccess(newBitmap); + + uchar *dptr = (uchar*)newBitmap->DataAddress(); + Mem::Copy(dptr, sptr, source.numBytes()); + + s60data->symbianBitmapDataAccess->endDataAccess(newBitmap); + + bitmap = newBitmap; + } else { + + QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap); + + TInt err = bitmap->Duplicate(s60data->cfbsBitmap->Handle()); + if (err != KErrNone) { + qWarning("Could not duplicate CFbsBitmap"); + delete bitmap; + bitmap = 0; + } + } + + if(displayMode == EGray2) { + // restore pixels + s60data->image.invertPixels(); + } + + return bitmap; +} + +/*! + \fn QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap, ConversionMode mode) + \since 4.6 + + Creates a QPixmap from native \c CFbsBitmap \a bitmap. The conversion + is based on the specified \a mode. Conversion mode is always QPixmap::CopyData + if given \a bitmap does not have display mode of TDisplayMode::EGray2, + \c TDisplayMode::EColor16MU or \c TDisplayMode::EColor16MAP. + + 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, ConversionMode mode) +{ + if (bitmap) { + + bool deleteSourceBitmap = false; + +#if 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 QPixmap(); + } + + 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) + mode = CopyData; + + + QPixmapData::PixelType type = (format!=QImage::Format_MonoLSB) + ? QPixmapData::PixmapType + : QPixmapData::BitmapType; + + QS60PixmapData *pixmapData = 0; + + if (mode == CopyData) { + + 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); + + pixmapData = new QS60PixmapData(type); + pixmapData->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. + pixmapData->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 QPixmap(); + } + + pixmapData = new QS60PixmapData(type); + pixmapData->fromSymbianBitmap(duplicate); + + if(deleteSourceBitmap) + delete sourceBitmap; + } + + return QPixmap(pixmapData); + } + + return QPixmap(); +} + +QS60PixmapData::QS60PixmapData(PixelType type) : QRasterPixmapData(type), + symbianBitmapDataAccess(new QSymbianBitmapDataAccess), + cfbsBitmap(0), + bitmapDevice(0), + bitmapContext(0), + pengine(0), + bytes(0) +{ + +} +QS60PixmapData::~QS60PixmapData() +{ + release(); +} + +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(); + } +} + +bool QS60PixmapData::initSymbianBitmapContext() +{ + QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(cfbsBitmap)); + TInt err = bitmapDevice->CreateBitmapContext(bitmapContext); + if (err != KErrNone) { + delete bitmapDevice; + bitmapDevice = 0; + return false; + } + return true; +} + +void QS60PixmapData::release() +{ + if (cfbsBitmap) { + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + delete bitmapContext; + delete bitmapDevice; + delete cfbsBitmap; + lock.relock(); + } + + delete pengine; + image = QImage(); + cfbsBitmap = 0; + bitmapContext = 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) { + image = 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; - const QImage img = toImage(); - QImage::Format destFormat = img.format(); - switch (img.format()) { - case QImage::Format_Mono: - destFormat = QImage::Format_MonoLSB; - // Fall through intended + switch (destFormat) { case QImage::Format_MonoLSB: mode = EGray2; break; - case QImage::Format_Indexed8: - if (img.isGrayscale()) - mode = EGray256; - else - mode = EColor256; - break; case QImage::Format_RGB32: mode = EColor16MU; break; - case QImage::Format_ARGB6666_Premultiplied: - case QImage::Format_ARGB8565_Premultiplied: - case QImage::Format_ARGB8555_Premultiplied: - destFormat = QImage::Format_ARGB32_Premultiplied; - // Fall through intended case QImage::Format_ARGB32_Premultiplied: #if !defined(__SERIES60_31__) && !defined(__S60_32__) - // ### TODO: Add runtime detection as well? mode = EColor16MAP; - break; + break; #endif destFormat = QImage::Format_ARGB32; // Fall through intended case QImage::Format_ARGB32: mode = EColor16MA; break; - case QImage::Format_RGB555: - destFormat = QImage::Format_RGB16; - // Fall through intended - case QImage::Format_RGB16: - mode = EColor64K; - break; - case QImage::Format_RGB666: - destFormat = QImage::Format_RGB888; - // Fall through intended - case QImage::Format_RGB888: - mode = EColor16M; - break; - case QImage::Format_RGB444: - mode = EColor4K; - break; case QImage::Format_Invalid: - return 0; + return; default: - qWarning("Image format not supported: %d", img.format()); - return 0; + 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<const QImage &>(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()); + } +} - CFbsBitmap* bitmap = q_check_ptr(new CFbsBitmap()); // CBase derived object needs check on new - TSize size(width(), height()); - if (bitmap->Create(size, mode) != KErrNone) { - CBase::Delete(bitmap); - return 0; +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<const QS60PixmapData*>(data); + + resize(rect.width(), rect.height()); + cfbsBitmap->SetDisplayMode(s60Data->cfbsBitmap->DisplayMode()); + + bitmapContext->BitBlt(TPoint(0, 0), s60Data->cfbsBitmap, qt_QRect2TRect(rect)); +} - QImage converted = img.convertToFormat(destFormat); +bool QS60PixmapData::scroll(int dx, int dy, const QRect &rect) +{ + bitmapContext->CopyRect(TPoint(dx, dy), qt_QRect2TRect(rect)); + return true; +} - //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid - //So invert mono bitmaps so that masks work correctly. - if (mode == EGray2) - converted.invertPixels(); +int QS60PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + if (!cfbsBitmap) + return 0; - bitmap->LockHeap(); - const uchar *sptr = converted.bits(); - uchar *dptr = (uchar*)bitmap->DataAddress(); - Mem::Copy(dptr, sptr, converted.numBytes()); - bitmap->UnlockHeap(); - return bitmap; -} + 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; -/*! - \since 4.6 +} - Returns a QPixmap that is equivalent to the \c CFbsBitmap \a bitmap - by copying the data. If the CFbsBitmap is not valid or is compressed - in memory, this function will return a null QPixmap. +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(); + } +} - \warning This function is only available on Symbian OS. +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); + } +} - \sa toSymbianCFbsBitmap(), {QPixmap#Pixmap Conversion}{Pixmap Conversion} -*/ +void QS60PixmapData::setAlphaChannel(const QPixmap &alphaChannel) +{ + QImage img(toImage()); + img.setAlphaChannel(alphaChannel.toImage()); + release(); + fromImage(img, Qt::OrderedDither | Qt::OrderedAlphaDither); +} -QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap) +QImage QS60PixmapData::toImage() const { - if (!bitmap) - return QPixmap(); + return image; +} - int width = bitmap->SizeInPixels().iWidth; - int height = bitmap->SizeInPixels().iHeight; +QPaintEngine* QS60PixmapData::paintEngine() const +{ + if (!pengine) { + QS60PixmapData *that = const_cast<QS60PixmapData*>(this); + that->pengine = new QS60PaintEngine(&that->image, that); + } + return pengine; +} - if (width <= 0 || height <= 0 || bitmap->IsCompressedInRAM()) - return QPixmap(); +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); + TSize size = cfbsBitmap->SizeInPixels(); + + 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); + } +} - TDisplayMode displayMode = bitmap->DisplayMode(); - QImage::Format format = qt_TDisplayMode2Format(displayMode); - int bytesPerLine = CFbsBitmap::ScanLineLength(width, displayMode); - bitmap->LockHeap(); - QImage image = QImage((const uchar*)bitmap->DataAddress(), width, height, bytesPerLine, format); - if (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 (displayMode == EGray256) { - for (int i=0; i < 256; ++i) - image.setColor(i, qRgb(i, i, i)); - }else if (displayMode == EColor256) { - const TColor256Util *palette = TColor256Util::Default(); - for (int i=0; i < 256; ++i) - image.setColor(i, (QRgb)(palette->Color256(i).Value())); - } - QPixmap pixmap = QPixmap::fromImage(image.copy()); - bitmap->UnlockHeap(); - return pixmap; +void QS60PixmapData::endDataAccess(bool readOnly) const +{ + if(!cfbsBitmap) + return; + + symbianBitmapDataAccess->endDataAccess(cfbsBitmap); } + QT_END_NAMESPACE |