diff options
Diffstat (limited to 'src')
40 files changed, 1043 insertions, 623 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index aeaca54..b198276 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1732,11 +1732,6 @@ public: NavigationModeCursorAuto, NavigationModeCursorForceVisible }; - - enum RenderHint { - QualityHint, - PerformanceHint - }; } #ifdef Q_MOC_RUN ; diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 4e369c9..c6f32d2 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -2965,18 +2965,3 @@ \sa QApplication::setNavigationMode() \sa QApplication::navigationMode() */ - -/*! - \enum Qt::RenderHint - \since 4.6 - - This enum describes the possible hints that can be used to control various - rendering operations. - - \value QualityHint Indicates that rendering quality is the most important factor, - at the potential cost of lower performance. - - \value PerformanceHint Indicates that rendering performance is the most important factor, - at the potential cost of lower quality. -*/ - diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp index 3815b60..83f4f79 100644 --- a/src/gui/effects/qgraphicseffect.cpp +++ b/src/gui/effects/qgraphicseffect.cpp @@ -97,6 +97,8 @@ */ #include "qgraphicseffect_p.h" +#include "private/qgraphicsitem_p.h" + #include <QtGui/qgraphicsitem.h> #include <QtGui/qimage.h> @@ -210,7 +212,23 @@ const QStyleOption *QGraphicsEffectSource::styleOption() const */ void QGraphicsEffectSource::draw(QPainter *painter) { - d_func()->draw(painter); + Q_D(const QGraphicsEffectSource); + + QPixmap pm; + if (QPixmapCache::find(d->m_cacheKey, &pm)) { + QTransform restoreTransform; + if (d->m_cachedSystem == Qt::DeviceCoordinates) { + restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + } + + painter->drawPixmap(d->m_cachedOffset, pm); + + if (d->m_cachedSystem == Qt::DeviceCoordinates) + painter->setWorldTransform(restoreTransform); + } else { + d_func()->draw(painter); + } } /*! @@ -260,6 +278,12 @@ QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offse return ((QGraphicsPixmapItem *) item)->pixmap(); } + if (system == Qt::DeviceCoordinates && item + && !static_cast<const QGraphicsItemEffectSourcePrivate *>(d_func())->info) { + qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); + return QPixmap(); + } + QPixmap pm; if (d->m_cachedSystem == system && d->m_cachedMode == mode) QPixmapCache::find(d->m_cacheKey, &pm); @@ -279,6 +303,13 @@ QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offse return pm; } +void QGraphicsEffectSourcePrivate::invalidateCache(bool effectRectChanged) const +{ + if (effectRectChanged && m_cachedMode != QGraphicsEffectSource::ExpandToEffectRectPadMode) + return; + QPixmapCache::remove(m_cacheKey); +} + /*! Constructs a new QGraphicsEffect instance having the specified \a parent. @@ -418,7 +449,7 @@ void QGraphicsEffect::updateBoundingRect() Q_D(QGraphicsEffect); if (d->source) { d->source->d_func()->effectBoundingRectChanged(); - d->source->d_func()->invalidateCache(); + d->source->d_func()->invalidateCache(true); } } @@ -606,6 +637,26 @@ void QGraphicsColorizeEffect::draw(QPainter *painter, QGraphicsEffectSource *sou */ /*! + \enum QGraphicsBlurEffect::BlurHint + \since 4.6 + + This enum describes the possible hints that can be used to control how + blur effects are applied. The hints might not have an effect in all the + paint engines. + + \value QualityHint Indicates that rendering quality is the most important factor, + at the potential cost of lower performance. + + \value PerformanceHint Indicates that rendering performance is the most important factor, + at the potential cost of lower quality. + + \value AnimationHint Indicates that the blur radius is going to be animated, hinting + that the implementation can keep a cache of blurred verisons of the source pixmap. + Do not use this hint if the source item is going to be dynamically changing. +*/ + + +/*! Constructs a new QGraphicsBlurEffect instance. The \a parent parameter is passed to QGraphicsEffect's constructor. */ @@ -613,7 +664,7 @@ QGraphicsBlurEffect::QGraphicsBlurEffect(QObject *parent) : QGraphicsEffect(*new QGraphicsBlurEffectPrivate, parent) { Q_D(QGraphicsBlurEffect); - d->filter->setBlurHint(Qt::PerformanceHint); + d->filter->setBlurHint(QGraphicsBlurEffect::PerformanceHint); } /*! @@ -660,20 +711,19 @@ void QGraphicsBlurEffect::setBlurRadius(qreal radius) \property QGraphicsBlurEffect::blurHint \brief the blur hint of the effect. - Use the Qt::PerformanceHint hint to say that you want a faster blur, - and the Qt::QualityHint hint to say that you prefer a higher quality blur. - - When animating the blur radius it's recommended to use Qt::PerformanceHint. + Use the PerformanceHint hint to say that you want a faster blur, + the QualityHint hint to say that you prefer a higher quality blur, + or the AnimationHint when you want to animate the blur radius. - By default, the blur hint is Qt::PerformanceHint. + By default, the blur hint is PerformanceHint. */ -Qt::RenderHint QGraphicsBlurEffect::blurHint() const +QGraphicsBlurEffect::BlurHint QGraphicsBlurEffect::blurHint() const { Q_D(const QGraphicsBlurEffect); return d->filter->blurHint(); } -void QGraphicsBlurEffect::setBlurHint(Qt::RenderHint hint) +void QGraphicsBlurEffect::setBlurHint(QGraphicsBlurEffect::BlurHint hint) { Q_D(QGraphicsBlurEffect); if (d->filter->blurHint() == hint) @@ -684,7 +734,7 @@ void QGraphicsBlurEffect::setBlurHint(Qt::RenderHint hint) } /*! - \fn void QGraphicsBlurEffect::blurHintChanged(Qt::RenderHint hint) + \fn void QGraphicsBlurEffect::blurHintChanged(Qt::BlurHint hint) This signal is emitted whenever the effect's blur hint changes. The \a hint parameter holds the effect's new blur hint. diff --git a/src/gui/effects/qgraphicseffect.h b/src/gui/effects/qgraphicseffect.h index bf18581..7335a25 100644 --- a/src/gui/effects/qgraphicseffect.h +++ b/src/gui/effects/qgraphicseffect.h @@ -183,22 +183,28 @@ class Q_GUI_EXPORT QGraphicsBlurEffect: public QGraphicsEffect { Q_OBJECT Q_PROPERTY(qreal blurRadius READ blurRadius WRITE setBlurRadius NOTIFY blurRadiusChanged) - Q_PROPERTY(Qt::RenderHint blurHint READ blurHint WRITE setBlurHint NOTIFY blurHintChanged) + Q_PROPERTY(BlurHint blurHint READ blurHint WRITE setBlurHint NOTIFY blurHintChanged) public: + enum BlurHint { + QualityHint, + PerformanceHint, + AnimationHint + }; + QGraphicsBlurEffect(QObject *parent = 0); ~QGraphicsBlurEffect(); QRectF boundingRectFor(const QRectF &rect) const; qreal blurRadius() const; - Qt::RenderHint blurHint() const; + BlurHint blurHint() const; public Q_SLOTS: void setBlurRadius(qreal blurRadius); - void setBlurHint(Qt::RenderHint hint); + void setBlurHint(BlurHint hint); Q_SIGNALS: void blurRadiusChanged(qreal blurRadius); - void blurHintChanged(Qt::RenderHint hint); + void blurHintChanged(BlurHint hint); protected: void draw(QPainter *painter, QGraphicsEffectSource *source); diff --git a/src/gui/effects/qgraphicseffect_p.h b/src/gui/effects/qgraphicseffect_p.h index 1ed7103..0ff5794 100644 --- a/src/gui/effects/qgraphicseffect_p.h +++ b/src/gui/effects/qgraphicseffect_p.h @@ -85,7 +85,7 @@ public: virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0, QGraphicsEffectSource::PixmapPadMode mode = QGraphicsEffectSource::ExpandToTransparentBorderPadMode) const = 0; virtual void effectBoundingRectChanged() = 0; - void invalidateCache() const { QPixmapCache::remove(m_cacheKey); } + void invalidateCache(bool effectRectChanged = false) const; friend class QGraphicsScenePrivate; friend class QGraphicsItem; diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index f72aa8a..c88f678 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -281,6 +281,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < #include <QtGui/qstyleoption.h> #include <QtGui/qinputcontext.h> #ifdef Q_WS_X11 +#include <QtGui/qpaintengine.h> #include <private/qt_x11_p.h> #endif @@ -3294,7 +3295,12 @@ void QGraphicsView::paintEvent(QPaintEvent *event) backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip); if (viewTransformed) backgroundPainter.setTransform(viewTransform); - backgroundPainter.setCompositionMode(QPainter::CompositionMode_Source); +#ifdef Q_WS_X11 +#undef X11 + if (backgroundPainter.paintEngine()->type() != QPaintEngine::X11) +#define X11 qt_x11Data +#endif + backgroundPainter.setCompositionMode(QPainter::CompositionMode_Source); drawBackground(&backgroundPainter, exposedSceneRect); d->backgroundPixmapExposed = QRegion(); } diff --git a/src/gui/image/qimagepixmapcleanuphooks.cpp b/src/gui/image/qimagepixmapcleanuphooks.cpp index ac30646..35322e9 100644 --- a/src/gui/image/qimagepixmapcleanuphooks.cpp +++ b/src/gui/image/qimagepixmapcleanuphooks.cpp @@ -40,7 +40,8 @@ ****************************************************************************/ #include "qimagepixmapcleanuphooks_p.h" -#include "qpixmapdata_p.h" +#include "private/qpixmapdata_p.h" +#include "private/qimage_p.h" QT_BEGIN_NAMESPACE @@ -132,4 +133,19 @@ void QImagePixmapCleanupHooks::executeImageHooks(qint64 key) qt_image_cleanup_hook_64(key); } +void QImagePixmapCleanupHooks::enableCleanupHooks(const QPixmap &pixmap) +{ + enableCleanupHooks(const_cast<QPixmap &>(pixmap).data_ptr().data()); +} + +void QImagePixmapCleanupHooks::enableCleanupHooks(QPixmapData *pixmapData) +{ + pixmapData->is_cached = true; +} + +void QImagePixmapCleanupHooks::enableCleanupHooks(const QImage &image) +{ + const_cast<QImage &>(image).data_ptr()->is_cached = true; +} + QT_END_NAMESPACE diff --git a/src/gui/image/qimagepixmapcleanuphooks_p.h b/src/gui/image/qimagepixmapcleanuphooks_p.h index 16c8974..9e490d7 100644 --- a/src/gui/image/qimagepixmapcleanuphooks_p.h +++ b/src/gui/image/qimagepixmapcleanuphooks_p.h @@ -70,6 +70,10 @@ public: static QImagePixmapCleanupHooks *instance(); + static void enableCleanupHooks(const QImage &image); + static void enableCleanupHooks(const QPixmap &pixmap); + static void enableCleanupHooks(QPixmapData *pixmapData); + // Gets called when a pixmap is about to be modified: void addPixmapModificationHook(_qt_pixmap_cleanup_hook_pm); diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 45ff5f4..c452b9a 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -113,13 +113,10 @@ void QPixmap::init(int w, int h, Type type) void QPixmap::init(int w, int h, int type) { - QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem(); - if (gs) - data = gs->createPixmapData(static_cast<QPixmapData::PixelType>(type)); + if ((w > 0 && h > 0) || type == QPixmapData::BitmapType) + data = QPixmapData::create(w, h, (QPixmapData::PixelType) type); else - data = QGraphicsSystem::createDefaultPixmapData(static_cast<QPixmapData::PixelType>(type)); - - data->resize(w, h); + data = 0; } /*! @@ -307,7 +304,7 @@ QPixmap::QPixmap(const char * const xpm[]) QImage image(xpm); if (!image.isNull()) { - if (data->pixelType() == QPixmapData::BitmapType) + if (data && data->pixelType() == QPixmapData::BitmapType) *this = QBitmap::fromImage(image); else *this = fromImage(image); @@ -322,8 +319,8 @@ QPixmap::QPixmap(const char * const xpm[]) QPixmap::~QPixmap() { - Q_ASSERT(data->ref >= 1); // Catch if ref-counting changes again - if (data->is_cached && data->ref == 1) // ref will be decrememnted after destructor returns + Q_ASSERT(!data || data->ref >= 1); // Catch if ref-counting changes again + if (data && data->is_cached && data->ref == 1) // ref will be decrememnted after destructor returns QImagePixmapCleanupHooks::executePixmapDestructionHooks(this); } @@ -544,7 +541,7 @@ bool QPixmap::isQBitmap() const */ bool QPixmap::isNull() const { - return data->isNull(); + return !data || data->isNull(); } /*! @@ -556,7 +553,7 @@ bool QPixmap::isNull() const */ int QPixmap::width() const { - return data->width(); + return data ? data->width() : 0; } /*! @@ -568,7 +565,7 @@ int QPixmap::width() const */ int QPixmap::height() const { - return data->height(); + return data ? data->height() : 0; } /*! @@ -581,7 +578,7 @@ int QPixmap::height() const */ QSize QPixmap::size() const { - return QSize(data->width(), data->height()); + return data ? QSize(data->width(), data->height()) : QSize(); } /*! @@ -593,7 +590,7 @@ QSize QPixmap::size() const */ QRect QPixmap::rect() const { - return QRect(0, 0, data->width(), data->height()); + return data ? QRect(0, 0, data->width(), data->height()) : QRect(); } /*! @@ -609,7 +606,7 @@ QRect QPixmap::rect() const */ int QPixmap::depth() const { - return data->depth(); + return data ? data->depth() : 0; } /*! @@ -639,7 +636,7 @@ void QPixmap::resize_helper(const QSize &s) return; // Create new pixmap - QPixmap pm(QSize(w, h), data->type); + QPixmap pm(QSize(w, h), data ? data->type : QPixmapData::PixmapType); bool uninit = false; #if defined(Q_WS_X11) QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data.data()) : 0; @@ -728,6 +725,9 @@ void QPixmap::setMask(const QBitmap &mask) return; } + if (isNull()) + return; + if (static_cast<const QPixmap &>(mask).data == data) // trying to selfmask return; @@ -826,11 +826,14 @@ bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConvers QFileInfo info(fileName); QString key = QLatin1String("qt_pixmap_") + info.absoluteFilePath() + QLatin1Char('_') + QString::number(info.lastModified().toTime_t()) + QLatin1Char('_') + - QString::number(info.size()) + QLatin1Char('_') + QString::number(data->pixelType()); + QString::number(info.size()) + QLatin1Char('_') + QString::number(data ? data->pixelType() : QPixmapData::PixmapType); if (QPixmapCache::find(key, *this)) return true; + if (!data) + data = QPixmapData::create(0, 0, QPixmapData::PixmapType); + if (data->fromFile(fileName, format, flags)) { QPixmapCache::insert(key, *this); return true; @@ -863,6 +866,9 @@ bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, Qt::I if (len == 0 || buf == 0) return false; + if (!data) + data = QPixmapData::create(0, 0, QPixmapData::PixmapType); + return data->fromData(buf, len, format, flags); } @@ -1008,6 +1014,9 @@ int QPixmap::serialNumber() const */ qint64 QPixmap::cacheKey() const { + if (isNull()) + return 0; + int classKey = data->classId(); if (classKey >= 1024) classKey = -(classKey >> 10); @@ -1224,7 +1233,7 @@ QPixmap::QPixmap(const QImage& image) QPixmap &QPixmap::operator=(const QImage &image) { - if (data->pixelType() == QPixmapData::BitmapType) + if (data && data->pixelType() == QPixmapData::BitmapType) *this = QBitmap::fromImage(image); else *this = fromImage(image); @@ -1254,7 +1263,7 @@ bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, Color */ bool QPixmap::convertFromImage(const QImage &image, ColorMode mode) { - if (data->pixelType() == QPixmapData::BitmapType) + if (data && data->pixelType() == QPixmapData::BitmapType) *this = QBitmap::fromImage(image, colorModeToFlags(mode)); else *this = fromImage(image, colorModeToFlags(mode)); @@ -1341,7 +1350,7 @@ Q_GUI_EXPORT void copyBlt(QPixmap *dst, int dx, int dy, bool QPixmap::isDetached() const { - return data->ref == 1; + return data && data->ref == 1; } /*! \internal @@ -1753,7 +1762,7 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) */ bool QPixmap::hasAlpha() const { - return (data->hasAlphaChannel() || !data->mask().isNull()); + return data && (data->hasAlphaChannel() || !data->mask().isNull()); } /*! @@ -1764,7 +1773,7 @@ bool QPixmap::hasAlpha() const */ bool QPixmap::hasAlphaChannel() const { - return data->hasAlphaChannel(); + return data && data->hasAlphaChannel(); } /*! @@ -1772,7 +1781,7 @@ bool QPixmap::hasAlphaChannel() const */ int QPixmap::metric(PaintDeviceMetric metric) const { - return data->metric(metric); + return data ? data->metric(metric) : 0; } /*! @@ -1844,7 +1853,7 @@ void QPixmap::setAlphaChannel(const QPixmap &alphaChannel) */ QPixmap QPixmap::alphaChannel() const { - return data->alphaChannel(); + return data ? data->alphaChannel() : QPixmap(); } /*! @@ -1852,7 +1861,7 @@ QPixmap QPixmap::alphaChannel() const */ QPaintEngine *QPixmap::paintEngine() const { - return data->paintEngine(); + return data ? data->paintEngine() : 0; } /*! @@ -1867,7 +1876,7 @@ QPaintEngine *QPixmap::paintEngine() const */ QBitmap QPixmap::mask() const { - return data->mask(); + return data ? data->mask() : QBitmap(); } /*! @@ -1916,6 +1925,9 @@ int QPixmap::defaultDepth() */ void QPixmap::detach() { + if (!data) + return; + QPixmapData::ClassId id = data->classId(); if (id == QPixmapData::RasterClass) { QRasterPixmapData *rasterData = static_cast<QRasterPixmapData*>(data.data()); diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp index afa6f83..15350e8 100644 --- a/src/gui/image/qpixmap_mac.cpp +++ b/src/gui/image/qpixmap_mac.cpp @@ -965,6 +965,9 @@ Qt::HANDLE QPixmap::macQDAlphaHandle() const Qt::HANDLE QPixmap::macCGHandle() const { + if (isNull()) + return 0; + if (data->classId() == QPixmapData::MacClass) { QMacPixmapData *d = static_cast<QMacPixmapData *>(data.data()); if (!d->cg_data) diff --git a/src/gui/image/qpixmap_qws.cpp b/src/gui/image/qpixmap_qws.cpp index 6b4283e..a8516a5 100644 --- a/src/gui/image/qpixmap_qws.cpp +++ b/src/gui/image/qpixmap_qws.cpp @@ -114,7 +114,7 @@ QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h) QRgb* QPixmap::clut() const { - if (data->classId() == QPixmapData::RasterClass) { + if (data && data->classId() == QPixmapData::RasterClass) { const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.colorTable().data(); } @@ -124,7 +124,7 @@ QRgb* QPixmap::clut() const int QPixmap::numCols() const { - if (data->classId() == QPixmapData::RasterClass) { + if (data && data->classId() == QPixmapData::RasterClass) { const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.numColors(); } @@ -134,7 +134,7 @@ int QPixmap::numCols() const const uchar* QPixmap::qwsBits() const { - if (data->classId() == QPixmapData::RasterClass) { + if (data && data->classId() == QPixmapData::RasterClass) { const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.bits(); } @@ -144,7 +144,7 @@ const uchar* QPixmap::qwsBits() const int QPixmap::qwsBytesPerLine() const { - if (data->classId() == QPixmapData::RasterClass) { + if (data && data->classId() == QPixmapData::RasterClass) { const QRasterPixmapData *d = static_cast<const QRasterPixmapData*>(data.data()); return d->image.bytesPerLine(); } diff --git a/src/gui/image/qpixmap_s60.cpp b/src/gui/image/qpixmap_s60.cpp index 7086341..7f0a504 100644 --- a/src/gui/image/qpixmap_s60.cpp +++ b/src/gui/image/qpixmap_s60.cpp @@ -311,7 +311,7 @@ QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h) CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const { QPixmapData *data = pixmapData(); - if (data->isNull()) + if (!data || data->isNull()) return 0; return reinterpret_cast<CFbsBitmap*>(data->toNativeType(QPixmapData::FbsBitmap)); @@ -337,8 +337,9 @@ QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap) if (!bitmap) return QPixmap(); - QPixmap pixmap; - pixmap.pixmapData()->fromNativeType(reinterpret_cast<void*>(bitmap), QPixmapData::FbsBitmap); + QScopedPointer<QS60PixmapData> data(new QS60PixmapData(QPixmapData::PixmapType)); + data->fromNativeType(reinterpret_cast<void*>(bitmap), QPixmapData::FbsBitmap); + QPixmap pixmap(data.take()); return pixmap; } @@ -751,9 +752,9 @@ QPixmap QPixmap::fromSymbianRSgImage(RSgImage *sgImage) if (!sgImage) return QPixmap(); - QPixmap pixmap; - pixmap.pixmapData()->fromNativeType(reinterpret_cast<void*>(sgImage), QPixmapData::SgImage); - + QScopedPointer<QS60PixmapData> data(new QS60PixmapData(QPixmapData::PixmapType)); + data->fromNativeType(reinterpret_cast<void*>(bitmap), QPixmapData::SgImage); + QPixmap pixmap(data.take()); return pixmap; } diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp index 1b61484..04027c1 100644 --- a/src/gui/image/qpixmap_win.cpp +++ b/src/gui/image/qpixmap_win.cpp @@ -121,6 +121,9 @@ QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h ) HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const { + if (isNull()) + return 0; + HBITMAP bitmap = 0; if (data->classId() == QPixmapData::RasterClass) { QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.data()); diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index ea9eff9..8a0120a 100644 --- a/src/gui/image/qpixmap_x11.cpp +++ b/src/gui/image/qpixmap_x11.cpp @@ -1976,6 +1976,9 @@ void QPixmap::x11SetScreen(int screen) return; } + if (isNull()) + return; + if (data->classId() != QPixmapData::X11Class) return; @@ -2078,7 +2081,7 @@ bool QX11PixmapData::hasAlphaChannel() const const QX11Info &QPixmap::x11Info() const { - if (data->classId() == QPixmapData::X11Class) + if (data && data->classId() == QPixmapData::X11Class) return static_cast<QX11PixmapData*>(data.data())->xinfo; else { static QX11Info nullX11Info; @@ -2135,7 +2138,7 @@ QPaintEngine* QX11PixmapData::paintEngine() const Qt::HANDLE QPixmap::x11PictureHandle() const { #ifndef QT_NO_XRENDER - if (data->classId() == QPixmapData::X11Class) + if (data && data->classId() == QPixmapData::X11Class) return static_cast<const QX11PixmapData*>(data.data())->picture; else return 0; diff --git a/src/gui/image/qpixmapdata.cpp b/src/gui/image/qpixmapdata.cpp index 1ad1f02..10194e4 100644 --- a/src/gui/image/qpixmapdata.cpp +++ b/src/gui/image/qpixmapdata.cpp @@ -51,6 +51,19 @@ QT_BEGIN_NAMESPACE const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; +QPixmapData *QPixmapData::create(int w, int h, PixelType type) +{ + QPixmapData *data; + QGraphicsSystem* gs = QApplicationPrivate::graphicsSystem(); + if (gs) + data = gs->createPixmapData(static_cast<QPixmapData::PixelType>(type)); + else + data = QGraphicsSystem::createDefaultPixmapData(static_cast<QPixmapData::PixelType>(type)); + data->resize(w, h); + return data; +} + + QPixmapData::QPixmapData(PixelType pixelType, int objectId) : w(0), h(0), diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h index 2f4f201..d1bb92a 100644 --- a/src/gui/image/qpixmapdata_p.h +++ b/src/gui/image/qpixmapdata_p.h @@ -122,6 +122,8 @@ public: virtual void fromNativeType(void* pixmap, NativeType type); #endif + static QPixmapData *create(int w, int h, PixelType type); + protected: void setSerialNumber(int serNo); int w; @@ -131,12 +133,11 @@ protected: private: friend class QPixmap; - friend class QGLContextPrivate; friend class QX11PixmapData; friend class QS60PixmapData; + friend class QImagePixmapCleanupHooks; // Needs to set is_cached friend class QGLTextureCache; //Needs to check the reference count friend class QExplicitlySharedDataPointer<QPixmapData>; - friend bool qt_createEGLSurfaceForPixmap(QPixmapData*, bool); // Needs to set is_cached QAtomicInt ref; int detach_no; diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp index d0de03e..e50cc8b 100644 --- a/src/gui/image/qpixmapfilter.cpp +++ b/src/gui/image/qpixmapfilter.cpp @@ -504,10 +504,10 @@ void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const Q class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate { public: - QPixmapBlurFilterPrivate() : radius(5), hint(Qt::PerformanceHint) {} + QPixmapBlurFilterPrivate() : radius(5), hint(QGraphicsBlurEffect::PerformanceHint) {} qreal radius; - Qt::RenderHint hint; + QGraphicsBlurEffect::BlurHint hint; }; @@ -556,12 +556,18 @@ qreal QPixmapBlurFilter::radius() const Setting the blur hint to PerformanceHint causes the implementation to trade off visual quality to blur the image faster. Setting the blur hint to QualityHint causes the implementation to improve - visual quality at the expense of speed. The implementation is free - to ignore this value if it only has a single blur algorithm. + visual quality at the expense of speed. + + AnimationHint causes the implementation to optimize for animating + the blur radius, possibly by caching blurred versions of the source + pixmap. + + The implementation is free to ignore this value if it only has a single + blur algorithm. \internal */ -void QPixmapBlurFilter::setBlurHint(Qt::RenderHint hint) +void QPixmapBlurFilter::setBlurHint(QGraphicsBlurEffect::BlurHint hint) { Q_D(QPixmapBlurFilter); d->hint = hint; @@ -572,7 +578,7 @@ void QPixmapBlurFilter::setBlurHint(Qt::RenderHint hint) \internal */ -Qt::RenderHint QPixmapBlurFilter::blurHint() const +QGraphicsBlurEffect::BlurHint QPixmapBlurFilter::blurHint() const { Q_D(const QPixmapBlurFilter); return d->hint; diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h index fc70795..6a96676 100644 --- a/src/gui/image/qpixmapfilter_p.h +++ b/src/gui/image/qpixmapfilter_p.h @@ -55,6 +55,7 @@ #include <QtCore/qnamespace.h> #include <QtGui/qpixmap.h> +#include <QtGui/qgraphicseffect.h> QT_BEGIN_HEADER @@ -130,10 +131,10 @@ public: ~QPixmapBlurFilter(); void setRadius(qreal radius); - void setBlurHint(Qt::RenderHint hint); + void setBlurHint(QGraphicsBlurEffect::BlurHint hint); qreal radius() const; - Qt::RenderHint blurHint() const; + QGraphicsBlurEffect::BlurHint blurHint() const; QRectF boundingRectFor(const QRectF &rect) const; void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const; diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index cddad7d..8d6cad3 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -2275,8 +2275,9 @@ void QPainter::setBrushOrigin(const QPointF &p) /*! Sets the composition mode to the given \a mode. - \warning You can only set the composition mode for QPainter - objects that operates on a QImage. + \warning Only a QPainter operating on a QImage fully supports all + composition modes. The RasterOp modes are supported for X11 as + described in compositionMode(). \sa compositionMode() */ diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index c57b3c1..228a6b1 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -452,6 +452,17 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine #endif if (join == FlatJoin) { + QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y), + qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y)); + QPointF isect; + QLineF::IntersectType type = prevLine.intersect(nextLine, &isect); + QLineF shortCut(prevLine.p2(), nextLine.p1()); + qreal angle = shortCut.angleTo(prevLine); + if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) { + emitLineTo(focal_x, focal_y); + emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1())); + return; + } emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1())); @@ -468,8 +479,8 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine // If we are on the inside, do the short cut... QLineF shortCut(prevLine.p2(), nextLine.p1()); qreal angle = shortCut.angleTo(prevLine); - if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) { + emitLineTo(focal_x, focal_y); emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1())); return; } @@ -509,8 +520,9 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine qfixed offset = m_strokeWidth / 2; QLineF shortCut(prevLine.p2(), nextLine.p1()); - qreal angle = prevLine.angle(shortCut); + qreal angle = shortCut.angleTo(prevLine); if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) { + emitLineTo(focal_x, focal_y); emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1())); return; } @@ -581,6 +593,13 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine qt_real_to_fixed(l1.x1()), qt_real_to_fixed(l1.y1())); } else if (join == SvgMiterJoin) { + QLineF shortCut(prevLine.p2(), nextLine.p1()); + qreal angle = shortCut.angleTo(prevLine); + if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) { + emitLineTo(focal_x, focal_y); + emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1())); + return; + } QLineF miterLine(QPointF(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y)), isect); if (miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) { diff --git a/src/gui/painting/qwindowsurface_x11.cpp b/src/gui/painting/qwindowsurface_x11.cpp index 46c4c42..77e019c 100644 --- a/src/gui/painting/qwindowsurface_x11.cpp +++ b/src/gui/painting/qwindowsurface_x11.cpp @@ -94,6 +94,8 @@ QPaintDevice *QX11WindowSurface::paintDevice() void QX11WindowSurface::beginPaint(const QRegion &rgn) { #ifndef QT_NO_XRENDER + Q_ASSERT(!d_ptr->device.isNull()); + if (d_ptr->translucentBackground) { if (d_ptr->device.depth() != 32) static_cast<QX11PixmapData *>(d_ptr->device.data_ptr().data())->convertToARGB32(); @@ -157,8 +159,8 @@ void QX11WindowSurface::setGeometry(const QRect &rect) QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen()); QX11PixmapData *oldData = static_cast<QX11PixmapData *>(d_ptr->device.pixmapData()); - Q_ASSERT(oldData); - if (!(oldData->flags & QX11PixmapData::Uninitialized) && hasStaticContents()) { + + if (oldData && !(oldData->flags & QX11PixmapData::Uninitialized) && hasStaticContents()) { // Copy the content of the old pixmap into the new one. QX11PixmapData *newData = new QX11PixmapData(QPixmapData::PixmapType); newData->resize(size.width(), size.height()); diff --git a/src/gui/text/qtextengine_mac.cpp b/src/gui/text/qtextengine_mac.cpp index eeccc72..54be53b 100644 --- a/src/gui/text/qtextengine_mac.cpp +++ b/src/gui/text/qtextengine_mac.cpp @@ -594,53 +594,50 @@ void QTextEngine::shapeTextMac(int item) const str = reinterpret_cast<const QChar *>(uc); } - while (true) { - ensureSpace(num_glyphs); - num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used; - - QGlyphLayout g = availableGlyphs(&si); - g.numGlyphs = num_glyphs; - unsigned short *log_clusters = logClusters(&si); + ensureSpace(num_glyphs); + num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used; - if (fe->stringToCMap(str, - len, - &g, - &num_glyphs, - flags, - log_clusters, - attributes())) { + QGlyphLayout g = availableGlyphs(&si); + g.numGlyphs = num_glyphs; + unsigned short *log_clusters = logClusters(&si); - heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs); - break; - } + bool stringToCMapFailed = false; + if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes())) { + ensureSpace(num_glyphs); + stringToCMapFailed = fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, + attributes()); } - si.num_glyphs = num_glyphs; + if (!stringToCMapFailed) { + heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs); - layoutData->used += si.num_glyphs; + si.num_glyphs = num_glyphs; - QGlyphLayout g = shapedGlyphs(&si); + layoutData->used += si.num_glyphs; - if (si.analysis.script == QUnicodeTables::Arabic) { - QVarLengthArray<QArabicProperties> props(len + 2); - QArabicProperties *properties = props.data(); - int f = si.position; - int l = len; - if (f > 0) { - --f; - ++l; - ++properties; - } - if (f + l < layoutData->string.length()) { - ++l; - } - qt_getArabicProperties((const unsigned short *)(layoutData->string.unicode()+f), l, props.data()); + QGlyphLayout g = shapedGlyphs(&si); - unsigned short *log_clusters = logClusters(&si); + if (si.analysis.script == QUnicodeTables::Arabic) { + QVarLengthArray<QArabicProperties> props(len + 2); + QArabicProperties *properties = props.data(); + int f = si.position; + int l = len; + if (f > 0) { + --f; + ++l; + ++properties; + } + if (f + l < layoutData->string.length()) { + ++l; + } + qt_getArabicProperties((const unsigned short *)(layoutData->string.unicode()+f), l, props.data()); - for (int i = 0; i < len; ++i) { - int gpos = log_clusters[i]; - g.attributes[gpos].justification = properties[i].justification; + unsigned short *log_clusters = logClusters(&si); + + for (int i = 0; i < len; ++i) { + int gpos = log_clusters[i]; + g.attributes[gpos].justification = properties[i].justification; + } } } diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp index ab2026c..b71a7b7 100644 --- a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp +++ b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp @@ -67,8 +67,10 @@ QGLCustomShaderStage::QGLCustomShaderStage() QGLCustomShaderStage::~QGLCustomShaderStage() { Q_D(QGLCustomShaderStage); - if (d->m_manager) - d->m_manager->removeCustomStage(this); + if (d->m_manager) { + d->m_manager->removeCustomStage(); + d->m_manager->sharedShaders->cleanupCustomStage(this); + } } void QGLCustomShaderStage::setUniformsDirty() @@ -85,6 +87,8 @@ bool QGLCustomShaderStage::setOnPainter(QPainter* p) qWarning("QGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2"); return false; } + if (d->m_manager) + qWarning("Custom shader is already set on a painter"); QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx*>(p->paintEngine()); d->m_manager = QGL2PaintEngineExPrivate::shaderManagerForEngine(engine); @@ -108,12 +112,21 @@ void QGLCustomShaderStage::removeFromPainter(QPainter* p) // This should leave the program in a compiled/linked state // if the next custom shader stage is this one again. d->m_manager->setCustomStage(0); + d->m_manager = 0; } -const char* QGLCustomShaderStage::source() const +QByteArray QGLCustomShaderStage::source() const { Q_D(const QGLCustomShaderStage); - return d->m_source.constData(); + return d->m_source; +} + +// Called by the shader manager if another custom shader is attached or +// the manager is deleted +void QGLCustomShaderStage::setInactive() +{ + Q_D(QGLCustomShaderStage); + d->m_manager = 0; } void QGLCustomShaderStage::setSource(const QByteArray& s) diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h index f8c13c5..e319389 100644 --- a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h +++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h @@ -74,8 +74,9 @@ public: bool setOnPainter(QPainter*); void removeFromPainter(QPainter*); - const char* source() const; + QByteArray source() const; + void setInactive(); protected: void setSource(const QByteArray&); diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index af9306f..34c448f 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -66,7 +66,7 @@ QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLConte return p; } -const char* QGLEngineSharedShaders::qglEngineShaderSourceCode[] = { +const char* QGLEngineSharedShaders::qShaderSnippets[] = { 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, @@ -78,7 +78,6 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) , blitShaderProg(0) , simpleShaderProg(0) { - memset(compiledShaders, 0, sizeof(compiledShaders)); /* Rather than having the shader source array statically initialised, it is initialised @@ -86,10 +85,10 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) around without having to change the order of the glsl strings. It is hoped this will make future hard-to-find runtime bugs more obvious and generally give more solid code. */ - static bool qglEngineShaderSourceCodePopulated = false; - if (!qglEngineShaderSourceCodePopulated) { + static bool snippetsPopulated = false; + if (!snippetsPopulated) { - const char** code = qglEngineShaderSourceCode; // shortcut + const char** code = qShaderSnippets; // shortcut code[MainVertexShader] = qglslMainVertexShader; code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader; @@ -121,7 +120,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader; code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader; code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader; - code[CustomImageSrcFragmentShader] = ""; // Supplied by app. + code[CustomImageSrcFragmentShader] = qglslCustomSrcFragmentShader; // Calls "customShader", which must be appended code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader; code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader; code[TextureBrushSrcWithPatternFragmentShader] = qglslTextureBrushSrcWithPatternFragmentShader; @@ -131,11 +130,13 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) code[ConicalGradientBrushSrcFragmentShader] = qglslConicalGradientBrushSrcFragmentShader; code[ShockingPinkSrcFragmentShader] = qglslShockingPinkSrcFragmentShader; + code[NoMaskFragmentShader] = ""; code[MaskFragmentShader] = qglslMaskFragmentShader; code[RgbMaskFragmentShaderPass1] = qglslRgbMaskFragmentShaderPass1; code[RgbMaskFragmentShaderPass2] = qglslRgbMaskFragmentShaderPass2; code[RgbMaskWithGammaFragmentShader] = ""; //### + code[NoCompositionModeFragmentShader] = ""; code[MultiplyCompositionModeFragmentShader] = ""; //### code[ScreenCompositionModeFragmentShader] = ""; //### code[OverlayCompositionModeFragmentShader] = ""; //### @@ -150,29 +151,41 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) #if defined(QT_DEBUG) // Check that all the elements have been filled: - for (int i = 0; i < TotalShaderCount; ++i) { - if (qglEngineShaderSourceCode[i] == 0) { - int enumIndex = staticMetaObject.indexOfEnumerator("ShaderName"); - QMetaEnum m = staticMetaObject.enumerator(enumIndex); - - qCritical() << "qglEngineShaderSourceCode: Source for" << m.valueToKey(i) - << "(shader" << i << ") missing!"; + for (int i = 0; i < TotalSnippetCount; ++i) { + if (qShaderSnippets[i] == 0) { + QByteArray msg; + msg.append("Fatal: Shader Snippet for "); + msg.append(snippetNameStr(SnippetName(i))); + msg.append(" ("); + msg.append(QByteArray::number(i)); + msg.append(") is missing!"); + qFatal(msg.constData()); } } #endif - qglEngineShaderSourceCodePopulated = true; + snippetsPopulated = true; } + QGLShader* fragShader; + QGLShader* vertexShader; + QByteArray source; + // Compile up the simple shader: + source.clear(); + source.append(qShaderSnippets[MainVertexShader]); + source.append(qShaderSnippets[PositionOnlyVertexShader]); + vertexShader = new QGLShader(QGLShader::VertexShader, context, this); + vertexShader->compile(source); + + source.clear(); + source.append(qShaderSnippets[MainFragmentShader]); + source.append(qShaderSnippets[ShockingPinkSrcFragmentShader]); + fragShader = new QGLShader(QGLShader::FragmentShader, context, this); + fragShader->compile(source); + simpleShaderProg = new QGLShaderProgram(context, this); - compileNamedShader(MainVertexShader, QGLShader::PartialVertexShader); - compileNamedShader(PositionOnlyVertexShader, QGLShader::PartialVertexShader); - compileNamedShader(MainFragmentShader, QGLShader::PartialFragmentShader); - compileNamedShader(ShockingPinkSrcFragmentShader, QGLShader::PartialFragmentShader); - simpleShaderProg->addShader(compiledShaders[MainVertexShader]); - simpleShaderProg->addShader(compiledShaders[PositionOnlyVertexShader]); - simpleShaderProg->addShader(compiledShaders[MainFragmentShader]); - simpleShaderProg->addShader(compiledShaders[ShockingPinkSrcFragmentShader]); + simpleShaderProg->addShader(vertexShader); + simpleShaderProg->addShader(fragShader); simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); simpleShaderProg->link(); if (!simpleShaderProg->isLinked()) { @@ -181,152 +194,159 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) } // Compile the blit shader: + source.clear(); + source.append(qShaderSnippets[MainWithTexCoordsVertexShader]); + source.append(qShaderSnippets[UntransformedPositionVertexShader]); + vertexShader = new QGLShader(QGLShader::VertexShader, context, this); + vertexShader->compile(source); + + source.clear(); + source.append(qShaderSnippets[MainFragmentShader]); + source.append(qShaderSnippets[ImageSrcFragmentShader]); + fragShader = new QGLShader(QGLShader::FragmentShader, context, this); + fragShader->compile(source); + blitShaderProg = new QGLShaderProgram(context, this); - compileNamedShader(MainWithTexCoordsVertexShader, QGLShader::PartialVertexShader); - compileNamedShader(UntransformedPositionVertexShader, QGLShader::PartialVertexShader); - compileNamedShader(MainFragmentShader, QGLShader::PartialFragmentShader); - compileNamedShader(ImageSrcFragmentShader, QGLShader::PartialFragmentShader); - blitShaderProg->addShader(compiledShaders[MainWithTexCoordsVertexShader]); - blitShaderProg->addShader(compiledShaders[UntransformedPositionVertexShader]); - blitShaderProg->addShader(compiledShaders[MainFragmentShader]); - blitShaderProg->addShader(compiledShaders[ImageSrcFragmentShader]); + blitShaderProg->addShader(vertexShader); + blitShaderProg->addShader(fragShader); blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); blitShaderProg->link(); if (!blitShaderProg->isLinked()) { qCritical() << "Errors linking blit shader:" - << blitShaderProg->log(); - } -} - -void QGLEngineSharedShaders::shaderDestroyed(QObject *shader) -{ - // Remove any shader programs which has this as the srcPixel shader: - for (int i = 0; i < cachedPrograms.size(); ++i) { - if (cachedPrograms.at(i).srcPixelFragShader == shader) { - delete cachedPrograms.at(i).program; - cachedPrograms.removeAt(i--); - } + << simpleShaderProg->log(); } - emit shaderProgNeedsChanging(); } -QGLShader *QGLEngineSharedShaders::compileNamedShader(ShaderName name, QGLShader::ShaderType type) +#if defined (QT_DEBUG) +QByteArray QGLEngineSharedShaders::snippetNameStr(SnippetName name) { - Q_ASSERT(name != CustomImageSrcFragmentShader); - Q_ASSERT(name < InvalidShaderName); - - if (compiledShaders[name]) - return compiledShaders[name]; - - QByteArray source = qglEngineShaderSourceCode[name]; - QGLShader *newShader = new QGLShader(type, ctxGuard.context(), this); - newShader->compile(source); - -#if defined(QT_DEBUG) - // Name the shader for easier debugging - QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName")); - newShader->setObjectName(QLatin1String(m.valueToKey(name))); -#endif - - compiledShaders[name] = newShader; - return newShader; + QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("SnippetName")); + return QByteArray(m.valueToKey(name)); } - -QGLShader *QGLEngineSharedShaders::compileCustomShader(QGLCustomShaderStage *stage, QGLShader::ShaderType type) -{ - QByteArray source = stage->source(); - source += qglslCustomSrcFragmentShader; - - QGLShader *newShader = customShaderCache.object(source); - if (newShader) - return newShader; - - newShader = new QGLShader(type, ctxGuard.context(), this); - newShader->compile(source); - customShaderCache.insert(source, newShader); - - connect(newShader, SIGNAL(destroyed(QObject *)), - this, SLOT(shaderDestroyed(QObject *))); - -#if defined(QT_DEBUG) - // Name the shader for easier debugging - QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName")); - newShader->setObjectName(QLatin1String(m.valueToKey(CustomImageSrcFragmentShader))); #endif - return newShader; -} - // The address returned here will only be valid until next time this function is called. QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog) { for (int i = 0; i < cachedPrograms.size(); ++i) { - if (cachedPrograms[i] == prog) - return &cachedPrograms[i]; + QGLEngineShaderProg *cachedProg = cachedPrograms[i]; + if (*cachedProg == prog) { + // Move the program to the top of the list as a poor-man's cache algo + cachedPrograms.move(i, 0); + return cachedProg; + } } - cachedPrograms.append(prog); - QGLEngineShaderProg &cached = cachedPrograms.last(); + QByteArray source; + source.append(qShaderSnippets[prog.mainFragShader]); + source.append(qShaderSnippets[prog.srcPixelFragShader]); + if (prog.srcPixelFragShader == CustomImageSrcFragmentShader) + source.append(prog.customStageSource); + if (prog.compositionFragShader) + source.append(qShaderSnippets[prog.compositionFragShader]); + if (prog.maskFragShader) + source.append(qShaderSnippets[prog.maskFragShader]); + QGLShader* fragShader = new QGLShader(QGLShader::FragmentShader, ctxGuard.context(), this); + fragShader->compile(source); + + source.clear(); + source.append(qShaderSnippets[prog.mainVertexShader]); + source.append(qShaderSnippets[prog.positionVertexShader]); + QGLShader* vertexShader = new QGLShader(QGLShader::VertexShader, ctxGuard.context(), this); + vertexShader->compile(source); + +#if defined(QT_DEBUG) + // Name the shaders for easier debugging + QByteArray description; + description.append("Fragment shader: main="); + description.append(snippetNameStr(prog.mainFragShader)); + description.append(", srcPixel="); + description.append(snippetNameStr(prog.srcPixelFragShader)); + if (prog.compositionFragShader) { + description.append(", composition="); + description.append(snippetNameStr(prog.compositionFragShader)); + } + if (prog.maskFragShader) { + description.append(", mask="); + description.append(snippetNameStr(prog.maskFragShader)); + } + fragShader->setObjectName(QString::fromLatin1(description)); + + description.clear(); + description.append("Vertex shader: main="); + description.append(snippetNameStr(prog.mainVertexShader)); + description.append(", position="); + description.append(snippetNameStr(prog.positionVertexShader)); + vertexShader->setObjectName(QString::fromLatin1(description)); +#endif + + QGLEngineShaderProg* newProg = new QGLEngineShaderProg(prog); // If the shader program's not found in the cache, create it now. - cached.program = new QGLShaderProgram(ctxGuard.context(), this); - cached.program->addShader(cached.mainVertexShader); - cached.program->addShader(cached.positionVertexShader); - cached.program->addShader(cached.mainFragShader); - cached.program->addShader(cached.srcPixelFragShader); - cached.program->addShader(cached.maskFragShader); - cached.program->addShader(cached.compositionFragShader); + newProg->program = new QGLShaderProgram(ctxGuard.context(), this); + newProg->program->addShader(vertexShader); + newProg->program->addShader(fragShader); // We have to bind the vertex attribute names before the program is linked: - cached.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - if (cached.useTextureCoords) - cached.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - if (cached.useOpacityAttribute) - cached.program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); - - cached.program->link(); - if (!cached.program->isLinked()) { + newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + if (newProg->useTextureCoords) + newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + if (newProg->useOpacityAttribute) + newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); + + newProg->program->link(); + if (!newProg->program->isLinked()) { QLatin1String none("none"); QLatin1String br("\n"); QString error; error = QLatin1String("Shader program failed to link,") #if defined(QT_DEBUG) + br - + QLatin1String(" Shaders Used:\n") - + QLatin1String(" mainVertexShader = ") - + (cached.mainVertexShader ? - cached.mainVertexShader->objectName() : none) + br - + QLatin1String(" positionVertexShader = ") - + (cached.positionVertexShader ? - cached.positionVertexShader->objectName() : none) + br - + QLatin1String(" mainFragShader = ") - + (cached.mainFragShader ? - cached.mainFragShader->objectName() : none) + br - + QLatin1String(" srcPixelFragShader = ") - + (cached.srcPixelFragShader ? - cached.srcPixelFragShader->objectName() : none) + br - + QLatin1String(" maskFragShader = ") - + (cached.maskFragShader ? - cached.maskFragShader->objectName() : none) + br - + QLatin1String(" compositionFragShader = ") - + (cached.compositionFragShader ? - cached.compositionFragShader->objectName() : none) + br + + QLatin1String(" Shaders Used:") + br + + QLatin1String(" ") + vertexShader->objectName() + QLatin1String(": ") + br + + QLatin1String(vertexShader->sourceCode()) + br + + QLatin1String(" ") + fragShader->objectName() + QLatin1String(": ") + br + + QLatin1String(fragShader->sourceCode()) + br #endif + QLatin1String(" Error Log:\n") - + QLatin1String(" ") + cached.program->log(); + + QLatin1String(" ") + newProg->program->log(); qWarning() << error; - delete cached.program; - cachedPrograms.removeLast(); - return 0; - } else { - // taking the address here is safe since - // cachePrograms isn't resized anywhere else - return &cached; + delete newProg; // Deletes the QGLShaderProgram in it's destructor + newProg = 0; } + else { + if (cachedPrograms.count() > 30) { + // The cache is full, so delete the last 5 programs in the list. + // These programs will be least used, as a program us bumped to + // the top of the list when it's used. + for (int i = 0; i < 5; ++i) { + delete cachedPrograms.last(); + cachedPrograms.removeLast(); + } + } + + cachedPrograms.insert(0, newProg); + } + + return newProg; } +void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage) +{ + // Remove any shader programs which has this as the custom shader src: + for (int i = 0; i < cachedPrograms.size(); ++i) { + QGLEngineShaderProg *cachedProg = cachedPrograms[i]; + if (cachedProg->customStageSource == stage->source()) { + delete cachedProg; + cachedPrograms.removeAt(i); + i--; + } + } +} + + QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) : ctx(context), shaderProgNeedsChanging(true), @@ -335,8 +355,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) maskType(NoMask), compositionMode(QPainter::CompositionMode_SourceOver), customSrcStage(0), - currentShaderProg(0), - customShader(0) + currentShaderProg(0) { sharedShaders = QGLEngineSharedShaders::shadersForContext(context); connect(sharedShaders, SIGNAL(shaderProgNeedsChanging()), this, SLOT(shaderProgNeedsChangingSlot())); @@ -345,6 +364,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) QGLEngineShaderManager::~QGLEngineShaderManager() { //### + removeCustomStage(); } uint QGLEngineShaderManager::getUniformLocation(Uniform id) @@ -436,21 +456,20 @@ void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode) void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage) { + if (customSrcStage) + removeCustomStage(); customSrcStage = stage; - customShader = 0; // Will be compiled from 'customSrcStage' later. shaderProgNeedsChanging = true; } -void QGLEngineShaderManager::removeCustomStage(QGLCustomShaderStage* stage) +void QGLEngineShaderManager::removeCustomStage() { - Q_UNUSED(stage); // Currently we only support one at a time... - + if (customSrcStage) + customSrcStage->setInactive(); customSrcStage = 0; - customShader = 0; shaderProgNeedsChanging = true; } - QGLShaderProgram* QGLEngineShaderManager::currentProgram() { return currentShaderProg->program; @@ -482,22 +501,21 @@ bool QGLEngineShaderManager::useCorrectShaderProg() } QGLEngineShaderProg requiredProgram; - requiredProgram.program = 0; bool texCoords = false; // Choose vertex shader shader position function (which typically also sets // varyings) and the source pixel (srcPixel) fragment shader function: - QGLEngineSharedShaders::ShaderName positionVertexShaderName = QGLEngineSharedShaders::InvalidShaderName; - QGLEngineSharedShaders::ShaderName srcPixelFragShaderName = QGLEngineSharedShaders::InvalidShaderName; + requiredProgram.positionVertexShader = QGLEngineSharedShaders::InvalidSnippetName; + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::InvalidSnippetName; bool isAffine = brushTransform.isAffine(); if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) { if (isAffine) - positionVertexShaderName = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader; + requiredProgram.positionVertexShader = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader; else - positionVertexShaderName = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader; + requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader; - srcPixelFragShaderName = QGLEngineSharedShaders::PatternBrushSrcFragmentShader; + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::PatternBrushSrcFragmentShader; } else switch (srcPixelType) { default: @@ -505,172 +523,157 @@ bool QGLEngineShaderManager::useCorrectShaderProg() qFatal("QGLEngineShaderManager::useCorrectShaderProg() - Qt::NoBrush style is set"); break; case QGLEngineShaderManager::ImageSrc: - srcPixelFragShaderName = QGLEngineSharedShaders::ImageSrcFragmentShader; - positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcFragmentShader; + requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; texCoords = true; break; case QGLEngineShaderManager::NonPremultipliedImageSrc: - srcPixelFragShaderName = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader; - positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader; + requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; texCoords = true; break; case QGLEngineShaderManager::PatternSrc: - srcPixelFragShaderName = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader; - positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader; + requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; texCoords = true; break; case QGLEngineShaderManager::TextureSrcWithPattern: - srcPixelFragShaderName = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader; - positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader; break; case Qt::SolidPattern: - srcPixelFragShaderName = QGLEngineSharedShaders::SolidBrushSrcFragmentShader; - positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::SolidBrushSrcFragmentShader; + requiredProgram.positionVertexShader = QGLEngineSharedShaders::PositionOnlyVertexShader; break; case Qt::LinearGradientPattern: - srcPixelFragShaderName = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader : QGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader; break; case Qt::ConicalGradientPattern: - srcPixelFragShaderName = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader : QGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader; break; case Qt::RadialGradientPattern: - srcPixelFragShaderName = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader : QGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader; break; case Qt::TexturePattern: - srcPixelFragShaderName = QGLEngineSharedShaders::TextureBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::TextureBrushSrcFragmentShader; + requiredProgram.positionVertexShader = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader; break; }; - requiredProgram.positionVertexShader = sharedShaders->compileNamedShader(positionVertexShaderName, QGLShader::PartialVertexShader); + if (useCustomSrc) { - if (!customShader) - customShader = sharedShaders->compileCustomShader(customSrcStage, QGLShader::PartialFragmentShader); - requiredProgram.srcPixelFragShader = customShader; - } else { - requiredProgram.srcPixelFragShader = sharedShaders->compileNamedShader(srcPixelFragShaderName, QGLShader::PartialFragmentShader); + requiredProgram.srcPixelFragShader = QGLEngineSharedShaders::CustomImageSrcFragmentShader; + requiredProgram.customStageSource = customSrcStage->source(); } const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; const bool hasMask = maskType != QGLEngineShaderManager::NoMask; // Choose fragment shader main function: - QGLEngineSharedShaders::ShaderName mainFragShaderName; - if (opacityMode == AttributeOpacity) { Q_ASSERT(!hasCompose && !hasMask); - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_ImageArrays; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_ImageArrays; } else { bool useGlobalOpacity = (opacityMode == UniformOpacity); if (hasCompose && hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CMO; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CMO; if (hasCompose && hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CM; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CM; if (!hasCompose && hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_MO; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_MO; if (!hasCompose && hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_M; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_M; if (hasCompose && !hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CO; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_CO; if (hasCompose && !hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_C; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_C; if (!hasCompose && !hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_O; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader_O; if (!hasCompose && !hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader; + requiredProgram.mainFragShader = QGLEngineSharedShaders::MainFragmentShader; } - requiredProgram.mainFragShader = sharedShaders->compileNamedShader(mainFragShaderName, QGLShader::PartialFragmentShader); - if (hasMask) { - QGLEngineSharedShaders::ShaderName maskShaderName = QGLEngineSharedShaders::InvalidShaderName; if (maskType == PixelMask) { - maskShaderName = QGLEngineSharedShaders::MaskFragmentShader; + requiredProgram.maskFragShader = QGLEngineSharedShaders::MaskFragmentShader; texCoords = true; } else if (maskType == SubPixelMaskPass1) { - maskShaderName = QGLEngineSharedShaders::RgbMaskFragmentShaderPass1; + requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass1; texCoords = true; } else if (maskType == SubPixelMaskPass2) { - maskShaderName = QGLEngineSharedShaders::RgbMaskFragmentShaderPass2; + requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskFragmentShaderPass2; texCoords = true; } else if (maskType == SubPixelWithGammaMask) { - maskShaderName = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader; + requiredProgram.maskFragShader = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader; texCoords = true; } else { qCritical("QGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type"); } - - requiredProgram.maskFragShader = sharedShaders->compileNamedShader(maskShaderName, QGLShader::PartialFragmentShader); } else { - requiredProgram.maskFragShader = 0; + requiredProgram.maskFragShader = QGLEngineSharedShaders::NoMaskFragmentShader; } if (hasCompose) { - QGLEngineSharedShaders::ShaderName compositionShaderName = QGLEngineSharedShaders::InvalidShaderName; switch (compositionMode) { case QPainter::CompositionMode_Multiply: - compositionShaderName = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader; break; case QPainter::CompositionMode_Screen: - compositionShaderName = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader; break; case QPainter::CompositionMode_Overlay: - compositionShaderName = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader; break; case QPainter::CompositionMode_Darken: - compositionShaderName = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader; break; case QPainter::CompositionMode_Lighten: - compositionShaderName = QGLEngineSharedShaders::LightenCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::LightenCompositionModeFragmentShader; break; case QPainter::CompositionMode_ColorDodge: - compositionShaderName = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader; break; case QPainter::CompositionMode_ColorBurn: - compositionShaderName = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader; break; case QPainter::CompositionMode_HardLight: - compositionShaderName = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader; break; case QPainter::CompositionMode_SoftLight: - compositionShaderName = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader; break; case QPainter::CompositionMode_Difference: - compositionShaderName = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader; break; case QPainter::CompositionMode_Exclusion: - compositionShaderName = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader; break; default: qWarning("QGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode"); } - requiredProgram.compositionFragShader = sharedShaders->compileNamedShader(compositionShaderName, QGLShader::PartialFragmentShader); } else { - requiredProgram.compositionFragShader = 0; + requiredProgram.compositionFragShader = QGLEngineSharedShaders::NoCompositionModeFragmentShader; } - // Choose vertex shader main function - QGLEngineSharedShaders::ShaderName mainVertexShaderName = QGLEngineSharedShaders::InvalidShaderName; + // Choose vertex shader main function if (opacityMode == AttributeOpacity) { Q_ASSERT(texCoords); - mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader; + requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader; } else if (texCoords) { - mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsVertexShader; + requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainWithTexCoordsVertexShader; } else { - mainVertexShaderName = QGLEngineSharedShaders::MainVertexShader; + requiredProgram.mainVertexShader = QGLEngineSharedShaders::MainVertexShader; } - requiredProgram.mainVertexShader = sharedShaders->compileNamedShader(mainVertexShaderName, QGLShader::PartialVertexShader); requiredProgram.useTextureCoords = texCoords; requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity); - // At this point, requiredProgram is fully populated so try to find the program in the cache currentShaderProg = sharedShaders->findProgramInCache(requiredProgram); diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index 291d24c..50c1432 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -234,32 +234,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(OpenGL) -struct QGLEngineShaderProg -{ - QGLShader* mainVertexShader; - QGLShader* positionVertexShader; - QGLShader* mainFragShader; - QGLShader* srcPixelFragShader; - QGLShader* maskFragShader; // Can be null for no mask - QGLShader* compositionFragShader; // Can be null for GL-handled mode - QGLShaderProgram* program; - - QVector<uint> uniformLocations; - - bool useTextureCoords; - bool useOpacityAttribute; - - bool operator==(const QGLEngineShaderProg& other) { - // We don't care about the program - return ( mainVertexShader == other.mainVertexShader && - positionVertexShader == other.positionVertexShader && - mainFragShader == other.mainFragShader && - srcPixelFragShader == other.srcPixelFragShader && - maskFragShader == other.maskFragShader && - compositionFragShader == other.compositionFragShader - ); - } -}; /* struct QGLEngineCachedShaderProg @@ -280,15 +254,19 @@ static const GLuint QT_VERTEX_COORDS_ATTR = 0; static const GLuint QT_TEXTURE_COORDS_ATTR = 1; static const GLuint QT_OPACITY_ATTR = 2; +class QGLEngineShaderProg; + class QGLEngineSharedShaders : public QObject { Q_OBJECT public: - enum ShaderName { + + enum SnippetName { MainVertexShader, MainWithTexCoordsVertexShader, MainWithTexCoordsAndOpacityVertexShader, + // UntransformedPositionVertexShader must be first in the list: UntransformedPositionVertexShader, PositionOnlyVertexShader, PositionWithPatternBrushVertexShader, @@ -302,6 +280,7 @@ public: AffinePositionWithRadialGradientBrushVertexShader, AffinePositionWithTextureBrushVertexShader, + // MainFragmentShader_CMO must be first in the list: MainFragmentShader_CMO, MainFragmentShader_CM, MainFragmentShader_MO, @@ -312,6 +291,7 @@ public: MainFragmentShader, MainFragmentShader_ImageArrays, + // ImageSrcFragmentShader must be first in the list:: ImageSrcFragmentShader, ImageSrcWithPatternFragmentShader, NonPremultipliedImageSrcFragmentShader, @@ -325,11 +305,15 @@ public: ConicalGradientBrushSrcFragmentShader, ShockingPinkSrcFragmentShader, + // NoMaskFragmentShader must be first in the list: + NoMaskFragmentShader, MaskFragmentShader, RgbMaskFragmentShaderPass1, RgbMaskFragmentShaderPass2, RgbMaskWithGammaFragmentShader, + // NoCompositionModeFragmentShader must be first in the list: + NoCompositionModeFragmentShader, MultiplyCompositionModeFragmentShader, ScreenCompositionModeFragmentShader, OverlayCompositionModeFragmentShader, @@ -342,37 +326,88 @@ public: DifferenceCompositionModeFragmentShader, ExclusionCompositionModeFragmentShader, - TotalShaderCount, InvalidShaderName + TotalSnippetCount, InvalidSnippetName }; +#if defined (QT_DEBUG) + Q_ENUMS(SnippetName) + static QByteArray snippetNameStr(SnippetName snippetName); +#endif - QGLEngineSharedShaders(const QGLContext *context); +/* + // These allow the ShaderName enum to be used as a cache key + const int mainVertexOffset = 0; + const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader; + const int mainFragOffset = (1<<6) - MainFragmentShader_CMO; + const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader; + const int maskOffset = (1<<14) - NoMaskShader; + const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader; +*/ - QGLShader *compileNamedShader(ShaderName name, QGLShader::ShaderType type); + QGLEngineSharedShaders(const QGLContext *context); QGLShaderProgram *simpleProgram() { return simpleShaderProg; } QGLShaderProgram *blitProgram() { return blitShaderProg; } // Compile the program if it's not already in the cache, return the item in the cache. QGLEngineShaderProg *findProgramInCache(const QGLEngineShaderProg &prog); // Compile the custom shader if it's not already in the cache, return the item in the cache. - QGLShader *compileCustomShader(QGLCustomShaderStage *stage, QGLShader::ShaderType type); static QGLEngineSharedShaders *shadersForContext(const QGLContext *context); + // Ideally, this would be static and cleanup all programs in all contexts which + // contain the custom code. Currently it is just a hint and we rely on deleted + // custom shaders being cleaned up by being kicked out of the cache when it's + // full. + void cleanupCustomStage(QGLCustomShaderStage* stage); + signals: void shaderProgNeedsChanging(); -private slots: - void shaderDestroyed(QObject *shader); - private: QGLSharedResourceGuard ctxGuard; QGLShaderProgram *blitShaderProg; QGLShaderProgram *simpleShaderProg; - QList<QGLEngineShaderProg> cachedPrograms; - QCache<QByteArray, QGLShader> customShaderCache; - QGLShader* compiledShaders[TotalShaderCount]; + QList<QGLEngineShaderProg*> cachedPrograms; - static const char* qglEngineShaderSourceCode[TotalShaderCount]; + static const char* qShaderSnippets[TotalSnippetCount]; +}; + + +class QGLEngineShaderProg +{ +public: + QGLEngineShaderProg() : program(0) {} + + ~QGLEngineShaderProg() { + if (program) + delete program; + } + + QGLEngineSharedShaders::SnippetName mainVertexShader; + QGLEngineSharedShaders::SnippetName positionVertexShader; + QGLEngineSharedShaders::SnippetName mainFragShader; + QGLEngineSharedShaders::SnippetName srcPixelFragShader; + QGLEngineSharedShaders::SnippetName maskFragShader; + QGLEngineSharedShaders::SnippetName compositionFragShader; + + QByteArray customStageSource; //TODO: Decent cache key for custom stages + QGLShaderProgram* program; + + QVector<uint> uniformLocations; + + bool useTextureCoords; + bool useOpacityAttribute; + + bool operator==(const QGLEngineShaderProg& other) { + // We don't care about the program + return ( mainVertexShader == other.mainVertexShader && + positionVertexShader == other.positionVertexShader && + mainFragShader == other.mainFragShader && + srcPixelFragShader == other.srcPixelFragShader && + maskFragShader == other.maskFragShader && + compositionFragShader == other.compositionFragShader && + customStageSource == other.customStageSource + ); + } }; class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject @@ -426,7 +461,7 @@ public: void setMaskType(MaskType); void setCompositionMode(QPainter::CompositionMode); void setCustomStage(QGLCustomShaderStage* stage); - void removeCustomStage(QGLCustomShaderStage* stage); + void removeCustomStage(); uint getUniformLocation(Uniform id); @@ -437,19 +472,7 @@ public: QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers QGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer -/* - // These allow the ShaderName enum to be used as a cache key - const int mainVertexOffset = 0; - const int positionVertexOffset = (1<<2) - PositionOnlyVertexShader; - const int mainFragOffset = (1<<6) - MainFragmentShader_CMO; - const int srcPixelOffset = (1<<10) - ImageSrcFragmentShader; - const int maskOffset = (1<<14) - NoMaskShader; - const int compositionOffset = (1 << 16) - MultiplyCompositionModeFragmentShader; -*/ - -#if defined (QT_DEBUG) - Q_ENUMS(ShaderName) -#endif + QGLEngineSharedShaders* sharedShaders; private slots: void shaderProgNeedsChangingSlot() { shaderProgNeedsChanging = true; } @@ -466,9 +489,7 @@ private: QPainter::CompositionMode compositionMode; QGLCustomShaderStage* customSrcStage; - QGLEngineShaderProg* currentShaderProg; - QGLEngineSharedShaders *sharedShaders; - QGLShader *customShader; + QGLEngineShaderProg* currentShaderProg; }; QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 209cd36..4cf2a83 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -289,6 +289,7 @@ public: QScopedPointer<QPixmapFilter> convolutionFilter; QScopedPointer<QPixmapFilter> colorizeFilter; QScopedPointer<QPixmapFilter> blurFilter; + QScopedPointer<QPixmapFilter> animationBlurFilter; QScopedPointer<QPixmapFilter> fastBlurFilter; QScopedPointer<QPixmapFilter> dropShadowFilter; QScopedPointer<QPixmapFilter> fastDropShadowFilter; diff --git a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp index ad18a51..1163eba 100644 --- a/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp +++ b/src/opengl/gl2paintengineex/qtriangulatingstroker.cpp @@ -114,7 +114,7 @@ void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen) if (m_join_style == Qt::RoundJoin) m_join_style = Qt::MiterJoin; m_curvyness_add = 0.5; - m_curvyness_mul = CURVE_FLATNESS; + m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; m_roundness = 1; } else if (cosmetic) { m_curvyness_add = realWidth / 2; diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index e80521b..ad177dc 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -1773,10 +1773,12 @@ Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg() /*! \enum QGLContext::BindOption + \since 4.6 + A set of options to decide how to bind a texture using bindTexture(). \value NoBindOption Don't do anything, pass the texture straight - thru. + through. \value InvertedYBindOption Specifies that the texture should be flipped over the X axis so that the texture coordinate 0,0 corresponds to @@ -2137,7 +2139,7 @@ QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G Q_ASSERT(texture); if (texture->id > 0) - const_cast<QImage &>(image).data_ptr()->is_cached = true; + QImagePixmapCleanupHooks::enableCleanupHooks(image); return texture; } @@ -2158,7 +2160,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G #ifndef QT_NO_DEBUG // Reset the gl error stack...git - while (glGetError() != GL_NO_ERROR); + while (glGetError() != GL_NO_ERROR) ; #endif // Scale the pixmap if needed. GL textures needs to have the @@ -2396,7 +2398,7 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, Q_ASSERT(texture); if (texture->id > 0) - const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = true; + QImagePixmapCleanupHooks::enableCleanupHooks(pixmap); return texture; } @@ -2445,6 +2447,9 @@ int QGLContextPrivate::maxTextureSize() */ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) { + if (image.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(image, target, format, false, DefaultBindOption); return texture->id; @@ -2477,6 +2482,9 @@ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) */ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format, BindOptions options) { + if (image.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(image, target, format, false, options); return texture->id; @@ -2486,6 +2494,9 @@ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format, /*! \internal */ GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format) { + if (image.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, DefaultBindOption); return texture->id; @@ -2495,6 +2506,9 @@ GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMa GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format, BindOptions options) { + if (image.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false, options); return texture->id; @@ -2507,6 +2521,9 @@ GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMa */ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) { + if (pixmap.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(pixmap, target, format, DefaultBindOption); return texture->id; @@ -2521,6 +2538,9 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint forma */ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, BindOptions options) { + if (pixmap.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(pixmap, target, format, options); return texture->id; @@ -2530,6 +2550,9 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint forma /*! \internal */ GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format) { + if (pixmap.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), DefaultBindOption); return texture->id; @@ -2538,6 +2561,9 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, Q GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format, BindOptions options) { + if (pixmap.isNull()) + return 0; + Q_D(QGLContext); QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), options); return texture->id; @@ -4595,6 +4621,9 @@ bool QGLWidget::autoBufferSwap() const */ GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format) { + if (image.isNull()) + return 0; + Q_D(QGLWidget); return d->glcx->bindTexture(image, target, format, QGLContext::DefaultBindOption); } @@ -4608,6 +4637,9 @@ GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format) */ GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options) { + if (image.isNull()) + return 0; + Q_D(QGLWidget); return d->glcx->bindTexture(image, target, format, options); } @@ -4617,6 +4649,9 @@ GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format, /*! \internal */ GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format) { + if (image.isNull()) + return 0; + Q_D(QGLWidget); return d->glcx->bindTexture(image, GLenum(target), GLint(format), QGLContext::DefaultBindOption); } @@ -4624,6 +4659,9 @@ GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMac GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format, QGLContext::BindOptions options) { + if (image.isNull()) + return 0; + Q_D(QGLWidget); return d->glcx->bindTexture(image, GLenum(target), GLint(format), options); } @@ -4637,6 +4675,9 @@ GLuint QGLWidget::bindTexture(const QImage &image, QMacCompatGLenum target, QMac */ GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) { + if (pixmap.isNull()) + return 0; + Q_D(QGLWidget); return d->glcx->bindTexture(pixmap, target, format, QGLContext::DefaultBindOption); } diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index e14e7fb..079953f 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -399,6 +399,7 @@ private: friend class QGLTextureGlyphCache; friend class QGLShareRegister; friend class QGLSharedResourceGuard; + friend class QGLPixmapBlurFilter; friend QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags(); #ifdef Q_WS_MAC public: diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp index 86e593d..899047a 100644 --- a/src/opengl/qgl_x11.cpp +++ b/src/opengl/qgl_x11.cpp @@ -53,6 +53,7 @@ #include <private/qfontengine_ft_p.h> #include <private/qt_x11_p.h> #include <private/qpixmap_x11_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> #ifdef Q_OS_HPUX // for GLXPBuffer #include <private/qglpixelbuffer_p.h> @@ -1704,7 +1705,7 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, con pixmapData->gl_surface = (Qt::HANDLE)glxPixmap; // Make sure the cleanup hook gets called so we can delete the glx pixmap - pixmapData->is_cached = true; + QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData); } GLuint textureId; diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 7180682..9b20297 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -42,6 +42,7 @@ #include "qgl.h" #include <private/qt_x11_p.h> #include <private/qpixmap_x11_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> #include <private/qgl_p.h> #include <private/qpaintengine_opengl_p.h> #include "qgl_egl_p.h" @@ -531,7 +532,7 @@ bool Q_OPENGL_EXPORT qt_createEGLSurfaceForPixmap(QPixmapData* pmd, bool readOnl Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure! pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface; - pixmapData->is_cached = true; // Make sure the cleanup hook gets called + QImagePixmapCleanupHooks::enableCleanupHooks(pixmapData); // Make sure the cleanup hook gets called return true; } diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 2af69e0..ff19e06 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -43,6 +43,8 @@ #include "private/qpixmapdata_gl_p.h" #include "private/qpaintengineex_opengl2_p.h" #include "private/qglengineshadermanager_p.h" +#include "private/qpixmapdata_p.h" +#include "private/qimagepixmapcleanuphooks_p.h" #include "qglpixmapfilter_p.h" #include "qgraphicssystem_gl_p.h" #include "qpaintengine_opengl_p.h" @@ -54,7 +56,7 @@ #include "private/qapplication_p.h" #include "private/qmath_p.h" - +#include "qmath.h" QT_BEGIN_NAMESPACE @@ -100,7 +102,7 @@ private: class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter> { public: - QGLPixmapBlurFilter(Qt::RenderHint hint); + QGLPixmapBlurFilter(QGraphicsBlurEffect::BlurHint hint); void setUniforms(QGLShaderProgram *program); @@ -114,16 +116,20 @@ private: mutable QSize m_textureSize; mutable bool m_horizontalBlur; mutable bool m_singlePass; + mutable bool m_animatedBlur; + + mutable qreal m_t; + mutable QSize m_targetSize; mutable bool m_haveCached; mutable int m_cachedRadius; - mutable Qt::RenderHint m_hint; + mutable QGraphicsBlurEffect::BlurHint m_hint; }; class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter> { public: - QGLPixmapDropShadowFilter(Qt::RenderHint hint); + QGLPixmapDropShadowFilter(QGraphicsBlurEffect::BlurHint hint); void setUniforms(QGLShaderProgram *program); @@ -137,7 +143,7 @@ private: mutable bool m_haveCached; mutable int m_cachedRadius; - mutable Qt::RenderHint m_hint; + mutable QGraphicsBlurEffect::BlurHint m_hint; }; extern QGLWidget *qt_gl_share_widget(); @@ -153,13 +159,18 @@ QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *pr case QPixmapFilter::BlurFilter: { const QPixmapBlurFilter *proto = static_cast<const QPixmapBlurFilter *>(prototype); - if (proto->blurHint() == Qt::PerformanceHint || proto->radius() <= 5) { + if (proto->blurHint() == QGraphicsBlurEffect::AnimationHint) { + if (!d->animationBlurFilter) + d->animationBlurFilter.reset(new QGLPixmapBlurFilter(proto->blurHint())); + return d->animationBlurFilter.data(); + } + if (proto->blurHint() == QGraphicsBlurEffect::PerformanceHint || proto->radius() <= 5) { if (!d->fastBlurFilter) - d->fastBlurFilter.reset(new QGLPixmapBlurFilter(Qt::PerformanceHint)); + d->fastBlurFilter.reset(new QGLPixmapBlurFilter(QGraphicsBlurEffect::PerformanceHint)); return d->fastBlurFilter.data(); } if (!d->blurFilter) - d->blurFilter.reset(new QGLPixmapBlurFilter(Qt::QualityHint)); + d->blurFilter.reset(new QGLPixmapBlurFilter(QGraphicsBlurEffect::QualityHint)); return d->blurFilter.data(); } @@ -167,11 +178,11 @@ QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *pr const QPixmapDropShadowFilter *proto = static_cast<const QPixmapDropShadowFilter *>(prototype); if (proto->blurRadius() <= 5) { if (!d->fastDropShadowFilter) - d->fastDropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::PerformanceHint)); + d->fastDropShadowFilter.reset(new QGLPixmapDropShadowFilter(QGraphicsBlurEffect::PerformanceHint)); return d->fastDropShadowFilter.data(); } if (!d->dropShadowFilter) - d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::QualityHint)); + d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter(QGraphicsBlurEffect::QualityHint)); return d->dropShadowFilter.data(); } @@ -327,21 +338,344 @@ static QByteArray qt_gl_convertToClamped(const QByteArray &source) return result; } -QGLPixmapBlurFilter::QGLPixmapBlurFilter(Qt::RenderHint hint) - : m_haveCached(false) +QGLPixmapBlurFilter::QGLPixmapBlurFilter(QGraphicsBlurEffect::BlurHint hint) + : m_animatedBlur(false) + , m_haveCached(false) , m_cachedRadius(0) , m_hint(hint) { } +// should be even numbers as they will be divided by two +static const int qCachedBlurLevels[] = { 6, 14, 30 }; +static const int qNumCachedBlurTextures = sizeof(qCachedBlurLevels) / sizeof(*qCachedBlurLevels); +static const int qMaxCachedBlurLevel = qCachedBlurLevels[qNumCachedBlurTextures - 1]; + +static qreal qLogBlurLevel(int level) +{ + static bool initialized = false; + static qreal logBlurLevelCache[qNumCachedBlurTextures]; + if (!initialized) { + for (int i = 0; i < qNumCachedBlurTextures; ++i) + logBlurLevelCache[i] = qLn(qCachedBlurLevels[i]); + initialized = true; + } + return logBlurLevelCache[level]; +} + +class QGLBlurTextureInfo +{ +public: + QGLBlurTextureInfo(QSize size, GLuint textureIds[]) + : m_size(size) + { + for (int i = 0; i < qNumCachedBlurTextures; ++i) + m_textureIds[i] = textureIds[i]; + } + + ~QGLBlurTextureInfo() + { + glDeleteTextures(qNumCachedBlurTextures, m_textureIds); + } + + QSize size() const { return m_size; } + GLuint textureId(int i) const { return m_textureIds[i]; } + +private: + GLuint m_textureIds[qNumCachedBlurTextures]; + QSize m_size; +}; + +class QGLBlurTextureCache : public QObject +{ +public: + static QGLBlurTextureCache *cacheForContext(const QGLContext *context); + + QGLBlurTextureCache(); + ~QGLBlurTextureCache(); + + QGLBlurTextureInfo *takeBlurTextureInfo(const QPixmap &pixmap); + bool fitsInCache(const QPixmap &pixmap) const; + bool hasBlurTextureInfo(const QPixmap &pixmap) const; + void insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info); + void clearBlurTextureInfo(const QPixmap &pixmap); + + void timerEvent(QTimerEvent *event); + +private: + static void pixmapDestroyed(QPixmap *pixmap); + + QCache<quint64, QGLBlurTextureInfo > cache; + + static QList<QGLBlurTextureCache *> blurTextureCaches; + + int timerId; +}; + +QList<QGLBlurTextureCache *> QGLBlurTextureCache::blurTextureCaches; + +static void QGLBlurTextureCache_free(void *ptr) +{ + delete reinterpret_cast<QGLBlurTextureCache *>(ptr); +} + +Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_blur_texture_caches, (QGLBlurTextureCache_free)) + +QGLBlurTextureCache::QGLBlurTextureCache() + : timerId(0) +{ + cache.setMaxCost(4 * 1024 * 1024); + blurTextureCaches.append(this); +} + +QGLBlurTextureCache::~QGLBlurTextureCache() +{ + blurTextureCaches.removeAt(blurTextureCaches.indexOf(this)); +} + +void QGLBlurTextureCache::timerEvent(QTimerEvent *event) +{ + killTimer(timerId); + timerId = 0; + + cache.clear(); +} + +QGLBlurTextureCache *QGLBlurTextureCache::cacheForContext(const QGLContext *context) +{ + QGLBlurTextureCache *p = reinterpret_cast<QGLBlurTextureCache *>(qt_blur_texture_caches()->value(context)); + if (!p) { + p = new QGLBlurTextureCache; + qt_blur_texture_caches()->insert(context, p); + } + return p; +} + +QGLBlurTextureInfo *QGLBlurTextureCache::takeBlurTextureInfo(const QPixmap &pixmap) +{ + return cache.take(pixmap.cacheKey()); +} + +void QGLBlurTextureCache::clearBlurTextureInfo(const QPixmap &pixmap) +{ + cache.remove(pixmap.cacheKey()); +} + +bool QGLBlurTextureCache::hasBlurTextureInfo(const QPixmap &pixmap) const +{ + return cache.contains(pixmap.cacheKey()); +} + +void QGLBlurTextureCache::insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info) +{ + static bool hookAdded = false; + if (!hookAdded) { + QImagePixmapCleanupHooks::instance()->addPixmapDestructionHook(pixmapDestroyed); + hookAdded = true; + } + + QImagePixmapCleanupHooks::enableCleanupHooks(pixmap); + cache.insert(pixmap.cacheKey(), info, pixmap.width() * pixmap.height()); + + if (timerId) + killTimer(timerId); + + timerId = startTimer(1000); +} + +bool QGLBlurTextureCache::fitsInCache(const QPixmap &pixmap) const +{ + return pixmap.width() * pixmap.height() <= cache.maxCost(); +} + +void QGLBlurTextureCache::pixmapDestroyed(QPixmap *pixmap) +{ + foreach (QGLBlurTextureCache *cache, blurTextureCaches) { + if (cache->hasBlurTextureInfo(*pixmap)) + cache->clearBlurTextureInfo(*pixmap); + } +} + +static const char *qt_gl_interpolate_filter = + "uniform lowp float interpolationValue;" + "uniform lowp sampler2D interpolateTarget;" + "uniform highp vec4 interpolateMapping;" + "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)" + "{" + " return mix(texture2D(interpolateTarget, interpolateMapping.xy + interpolateMapping.zw * srcCoords)," + " texture2D(src, srcCoords), interpolationValue);" + "}"; + +static void initializeTexture(GLuint id, int width, int height) +{ + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); +} + bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const { QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this); + QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); + QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx); + + if (m_hint == QGraphicsBlurEffect::AnimationHint && blurTextureCache->fitsInCache(src)) { + QRect targetRect = src.rect().adjusted(-qMaxCachedBlurLevel, -qMaxCachedBlurLevel, qMaxCachedBlurLevel, qMaxCachedBlurLevel); + // ensure even dimensions (going to divide by two) + targetRect.setWidth((targetRect.width() + 1) & ~1); + targetRect.setHeight((targetRect.height() + 1) & ~1); + + QGLBlurTextureInfo *info = 0; + if (blurTextureCache->hasBlurTextureInfo(src)) { + info = blurTextureCache->takeBlurTextureInfo(src); + } else { + m_animatedBlur = false; + m_hint = QGraphicsBlurEffect::QualityHint; + m_singlePass = false; + + QGLFramebufferObjectFormat format; + format.setInternalTextureFormat(GL_RGBA); + QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size() / 2, format, true); + + if (!fbo) + return false; + + QPainter fboPainter(fbo); + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(fboPainter.paintEngine()); + + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + // ensure GL_LINEAR filtering is used for scaling down to half the size + fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); + fboPainter.setCompositionMode(QPainter::CompositionMode_Source); + fboPainter.drawPixmap(qMaxCachedBlurLevel / 2, qMaxCachedBlurLevel / 2, + targetRect.width() / 2 - qMaxCachedBlurLevel, targetRect.height() / 2 - qMaxCachedBlurLevel, src); + + GLuint textures[qNumCachedBlurTextures]; // blur textures + glGenTextures(qNumCachedBlurTextures, textures); + GLuint temp; // temp texture + glGenTextures(1, &temp); + + initializeTexture(temp, fbo->width(), fbo->height()); + m_textureSize = fbo->size(); + + int currentBlur = 0; + + QRect fboRect(0, 0, fbo->width(), fbo->height()); + GLuint sourceTexture = fbo->texture(); + for (int i = 0; i < qNumCachedBlurTextures; ++i) { + int targetBlur = qCachedBlurLevels[i] / 2; + + int blurDelta = qRound(qSqrt(targetBlur * targetBlur - currentBlur * currentBlur)); + QByteArray source = generateGaussianShader(blurDelta); + filter->setSource(source); + + currentBlur = targetBlur; + + // now we're going to be nasty and keep using the same FBO with different textures + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, temp, 0); + + m_horizontalBlur = true; + filter->setOnPainter(&fboPainter); + engine->drawTexture(fboRect, sourceTexture, fbo->size(), fboRect); + filter->removeFromPainter(&fboPainter); + + sourceTexture = textures[i]; + initializeTexture(sourceTexture, fbo->width(), fbo->height()); + + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, textures[i], 0); + + m_horizontalBlur = false; + filter->setOnPainter(&fboPainter); + engine->drawTexture(fboRect, temp, fbo->size(), fboRect); + filter->removeFromPainter(&fboPainter); + } + + glDeleteTextures(1, &temp); + + // reattach the original FBO texture + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, fbo->texture(), 0); + + fboPainter.end(); + + qgl_fbo_pool()->release(fbo); + + info = new QGLBlurTextureInfo(fboRect.size(), textures); + } + + if (!m_haveCached || !m_animatedBlur) { + m_haveCached = true; + m_animatedBlur = true; + m_hint = QGraphicsBlurEffect::AnimationHint; + filter->setSource(qt_gl_interpolate_filter); + } + + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); + painter->setRenderHint(QPainter::SmoothPixmapTransform); + filter->setOnPainter(painter); + + qreal logRadius = qLn(radius()); + + int t; + for (t = -1; t < qNumCachedBlurTextures - 2; ++t) { + if (logRadius < qLogBlurLevel(t+1)) + break; + } + + qreal logBase = t >= 0 ? qLogBlurLevel(t) : 0; + m_t = qBound(qreal(0), (logRadius - logBase) / (qLogBlurLevel(t+1) - logBase), qreal(1)); + + m_textureSize = info->size(); + + glActiveTexture(GL_TEXTURE0 + 3); + if (t >= 0) { + glBindTexture(GL_TEXTURE_2D, info->textureId(t)); + m_targetSize = info->size(); + } else { + QGLTexture *texture = + ctx->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, + QGLContext::InternalBindOption + | QGLContext::CanFlipNativePixmapBindOption); + m_targetSize = src.size(); + if (!(texture->options & QGLContext::InvertedYBindOption)) + m_targetSize.setHeight(-m_targetSize.height()); + } + + // restrict the target rect to the max of the radii we are interpolating between + int radiusDelta = qMaxCachedBlurLevel - qCachedBlurLevels[t+1]; + targetRect = targetRect.translated(pos.toPoint()).adjusted(radiusDelta, radiusDelta, -radiusDelta, -radiusDelta); + + radiusDelta /= 2; + QRect sourceRect = QRect(QPoint(), m_textureSize).adjusted(radiusDelta, radiusDelta, -radiusDelta, -radiusDelta); + + engine->drawTexture(targetRect, info->textureId(t+1), m_textureSize, sourceRect); + + glActiveTexture(GL_TEXTURE0 + 3); + glBindTexture(GL_TEXTURE_2D, 0); + + filter->removeFromPainter(painter); + blurTextureCache->insertBlurTextureInfo(src, info); + + return true; + } + + if (blurTextureCache->hasBlurTextureInfo(src)) + blurTextureCache->clearBlurTextureInfo(src); + int actualRadius = qRound(radius()); int filterRadius = actualRadius; int fastRadii[] = { 1, 2, 3, 5, 8, 15, 25 }; - if (m_hint == Qt::PerformanceHint) { + if (m_hint != QGraphicsBlurEffect::QualityHint) { uint i = 0; for (; i < (sizeof(fastRadii)/sizeof(*fastRadii))-1; ++i) { if (fastRadii[i+1] > filterRadius) @@ -352,9 +686,10 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const m_singlePass = filterRadius <= 3; - if (!m_haveCached || filterRadius != m_cachedRadius) { + if (!m_haveCached || m_animatedBlur || filterRadius != m_cachedRadius) { // Only regenerate the shader from source if parameters have changed. m_haveCached = true; + m_animatedBlur = false; m_cachedRadius = filterRadius; QByteArray source = generateGaussianShader(filterRadius, m_singlePass); filter->setSource(source); @@ -389,13 +724,12 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPainter fboPainter(fbo); - if (src.hasAlphaChannel()) { - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - } + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); // ensure GL_LINEAR filtering is used fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); + fboPainter.setCompositionMode(QPainter::CompositionMode_Source); filter->setOnPainter(&fboPainter); QBrush pixmapBrush = src; pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius)); @@ -428,7 +762,29 @@ void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (m_hint == Qt::QualityHint) { + if (m_animatedBlur) { + program->setUniformValue("interpolateTarget", 3); + program->setUniformValue("interpolationValue", GLfloat(m_t)); + + if (m_textureSize == m_targetSize) { + program->setUniformValue("interpolateMapping", 0.0f, 0.0f, 1.0f, 1.0f); + } else { + float offsetX = (-qMaxCachedBlurLevel - 0.5) / qreal(m_targetSize.width()); + float offsetY = (-qMaxCachedBlurLevel - 0.5) / qreal(m_targetSize.height()); + + if (m_targetSize.height() < 0) + offsetY = 1 + offsetY; + + float scaleX = 2.0f * qreal(m_textureSize.width()) / qreal(m_targetSize.width()); + float scaleY = 2.0f * qreal(m_textureSize.height()) / qreal(m_targetSize.height()); + + program->setUniformValue("interpolateMapping", offsetX, offsetY, scaleX, scaleY); + } + + return; + } + + if (m_hint == QGraphicsBlurEffect::QualityHint) { if (m_singlePass) program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height()); else if (m_horizontalBlur) @@ -578,7 +934,7 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool singlePa return source; } -QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(Qt::RenderHint hint) +QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(QGraphicsBlurEffect::BlurHint hint) : m_haveCached(false) , m_cachedRadius(0) , m_hint(hint) @@ -632,13 +988,12 @@ bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, QPainter fboPainter(fbo); - if (src.hasAlphaChannel()) { - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - } + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); // ensure GL_LINEAR filtering is used fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); + fboPainter.setCompositionMode(QPainter::CompositionMode_Source); filter->setOnPainter(&fboPainter); QBrush pixmapBrush = src; pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius)); @@ -685,7 +1040,7 @@ void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program) alpha); } - if (m_hint == Qt::QualityHint) { + if (m_hint == QGraphicsBlurEffect::QualityHint) { if (m_singlePass) program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height()); else if (m_horizontalBlur) diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp index 90b496e..080c3b2 100644 --- a/src/opengl/qglshaderprogram.cpp +++ b/src/opengl/qglshaderprogram.cpp @@ -106,30 +106,6 @@ QT_BEGIN_NAMESPACE \snippet doc/src/snippets/code/src_opengl_qglshaderprogram.cpp 2 - \section1 Partial shaders - - Desktop GLSL can attach an arbitrary number of vertex and fragment - shaders to a shader program. Embedded GLSL/ES on the other hand - supports only a single shader of each type on a shader program. - - Multiple shaders of the same type can be useful when large libraries - of shaders are needed. Common functions can be factored out into - library shaders that can be reused in multiple shader programs. - - To support this use of shaders, the application programmer can - create shaders with the QGLShader::PartialVertexShader and - QGLShader::PartialFragmentShader types. These types direct - QGLShader and QGLShaderProgram to delay shader compilation until - link time. - - When link() is called, the sources for the partial shaders are - concatenated, and a single vertex or fragment shader is compiled - and linked into the shader program. - - It is more efficient to use the QGLShader::VertexShader and - QGLShader::FragmentShader when there is only one shader of that - type in the program. - \sa QGLShader */ @@ -154,11 +130,6 @@ QT_BEGIN_NAMESPACE \value VertexShader Vertex shader written in the OpenGL Shading Language (GLSL). \value FragmentShader Fragment shader written in the OpenGL Shading Language (GLSL). - - \value PartialVertexShader Partial vertex shader that will be concatenated with all other partial vertex shaders at link time. - \value PartialFragmentShader Partial fragment shader that will be concatenated with all other partial fragment shaders at link time. - - \omitvalue PartialShader */ #ifndef GL_FRAGMENT_SHADER @@ -209,8 +180,6 @@ public: : shaderGuard(context) , shaderType(type) , compiled(false) - , isPartial((type & QGLShader::PartialShader) != 0) - , hasPartialSource(false) { } ~QGLShaderPrivate(); @@ -218,10 +187,7 @@ public: QGLSharedResourceGuard shaderGuard; QGLShader::ShaderType shaderType; bool compiled; - bool isPartial; - bool hasPartialSource; QString log; - QByteArray partialSource; bool create(); bool compile(QGLShader *q); @@ -243,8 +209,6 @@ bool QGLShaderPrivate::create() const QGLContext *context = shaderGuard.context(); if (!context) return false; - if (isPartial) - return true; if (qt_resolve_glsl_extensions(const_cast<QGLContext *>(context))) { GLuint shader; if (shaderType == QGLShader::VertexShader) @@ -264,11 +228,6 @@ bool QGLShaderPrivate::create() bool QGLShaderPrivate::compile(QGLShader *q) { - // Partial shaders are compiled during QGLShaderProgram::link(). - if (isPartial && hasPartialSource) { - compiled = true; - return true; - } GLuint shader = shaderGuard.id(); if (!shader) return false; @@ -441,21 +400,12 @@ static const char redefineHighp[] = Sets the \a source code for this shader and compiles it. Returns true if the source was successfully compiled, false otherwise. - If shaderType() is PartialVertexShader or PartialFragmentShader, - then this function will always return true, even if the source code - is invalid. Partial shaders are compiled when QGLShaderProgram::link() - is called. - \sa compileFile() */ bool QGLShader::compile(const char *source) { Q_D(QGLShader); - if (d->isPartial) { - d->partialSource = QByteArray(source); - d->hasPartialSource = true; - return d->compile(this); - } else if (d->shaderGuard.id()) { + if (d->shaderGuard.id()) { QVarLengthArray<const char *, 4> src; QVarLengthArray<GLint, 4> srclen; int headerLen = 0; @@ -481,8 +431,7 @@ bool QGLShader::compile(const char *source) srclen.append(GLint(sizeof(qualifierDefines) - 1)); #endif #ifdef QGL_REDEFINE_HIGHP - if (d->shaderType == FragmentShader || - d->shaderType == PartialFragmentShader) { + if (d->shaderType == FragmentShader) { src.append(redefineHighp); srclen.append(GLint(sizeof(redefineHighp) - 1)); } @@ -497,71 +446,11 @@ bool QGLShader::compile(const char *source) } /*! - \internal -*/ -bool QGLShader::compile - (const QList<QGLShader *>& shaders, QGLShader::ShaderType type) -{ - Q_D(QGLShader); - QVarLengthArray<const char *, 16> src; - QVarLengthArray<GLint, 16> srclen; - if (!d->shaderGuard.id()) - return false; - foreach (QGLShader *shader, shaders) { - if (shader->shaderType() != type) - continue; - const char *source = shader->d_func()->partialSource.constData(); - int headerLen = 0; - if (src.isEmpty()) { - // First shader: handle the #version and #extension tags - // plus the precision qualifiers. - while (source && source[headerLen] == '#') { - // Skip #version and #extension directives at the start of - // the shader code. We need to insert the qualifierDefines - // and redefineHighp just after them. - if (qstrncmp(source + headerLen, "#version", 8) != 0 && - qstrncmp(source + headerLen, "#extension", 10) != 0) { - break; - } - while (source[headerLen] != '\0' && source[headerLen] != '\n') - ++headerLen; - if (source[headerLen] == '\n') - ++headerLen; - } - if (headerLen > 0) { - src.append(source); - srclen.append(GLint(headerLen)); - } -#ifdef QGL_DEFINE_QUALIFIERS - src.append(qualifierDefines); - srclen.append(GLint(sizeof(qualifierDefines) - 1)); -#endif -#ifdef QGL_REDEFINE_HIGHP - if (d->shaderType == FragmentShader || - d->shaderType == PartialFragmentShader) { - src.append(redefineHighp); - srclen.append(GLint(sizeof(redefineHighp) - 1)); - } -#endif - } - src.append(source + headerLen); - srclen.append(GLint(qstrlen(source + headerLen))); - } - glShaderSource(d->shaderGuard.id(), src.size(), src.data(), srclen.data()); - return d->compile(this); -} - -/*! \overload Sets the \a source code for this shader and compiles it. Returns true if the source was successfully compiled, false otherwise. - If shaderType() is PartialVertexShader or PartialFragmentShader, - then this function will always return true, even if the source code - is invalid. Partial shaders are compiled when QGLShaderProgram::link() - is called. - \sa compileFile() */ bool QGLShader::compile(const QByteArray& source) @@ -575,11 +464,6 @@ bool QGLShader::compile(const QByteArray& source) Sets the \a source code for this shader and compiles it. Returns true if the source was successfully compiled, false otherwise. - If shaderType() is PartialVertexShader or PartialFragmentShader, - then this function will always return true, even if the source code - is invalid. Partial shaders are compiled when QGLShaderProgram::link() - is called. - \sa compileFile() */ bool QGLShader::compile(const QString& source) @@ -592,11 +476,6 @@ bool QGLShader::compile(const QString& source) and compiles it. Returns true if the file could be opened and the source compiled, false otherwise. - If shaderType() is PartialVertexShader or PartialFragmentShader, - then this function will always return true, even if the source code - is invalid. Partial shaders are compiled when QGLShaderProgram::link() - is called. - \sa compile() */ bool QGLShader::compileFile(const QString& fileName) @@ -619,8 +498,6 @@ bool QGLShader::compileFile(const QString& fileName) QByteArray QGLShader::sourceCode() const { Q_D(const QGLShader); - if (d->isPartial) - return d->partialSource; GLuint shader = d->shaderGuard.id(); if (!shader) return QByteArray(); @@ -661,10 +538,6 @@ QString QGLShader::log() const /*! Returns the OpenGL identifier associated with this shader. - If shaderType() is PartialVertexShader or PartialFragmentShader, - this function will always return zero. Partial shaders are - created and compiled when QGLShaderProgram::link() is called. - \sa QGLShaderProgram::programId() */ GLuint QGLShader::shaderId() const @@ -684,7 +557,6 @@ public: : programGuard(context) , linked(false) , inited(false) - , hasPartialShaders(false) , removingShaders(false) , vertexShader(0) , fragmentShader(0) @@ -695,7 +567,6 @@ public: QGLSharedResourceGuard programGuard; bool linked; bool inited; - bool hasPartialShaders; bool removingShaders; QString log; QList<QGLShader *> shaders; @@ -812,13 +683,9 @@ bool QGLShaderProgram::addShader(QGLShader *shader) } if (!shader->d_func()->compiled) return false; - if (!shader->d_func()->isPartial) { - if (!shader->d_func()->shaderGuard.id()) - return false; - glAttachShader(d->programGuard.id(), shader->d_func()->shaderGuard.id()); - } else { - d->hasPartialShaders = true; - } + if (!shader->d_func()->shaderGuard.id()) + return false; + glAttachShader(d->programGuard.id(), shader->d_func()->shaderGuard.id()); d->linked = false; // Program needs to be relinked. d->shaders.append(shader); connect(shader, SIGNAL(destroyed()), this, SLOT(shaderDestroyed())); @@ -999,45 +866,6 @@ bool QGLShaderProgram::link() GLuint program = d->programGuard.id(); if (!program) return false; - if (d->hasPartialShaders) { - // Compile the partial vertex and fragment shaders. - if (d->hasShader(QGLShader::PartialVertexShader)) { - if (!d->vertexShader) { - d->vertexShader = - new QGLShader(QGLShader::VertexShader, this); - } - if (!d->vertexShader->compile - (d->shaders, QGLShader::PartialVertexShader)) { - d->log = d->vertexShader->log(); - return false; - } - glAttachShader(program, d->vertexShader->d_func()->shaderGuard.id()); - } else { - if (d->vertexShader) { - glDetachShader(program, d->vertexShader->d_func()->shaderGuard.id()); - delete d->vertexShader; - d->vertexShader = 0; - } - } - if (d->hasShader(QGLShader::PartialFragmentShader)) { - if (!d->fragmentShader) { - d->fragmentShader = - new QGLShader(QGLShader::FragmentShader, this); - } - if (!d->fragmentShader->compile - (d->shaders, QGLShader::PartialFragmentShader)) { - d->log = d->fragmentShader->log(); - return false; - } - glAttachShader(program, d->fragmentShader->d_func()->shaderGuard.id()); - } else { - if (d->fragmentShader) { - glDetachShader(program, d->fragmentShader->d_func()->shaderGuard.id()); - delete d->fragmentShader; - d->fragmentShader = 0; - } - } - } glLinkProgram(program); GLint value = 0; glGetProgramiv(program, GL_LINK_STATUS, &value); diff --git a/src/opengl/qglshaderprogram.h b/src/opengl/qglshaderprogram.h index 708cf09..b7bd2d7 100644 --- a/src/opengl/qglshaderprogram.h +++ b/src/opengl/qglshaderprogram.h @@ -66,12 +66,7 @@ public: enum ShaderTypeBits { VertexShader = 0x0001, - FragmentShader = 0x0002, - - PartialShader = 0x1000, - - PartialVertexShader = PartialShader | VertexShader, - PartialFragmentShader = PartialShader | FragmentShader + FragmentShader = 0x0002 }; Q_DECLARE_FLAGS(ShaderType, ShaderTypeBits) @@ -100,8 +95,6 @@ private: Q_DISABLE_COPY(QGLShader) Q_DECLARE_PRIVATE(QGLShader) - - bool compile(const QList<QGLShader *>& shaders, QGLShader::ShaderType type); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QGLShader::ShaderType) diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index c965947..5ca37ef 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -89,13 +89,22 @@ static inline QSize maybeRoundToNextPowerOfTwo(const QSize &sz) } -QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat) +QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat, bool strictSize) { QGLFramebufferObject *chosen = 0; QGLFramebufferObject *candidate = 0; for (int i = 0; !chosen && i < m_fbos.size(); ++i) { QGLFramebufferObject *fbo = m_fbos.at(i); + if (strictSize) { + if (fbo->size() == requestSize && fbo->format() == requestFormat) { + chosen = fbo; + break; + } else { + continue; + } + } + if (fbo->format() == requestFormat) { // choose the fbo with a matching format and the closest size if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo)) @@ -127,7 +136,10 @@ QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize } if (!chosen) { - chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat); + if (strictSize) + chosen = new QGLFramebufferObject(requestSize, requestFormat); + else + chosen = new QGLFramebufferObject(maybeRoundToNextPowerOfTwo(requestSize), requestFormat); } if (!chosen->isValid()) { @@ -140,7 +152,8 @@ QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo) { - m_fbos << fbo; + if (fbo) + m_fbos << fbo; } diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index 6190d38..8a13e03 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -69,7 +69,7 @@ class QGLPixmapData; class QGLFramebufferObjectPool { public: - QGLFramebufferObject *acquire(const QSize &size, const QGLFramebufferObjectFormat &format); + QGLFramebufferObject *acquire(const QSize &size, const QGLFramebufferObjectFormat &format, bool strictSize = false); void release(QGLFramebufferObject *fbo); private: diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp index 8a485a0..047d9d8 100644 --- a/src/openvg/qpaintengine_vg.cpp +++ b/src/openvg/qpaintengine_vg.cpp @@ -1178,6 +1178,8 @@ VGPaintType QVGPaintEnginePrivate::setBrush case Qt::TexturePattern: { // The brush is a texture specified by a QPixmap/QImage. QPixmapData *pd = brush.texture().pixmapData(); + if (!pd) + break; // null QPixmap VGImage vgImg; bool deref = false; if (pd->pixelType() == QPixmapData::BitmapType) { @@ -2893,6 +2895,8 @@ void qt_vg_drawVGImageStencil void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) { QPixmapData *pd = pm.pixmapData(); + if (!pd) + return; // null QPixmap if (pd->classId() == QPixmapData::OpenVGClass) { Q_D(QVGPaintEngine); QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); @@ -2910,6 +2914,8 @@ void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) { QPixmapData *pd = pm.pixmapData(); + if (!pd) + return; // null QPixmap if (pd->classId() == QPixmapData::OpenVGClass) { Q_D(QVGPaintEngine); QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); @@ -2985,6 +2991,8 @@ void QVGPaintEngine::drawPixmaps // If the pixmap is not VG, or the transformation is projective, // then fall back to the default implementation. QPixmapData *pd = pixmap.pixmapData(); + if (!pd) + return; // null QPixmap if (pd->classId() != QPixmapData::OpenVGClass || !d->simpleTransform) { QPaintEngineEx::drawPixmaps(drawingData, dataCount, pixmap, hints); return; @@ -3581,6 +3589,8 @@ void QVGCompositionHelper::drawCursorPixmap // Fetch the VGImage from the pixmap if possible. QPixmapData *pd = pixmap.pixmapData(); + if (!pd) + return; // null QPixmap if (pd->classId() == QPixmapData::OpenVGClass) { QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); if (vgpd->isValid()) diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index f86e116..3254aa3 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -369,6 +369,8 @@ QImage::Format QVGPixmapData::sourceFormat() const Q_OPENVG_EXPORT VGImage qPixmapToVGImage(const QPixmap& pixmap) { QPixmapData *pd = pixmap.pixmapData(); + if (!pd) + return VG_INVALID_HANDLE; // null QPixmap if (pd->classId() == QPixmapData::OpenVGClass) { QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pd); if (vgpd->isValid()) diff --git a/src/openvg/qpixmapfilter_vg.cpp b/src/openvg/qpixmapfilter_vg.cpp index 8e104db..e17c728 100644 --- a/src/openvg/qpixmapfilter_vg.cpp +++ b/src/openvg/qpixmapfilter_vg.cpp @@ -65,6 +65,9 @@ void QVGPixmapConvolutionFilter::draw (QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const { + if (src.isNull()) + return; + if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) { // The pixmap data is not an instance of QVGPixmapData, so fall // back to the default convolution filter implementation. @@ -135,6 +138,9 @@ QVGPixmapColorizeFilter::~QVGPixmapColorizeFilter() void QVGPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const { + if (src.isNull()) + return; + if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) { // The pixmap data is not an instance of QVGPixmapData, so fall // back to the default colorize filter implementation. @@ -225,6 +231,9 @@ QVGPixmapDropShadowFilter::~QVGPixmapDropShadowFilter() void QVGPixmapDropShadowFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const { + if (src.isNull()) + return; + if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) { // The pixmap data is not an instance of QVGPixmapData, so fall // back to the default drop shadow filter implementation. @@ -290,6 +299,9 @@ QVGPixmapBlurFilter::~QVGPixmapBlurFilter() void QVGPixmapBlurFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const { + if (src.isNull()) + return; + if (src.pixmapData()->classId() != QPixmapData::OpenVGClass) { // The pixmap data is not an instance of QVGPixmapData, so fall // back to the default blur filter implementation. |