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 | |
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')
-rw-r--r-- | src/gui/image/image.pri | 1 | ||||
-rw-r--r-- | src/gui/image/qpixmap.cpp | 13 | ||||
-rw-r--r-- | src/gui/image/qpixmap.h | 7 | ||||
-rw-r--r-- | src/gui/image/qpixmap_raster_p.h | 2 | ||||
-rw-r--r-- | src/gui/image/qpixmap_s60.cpp | 891 | ||||
-rw-r--r-- | src/gui/image/qpixmap_s60_p.h | 97 | ||||
-rw-r--r-- | src/gui/image/qpixmapdatafactory.cpp | 9 |
7 files changed, 894 insertions, 126 deletions
diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 0970385..f365f66 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -70,6 +70,7 @@ mac { SOURCES += image/qpixmap_mac.cpp } symbian { + HEADERS += image/qpixmap_s60_p.h SOURCES += image/qpixmap_s60.cpp } diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index eb38b85..4f145af 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -1557,8 +1557,8 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) premultiplied alpha format. If the image has an alpha channel, and if the system allows, the preferred format is premultiplied alpha. Note also that QPixmap, unlike QImage, may be hardware dependent. - On X11 and Mac, a QPixmap is stored on the server side while a - QImage is stored on the client side (on Windows, these two classes + On X11, Mac and Symbian, a QPixmap is stored on the server side while + a QImage is stored on the client side (on Windows, these two classes have an equivalent internal representation, i.e. both QImage and QPixmap are stored on the client side and don't use any GDI resources). @@ -1577,7 +1577,8 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) screen. Alternatively, if no manipulation is desired, the image file can be loaded directly into a QPixmap. On Windows, the QPixmap class also supports conversion between \c HBITMAP and - QPixmap. + QPixmap. On Symbian, the QPixmap class also supports conversion + between CFbsBitmap and QPixmap. QPixmap provides a collection of functions that can be used to obtain a variety of information about the pixmap. In addition, @@ -1682,6 +1683,12 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) returns the HBITMAP handle. The fromWinHBITMAP() function returns a QPixmap that is equivalent to the given bitmap which has the specified format. + + In addition, on Symbian, the QPixmap class supports conversion to + and from CFbsBitmap: the toSymbianCFbsBitmap() function creates + CFbsBitmap equivalent to the QPixmap, based on given mode and returns + a CFbsBitmap object. The fromSymbianCFbsBitmap() function returns a + QPixmap that is equivalent to the given bitmap and given mode. \section1 Pixmap Transformations diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index 1eaafdc..4a8a876 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -157,8 +157,10 @@ public: #endif #if defined(Q_OS_SYMBIAN) - CFbsBitmap *toSymbianCFbsBitmap() const; - static QPixmap fromSymbianCFbsBitmap(CFbsBitmap *bitmap); + enum ConversionMode { CopyData, DuplicateHandle }; + + CFbsBitmap *toSymbianCFbsBitmap(ConversionMode mode = DuplicateHandle) const; + static QPixmap fromSymbianCFbsBitmap(CFbsBitmap *bitmap, ConversionMode mode = DuplicateHandle); #endif inline QPixmap copy(int x, int y, int width, int height) const; @@ -254,6 +256,7 @@ private: friend class QPixmapData; friend class QX11PixmapData; friend class QMacPixmapData; + friend class QS60PixmapData; friend class QBitmap; friend class QPaintDevice; friend class QPainter; diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h index 5add5ed..2af2399 100644 --- a/src/gui/image/qpixmap_raster_p.h +++ b/src/gui/image/qpixmap_raster_p.h @@ -83,13 +83,13 @@ public: protected: int metric(QPaintDevice::PaintDeviceMetric metric) const; + QImage image; private: friend class QPixmap; friend class QBitmap; friend class QDetachedPixmap; friend class QRasterPaintEngine; - QImage image; }; QT_END_NAMESPACE 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 diff --git a/src/gui/image/qpixmap_s60_p.h b/src/gui/image/qpixmap_s60_p.h new file mode 100644 index 0000000..b56b270 --- /dev/null +++ b/src/gui/image/qpixmap_s60_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAPDATA_S60_P_H +#define QPIXMAPDATA_S60_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qpixmap_raster_p.h> + +QT_BEGIN_NAMESPACE + +class CFbsBitmap; +class CFbsBitmapDevice; +class CBitmapContext; + +class QSymbianBitmapDataAccess; + +class QSymbianFbsHeapLock +{ +public: + + enum LockAction { + Unlock + }; + + explicit QSymbianFbsHeapLock(LockAction a); + ~QSymbianFbsHeapLock(); + void relock(); + +private: + + LockAction action; + bool wasLocked; +}; + +class QS60PixmapData : public QRasterPixmapData +{ +public: + QS60PixmapData(PixelType type); + ~QS60PixmapData(); + + void resize(int width, int height); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, const QRect &rect); + + int metric(QPaintDevice::PaintDeviceMetric metric) const; + void fill(const QColor &color); + void setMask(const QBitmap &mask); + void setAlphaChannel(const QPixmap &alphaChannel); + QImage toImage() const; + QPaintEngine* paintEngine() const; + + void beginDataAccess(); + void endDataAccess(bool readOnly=false) const; + +private: + void release(); + void fromSymbianBitmap(CFbsBitmap* bitmap); + bool initSymbianBitmapContext(); + + QSymbianBitmapDataAccess *symbianBitmapDataAccess; + + CFbsBitmap *cfbsBitmap; + CFbsBitmapDevice *bitmapDevice; + CBitmapContext *bitmapContext; + QPaintEngine *pengine; + uchar* bytes; + + friend class QPixmap; + friend class QS60WindowSurface; + friend class QS60PaintEngine; + friend class QS60Data; +}; + +QT_END_NAMESPACE + +#endif // QPIXMAPDATA_S60_P_H + diff --git a/src/gui/image/qpixmapdatafactory.cpp b/src/gui/image/qpixmapdatafactory.cpp index e3d2e6c..806883d 100644 --- a/src/gui/image/qpixmapdatafactory.cpp +++ b/src/gui/image/qpixmapdatafactory.cpp @@ -47,12 +47,15 @@ #ifdef Q_WS_X11 # include <private/qpixmap_x11_p.h> #endif -#if defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_WIN) # include <private/qpixmap_raster_p.h> #endif #ifdef Q_WS_MAC # include <private/qpixmap_mac_p.h> #endif +#ifdef Q_WS_S60 +# include <private/qpixmap_s60_p.h> +#endif #include "private/qapplication_p.h" #include "private/qgraphicssystem_p.h" @@ -75,10 +78,12 @@ QPixmapData* QSimplePixmapDataFactory::create(QPixmapData::PixelType type) #if defined(Q_WS_X11) return new QX11PixmapData(type); -#elif defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) +#elif defined(Q_WS_WIN) return new QRasterPixmapData(type); #elif defined(Q_WS_MAC) return new QMacPixmapData(type); +#elif defined(Q_WS_S60) + return new QS60PixmapData(type); #else #error QSimplePixmapDataFactory::create() not implemented #endif |