diff options
author | axis <qt-info@nokia.com> | 2009-08-21 13:51:34 (GMT) |
---|---|---|
committer | axis <qt-info@nokia.com> | 2009-08-21 13:51:34 (GMT) |
commit | f51a3bf2ed75b68e6746f9154f3e44e48723a7fc (patch) | |
tree | 6105a10f236a9478404d2a48d7c68fc3fc4da592 /src | |
parent | afd7cfce7333635edc8d3637f81cc9c3023ee874 (diff) | |
parent | 4cc604be3fe96169c1dfdc05ff1b1a37e150e265 (diff) | |
download | Qt-f51a3bf2ed75b68e6746f9154f3e44e48723a7fc.zip Qt-f51a3bf2ed75b68e6746f9154f3e44e48723a7fc.tar.gz Qt-f51a3bf2ed75b68e6746f9154f3e44e48723a7fc.tar.bz2 |
Merge branch 'master' of git@scm.dev.nokia.troll.no:qt/qt into master-s60
Conflicts:
src/gui/graphicsview/qgraphicsitem.cpp
src/gui/kernel/qwidget.cpp
src/gui/kernel/qwidget_p.h
Diffstat (limited to 'src')
39 files changed, 3239 insertions, 228 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index e93e477..ab9a3ca 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1591,6 +1591,11 @@ public: Uninitialized }; + enum CoordinateSystem { + DeviceCoordinates, + LogicalCoordinates + }; + enum TouchPointState { TouchPointPressed = 0x01, TouchPointMoved = 0x02, diff --git a/src/gui/effects/effects.pri b/src/gui/effects/effects.pri new file mode 100644 index 0000000..0ebf96f --- /dev/null +++ b/src/gui/effects/effects.pri @@ -0,0 +1,4 @@ +HEADERS += effects/qgraphicseffect.h \ + effects/qgraphicseffect_p.h + +SOURCES += effects/qgraphicseffect.cpp diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp new file mode 100644 index 0000000..949922a --- /dev/null +++ b/src/gui/effects/qgraphicseffect.cpp @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QGraphicsEffect + \brief The QGraphicsEffect class is the base class for all graphics effects. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + + Effects alter the appearance of elements by hooking into the rendering + pipeline and operating between the source (e.g., a QGraphicsPixmapItem), + and the destination device (e.g., QGraphicsView's viewport). Effects can be + disabled by calling setEnabled(); if the effect is disabled, the source is + rendered directly. + + If you want to add a visual effect to a QGraphicsItem, you can either use + one of the standard effects, or create your own effect by making a subclass + of QGraphicsEffect. + + Qt provides several standard effects, including: + + \list + \o QGraphicsGrayScaleEffect - renders the item in shades of gray + \o QGraphicsColorizeEffect - renders the item in shades of any given color + \o QGraphicsPixelizeEffect - pixelizes the item with any pixel size + \o QGraphicsBlurEffect - blurs the item by a given radius + \o QGraphicsDropShadowEffect - renders a dropshadow behind the item + \endlist + + If all you want is to add an effect to an item, you should visit the + documentation for the specific effect to learn more about how each effect + can be used. + + If you want to create your own custom effect, you can start by creating a + subclass of QGraphicsEffect (or any of the existing effects), and + reimplement the virtual function draw(). This function is called whenever + the effect needs to redraw. draw() has two arguments: the painter, and a + pointer to the source (QGraphicsEffectSource). The source provides extra + context information, such as a pointer to the item that is rendering the + effect, any cached pixmap data, and the device rect bounds. See the draw() + documentation for more details. You can also get a pointer to the current + source by calling source(). + + If your effect changes, you can call update() to request a redraw. If your + custom effect changes the bounding rectangle of the source (e.g., a radial + glow effect may need to apply an extra margin), you can reimplement the + virtual boundingRectFor() function, and call updateBoundingRect() to notify + the framework whenever this rectangle changes. The virtual + sourceBoundingRectChanged() function is called to notify the effects that + the source's bounding rectangle has changed (e.g., if the source is a + QGraphicsRectItem, then if the rectangle parameters have changed). + + \sa QGraphicsItem::setGraphicsEffect() +*/ + +#include "qgraphicseffect_p.h" + +#include <QtGui/qimage.h> +#include <QtGui/qpainter.h> +#include <QtCore/qrect.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QGraphicsEffectSource::QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent) + : QObject(dd, parent) +{} + +QGraphicsEffectSource::~QGraphicsEffectSource() +{} + +QRect QGraphicsEffectSource::deviceRect() const +{ + return d_func()->deviceRect(); +} + +QRectF QGraphicsEffectSource::boundingRect(Qt::CoordinateSystem system) const +{ + return d_func()->boundingRect(system); +} + +const QGraphicsItem *QGraphicsEffectSource::graphicsItem() const +{ + return d_func()->graphicsItem(); +} + +const QWidget *QGraphicsEffectSource::widget() const +{ + return d_func()->widget(); +} + +const QStyleOption *QGraphicsEffectSource::styleOption() const +{ + return d_func()->styleOption(); +} + +void QGraphicsEffectSource::draw(QPainter *painter) +{ + d_func()->draw(painter); +} + +void QGraphicsEffectSource::update() +{ + d_func()->update(); +} + +bool QGraphicsEffectSource::isPixmap() const +{ + return d_func()->isPixmap(); +} + +QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offset) const +{ + return d_func()->pixmap(system, offset); +} + +/*! + Constructs a new QGraphicsEffect instance. +*/ +QGraphicsEffect::QGraphicsEffect(QObject *parent) + : QObject(*new QGraphicsEffectPrivate, parent) +{ +} + +/*! + \internal +*/ +QGraphicsEffect::QGraphicsEffect(QGraphicsEffectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Removes the effect from the source, and destroys the graphics effect. +*/ +QGraphicsEffect::~QGraphicsEffect() +{ + Q_D(QGraphicsEffect); + d->setGraphicsEffectSource(0); +} + +/*! + Returns the bounding rectangle for this effect (i.e., the bounding + rectangle of the source, adjusted by any margins applied by the effect + itself). + + \sa boundingRectFor(), updateBoundingRect() +*/ +QRectF QGraphicsEffect::boundingRect() const +{ + Q_D(const QGraphicsEffect); + if (d->source) + return boundingRectFor(d->source->boundingRect()); + return QRectF(); +} + +/*! + Returns the bounding rectangle for this effect, given the provided source + \a rect. When writing you own custom effect, you must call + updateBoundingRect() whenever any parameters are changed that may cause + this this function to return a different value. + + \sa boundingRect() +*/ +QRectF QGraphicsEffect::boundingRectFor(const QRectF &rect) const +{ + return rect; +} + +/*! + \property QGraphicsEffect::enabled + \brief whether the effect is enabled or not. + + If an effect is disabled, the source will be rendered with as normal, with + no interference from the effect. If the effect is enabled (default), the + source will be rendered with the effect applied. + + This property is provided so that you can disable certain effects on slow + platforms, in order to ensure that the user interface is responsive. +*/ +bool QGraphicsEffect::isEnabled() const +{ + Q_D(const QGraphicsEffect); + return d->isEnabled; +} + +void QGraphicsEffect::setEnabled(bool enable) +{ + Q_D(QGraphicsEffect); + if (d->isEnabled == enable) + return; + + d->isEnabled = enable; + if (d->source) + d->source->update(); + emit enabledChanged(enable); +} + +/*! + Returns a pointer to the source, which provides extra context information + that can be useful for the effect. + + \sa draw() +*/ +QGraphicsEffectSource *QGraphicsEffect::source() const +{ + Q_D(const QGraphicsEffect); + return d->source; +} + +/*! + This function notifies the effect framework that the effect's bounding + rectangle has changed. As a custom effect author, you must call this + function whenever you change any parameters that will cause the virtual + boundingRectFor() function to return a different value. + + \sa boundingRectFor(), boundingRect() +*/ +void QGraphicsEffect::updateBoundingRect() +{ + Q_D(QGraphicsEffect); + if (d->source) + d->source->update(); +} + +/*! + This virtual function is called by QGraphicsEffect to notify the effect + that the source has changed. If the effect applies any cache, then this + cache must be purged in order to reflect the new appearance of the source. + + The \a flags describes what has changed. +*/ +void QGraphicsEffect::sourceChanged(ChangeFlags flags) +{ + Q_UNUSED(flags); +} + +QGraphicsGrayscaleEffect::QGraphicsGrayscaleEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsGrayscaleEffectPrivate, parent) +{ +} + +QGraphicsGrayscaleEffect::~QGraphicsGrayscaleEffect() +{ +} + +void QGraphicsGrayscaleEffect::draw(QPainter *painter, QGraphicsEffectSource *source) +{ + Q_D(QGraphicsGrayscaleEffect); + QPoint offset; + if (source->isPixmap()) { + // No point in drawing in device coordinates (pixmap will be scaled anyways). + const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset); + d->filter->draw(painter, offset, pixmap); + return; + } + + // Draw pixmap in device coordinates to avoid pixmap scaling; + const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset); + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + d->filter->draw(painter, offset, pixmap); + painter->setWorldTransform(restoreTransform); + +} + +QGraphicsColorizeEffect::QGraphicsColorizeEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsColorizeEffectPrivate, parent) +{ +} + +QGraphicsColorizeEffect::~QGraphicsColorizeEffect() +{ +} + +QColor QGraphicsColorizeEffect::color() const +{ + Q_D(const QGraphicsColorizeEffect); + return d->filter->color(); +} + +void QGraphicsColorizeEffect::setColor(const QColor &c) +{ + Q_D(QGraphicsColorizeEffect); + if (d->filter->color() == c) + return; + + d->filter->setColor(c); + emit colorChanged(c); +} + +void QGraphicsColorizeEffect::draw(QPainter *painter, QGraphicsEffectSource *source) +{ + Q_D(QGraphicsColorizeEffect); + QPoint offset; + if (source->isPixmap()) { + // No point in drawing in device coordinates (pixmap will be scaled anyways). + const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset); + d->filter->draw(painter, offset, pixmap); + return; + } + + // Draw pixmap in deviceCoordinates to avoid pixmap scaling. + const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset); + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + d->filter->draw(painter, offset, pixmap); + painter->setWorldTransform(restoreTransform); +} + +QGraphicsPixelizeEffect::QGraphicsPixelizeEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsPixelizeEffectPrivate, parent) +{ +} + +QGraphicsPixelizeEffect::~QGraphicsPixelizeEffect() +{ +} + +int QGraphicsPixelizeEffect::pixelSize() const +{ + Q_D(const QGraphicsPixelizeEffect); + return d->pixelSize; +} + +void QGraphicsPixelizeEffect::setPixelSize(int size) +{ + Q_D(QGraphicsPixelizeEffect); + if (d->pixelSize == size) + return; + + d->pixelSize = size; + emit pixelSizeChanged(size); +} + +static inline void pixelize(QImage *image, int pixelSize) +{ + Q_ASSERT(pixelSize > 0); + Q_ASSERT(image); + int width = image->width(); + int height = image->height(); + for (int y = 0; y < height; y += pixelSize) { + int ys = qMin(height - 1, y + pixelSize / 2); + QRgb *sbuf = reinterpret_cast<QRgb*>(image->scanLine(ys)); + for (int x = 0; x < width; x += pixelSize) { + int xs = qMin(width - 1, x + pixelSize / 2); + QRgb color = sbuf[xs]; + for (int yi = 0; yi < qMin(pixelSize, height - y); ++yi) { + QRgb *buf = reinterpret_cast<QRgb*>(image->scanLine(y + yi)); + for (int xi = 0; xi < qMin(pixelSize, width - x); ++xi) + buf[x + xi] = color; + } + } + } +} + +void QGraphicsPixelizeEffect::draw(QPainter *painter, QGraphicsEffectSource *source) +{ + Q_D(QGraphicsPixelizeEffect); + if (d->pixelSize <= 0) { + source->draw(painter); + return; + } + + QPoint offset; + if (source->isPixmap()) { + const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset); + QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + pixelize(&image, d->pixelSize); + painter->drawImage(offset, image); + return; + } + + // Draw pixmap in device coordinates to avoid pixmap scaling. + const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset); + + // pixelize routine + QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + pixelize(&image, d->pixelSize); + + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + painter->drawImage(offset, image); + painter->setWorldTransform(restoreTransform); +} + +QGraphicsBlurEffect::QGraphicsBlurEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsBlurEffectPrivate, parent) +{ +} + +QGraphicsBlurEffect::~QGraphicsBlurEffect() +{ +} + +int QGraphicsBlurEffect::blurRadius() const +{ + Q_D(const QGraphicsBlurEffect); + return d->filter->radius(); +} + +void QGraphicsBlurEffect::setBlurRadius(int radius) +{ + Q_D(QGraphicsBlurEffect); + if (d->filter->radius() == radius) + return; + + d->filter->setRadius(radius); + updateBoundingRect(); + emit blurRadiusChanged(radius); +} + +QRectF QGraphicsBlurEffect::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QGraphicsBlurEffect); + return d->filter->boundingRectFor(rect); +} + +void QGraphicsBlurEffect::draw(QPainter *painter, QGraphicsEffectSource *source) +{ + Q_D(QGraphicsBlurEffect); + if (d->filter->radius() <= 0) { + source->draw(painter); + return; + } + + QPoint offset; + if (source->isPixmap()) { + // No point in drawing in device coordinates (pixmap will be scaled anyways). + const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset); + d->filter->draw(painter, offset, pixmap); + return; + } + + // Draw pixmap in device coordinates to avoid pixmap scaling. + const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset); + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + d->filter->draw(painter, offset, pixmap); + painter->setWorldTransform(restoreTransform); +} + +QGraphicsDropShadowEffect::QGraphicsDropShadowEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsDropShadowEffectPrivate, parent) +{ +} + +QGraphicsDropShadowEffect::~QGraphicsDropShadowEffect() +{ +} + +QPointF QGraphicsDropShadowEffect::offset() const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->offset(); +} + +void QGraphicsDropShadowEffect::setOffset(const QPointF &ofs) +{ + Q_D(QGraphicsDropShadowEffect); + if (d->filter->offset() == ofs) + return; + + d->filter->setOffset(ofs); + updateBoundingRect(); + emit offsetChanged(ofs); +} + +int QGraphicsDropShadowEffect::blurRadius() const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->blurRadius(); +} + +void QGraphicsDropShadowEffect::setBlurRadius(int blurRadius) +{ + Q_D(QGraphicsDropShadowEffect); + if (d->filter->blurRadius() == blurRadius) + return; + + d->filter->setBlurRadius(blurRadius); + updateBoundingRect(); + emit blurRadiusChanged(blurRadius); +} + +QColor QGraphicsDropShadowEffect::color() const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->color(); +} + +void QGraphicsDropShadowEffect::setColor(const QColor &color) +{ + Q_D(QGraphicsDropShadowEffect); + if (d->filter->color() == color) + return; + + d->filter->setColor(color); + emit colorChanged(color); +} + +QRectF QGraphicsDropShadowEffect::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QGraphicsDropShadowEffect); + return d->filter->boundingRectFor(rect); +} + +void QGraphicsDropShadowEffect::draw(QPainter *painter, QGraphicsEffectSource *source) +{ + Q_D(QGraphicsDropShadowEffect); + if (d->filter->blurRadius() <= 0 && d->filter->offset().isNull()) { + source->draw(painter); + return; + } + + QPoint offset; + if (source->isPixmap()) { + // No point in drawing in device coordinates (pixmap will be scaled anyways). + const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset); + d->filter->draw(painter, offset, pixmap); + return; + } + + // Draw pixmap in device coordinates to avoid pixmap scaling. + const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset); + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + d->filter->draw(painter, offset, pixmap); + painter->setWorldTransform(restoreTransform); +} + +QT_END_NAMESPACE + diff --git a/src/gui/effects/qgraphicseffect.h b/src/gui/effects/qgraphicseffect.h new file mode 100644 index 0000000..99d8548 --- /dev/null +++ b/src/gui/effects/qgraphicseffect.h @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSEFFECT_H +#define QGRAPHICSEFFECT_H + +#include <QtCore/qobject.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGraphicsItem; +class QStyleOption; +class QPainter; +class QPixmap; + +class QGraphicsEffectSourcePrivate; +class Q_GUI_EXPORT QGraphicsEffectSource : public QObject +{ + Q_OBJECT +public: + ~QGraphicsEffectSource(); + const QGraphicsItem *graphicsItem() const; + const QWidget *widget() const; + const QStyleOption *styleOption() const; + + bool isPixmap() const; + void draw(QPainter *painter); + void update(); + + QRectF boundingRect(Qt::CoordinateSystem coordinateSystem = Qt::LogicalCoordinates) const; + QRect deviceRect() const; + QPixmap pixmap(Qt::CoordinateSystem system = Qt::LogicalCoordinates, QPoint *offset = 0) const; + +protected: + QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent = 0); + +private: + Q_DECLARE_PRIVATE(QGraphicsEffectSource); + Q_DISABLE_COPY(QGraphicsEffectSource); + friend class QGraphicsEffect; + friend class QGraphicsEffectPrivate; + friend class QGraphicsScenePrivate; + friend class QGraphicsItem; + friend class QWidget; + friend class QWidgetPrivate; +}; + +class QGraphicsEffectPrivate; +class Q_GUI_EXPORT QGraphicsEffect : public QObject +{ + Q_OBJECT + Q_FLAGS(ChangeFlags) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) +public: + enum ChangeFlag { + SourceAttached = 0x1, + SourceDetached = 0x2, + SourceBoundingRectChanged = 0x4, + SourceInvalidated = 0x8 + }; + Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag); + + QGraphicsEffect(QObject *parent = 0); + virtual ~QGraphicsEffect(); + + virtual QRectF boundingRectFor(const QRectF &rect) const; + QRectF boundingRect() const; + + QGraphicsEffectSource *source() const; + + bool isEnabled() const; + +public Q_SLOTS: + void setEnabled(bool enable); + // ### add update() slot + +Q_SIGNALS: + void enabledChanged(bool enabled); + +protected: + QGraphicsEffect(QGraphicsEffectPrivate &d, QObject *parent = 0); + virtual void draw(QPainter *painter, QGraphicsEffectSource *source) = 0; + virtual void sourceChanged(ChangeFlags flags); + void updateBoundingRect(); + +private: + Q_DECLARE_PRIVATE(QGraphicsEffect) + Q_DISABLE_COPY(QGraphicsEffect) + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + friend class QGraphicsScenePrivate; + friend class QWidget; + friend class QWidgetPrivate; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsEffect::ChangeFlags); + +class QGraphicsGrayscaleEffectPrivate; +class Q_GUI_EXPORT QGraphicsGrayscaleEffect: public QGraphicsEffect +{ + Q_OBJECT +public: + QGraphicsGrayscaleEffect(QObject *parent = 0); + ~QGraphicsGrayscaleEffect(); + +protected: + void draw(QPainter *painter, QGraphicsEffectSource *source); + +private: + Q_DECLARE_PRIVATE(QGraphicsGrayscaleEffect) + Q_DISABLE_COPY(QGraphicsGrayscaleEffect) +}; + +class QGraphicsColorizeEffectPrivate; +class Q_GUI_EXPORT QGraphicsColorizeEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) +public: + QGraphicsColorizeEffect(QObject *parent = 0); + ~QGraphicsColorizeEffect(); + + QColor color() const; + +public Q_SLOTS: + void setColor(const QColor &c); + +Q_SIGNALS: + void colorChanged(const QColor &color); + +protected: + void draw(QPainter *painter, QGraphicsEffectSource *source); + +private: + Q_DECLARE_PRIVATE(QGraphicsColorizeEffect) + Q_DISABLE_COPY(QGraphicsColorizeEffect) +}; + +class QGraphicsPixelizeEffectPrivate; +class Q_GUI_EXPORT QGraphicsPixelizeEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_PROPERTY(int pixelSize READ pixelSize WRITE setPixelSize NOTIFY pixelSizeChanged) +public: + QGraphicsPixelizeEffect(QObject *parent = 0); + ~QGraphicsPixelizeEffect(); + + int pixelSize() const; + +public Q_SLOTS: + void setPixelSize(int pixelSize); + +Q_SIGNALS: + void pixelSizeChanged(int pixelSize); + +protected: + void draw(QPainter *painter, QGraphicsEffectSource *source); + +private: + Q_DECLARE_PRIVATE(QGraphicsPixelizeEffect) + Q_DISABLE_COPY(QGraphicsPixelizeEffect) +}; + +class QGraphicsBlurEffectPrivate; +class Q_GUI_EXPORT QGraphicsBlurEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_PROPERTY(int blurRadius READ blurRadius WRITE setBlurRadius NOTIFY blurRadiusChanged) +public: + QGraphicsBlurEffect(QObject *parent = 0); + ~QGraphicsBlurEffect(); + + QRectF boundingRectFor(const QRectF &rect) const; + int blurRadius() const; + +public Q_SLOTS: + void setBlurRadius(int blurRadius); + +Q_SIGNALS: + void blurRadiusChanged(int blurRadius); + +protected: + void draw(QPainter *painter, QGraphicsEffectSource *source); + +private: + Q_DECLARE_PRIVATE(QGraphicsBlurEffect) + Q_DISABLE_COPY(QGraphicsBlurEffect) +}; + +class QGraphicsDropShadowEffectPrivate; +class Q_GUI_EXPORT QGraphicsDropShadowEffect: public QGraphicsEffect +{ + Q_OBJECT + Q_PROPERTY(QPointF offset READ offset WRITE setOffset NOTIFY offsetChanged) + Q_PROPERTY(int blurRadius READ blurRadius WRITE setBlurRadius NOTIFY blurRadiusChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) +public: + QGraphicsDropShadowEffect(QObject *parent = 0); + ~QGraphicsDropShadowEffect(); + + QRectF boundingRectFor(const QRectF &rect) const; + QPointF offset() const; + int blurRadius() const; + QColor color() const; + +public Q_SLOTS: + void setOffset(const QPointF &ofs); + inline void setOffset(qreal dx, qreal dy) + { setOffset(QPointF(dx, dy)); } + inline void setOffset(qreal d) + { setOffset(QPointF(d, d)); } + void setBlurRadius(int blurRadius); + void setColor(const QColor &color); + +Q_SIGNALS: + void offsetChanged(const QPointF &offset); + void blurRadiusChanged(int blurRadius); + void colorChanged(const QColor &color); + +protected: + void draw(QPainter *painter, QGraphicsEffectSource *source); + +private: + Q_DECLARE_PRIVATE(QGraphicsDropShadowEffect) + Q_DISABLE_COPY(QGraphicsDropShadowEffect) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSEFFECT_H + diff --git a/src/gui/effects/qgraphicseffect_p.h b/src/gui/effects/qgraphicseffect_p.h new file mode 100644 index 0000000..35f2d40 --- /dev/null +++ b/src/gui/effects/qgraphicseffect_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSEFFECT_P_H +#define QGRAPHICSEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicseffect.h" + +#include <private/qobject_p.h> +#include <private/qpixmapfilter_p.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsEffectSourcePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsEffectSource) +public: + QGraphicsEffectSourcePrivate() : QObjectPrivate() {} + virtual ~QGraphicsEffectSourcePrivate() {} + virtual void detach() = 0; + virtual QRectF boundingRect(Qt::CoordinateSystem system) const = 0; + virtual QRect deviceRect() const = 0; + virtual const QGraphicsItem *graphicsItem() const = 0; + virtual const QWidget *widget() const = 0; + virtual const QStyleOption *styleOption() const = 0; + virtual void draw(QPainter *p) = 0; + virtual void update() = 0; + virtual bool isPixmap() const = 0; + virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0) const = 0; + friend class QGraphicsScenePrivate; + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; +}; + +class Q_GUI_EXPORT QGraphicsEffectPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsEffect) +public: + QGraphicsEffectPrivate() : source(0), isEnabled(1) {} + + inline void setGraphicsEffectSource(QGraphicsEffectSource *newSource) + { + QGraphicsEffect::ChangeFlags flags; + if (source) { + flags |= QGraphicsEffect::SourceDetached; + source->d_func()->detach(); + delete source; + } + source = newSource; + if (newSource) + flags |= QGraphicsEffect::SourceAttached; + q_func()->sourceChanged(flags); + } + + QGraphicsEffectSource *source; + QRectF boundingRect; + quint32 isEnabled : 1; + quint32 padding : 31; // feel free to use +}; + +class QGraphicsGrayscaleEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsGrayscaleEffect) +public: + QGraphicsGrayscaleEffectPrivate() + { + filter = new QPixmapColorizeFilter; + filter->setColor(Qt::black); + } + ~QGraphicsGrayscaleEffectPrivate() { delete filter; } + + QPixmapColorizeFilter *filter; +}; + +class QGraphicsColorizeEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsColorizeEffect) +public: + QGraphicsColorizeEffectPrivate() { filter = new QPixmapColorizeFilter; } + ~QGraphicsColorizeEffectPrivate() { delete filter; } + + QPixmapColorizeFilter *filter; +}; + +class QGraphicsPixelizeEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsPixelizeEffect) +public: + QGraphicsPixelizeEffectPrivate() : pixelSize(3) {} + + int pixelSize; +}; + +class QGraphicsBlurEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsBlurEffect) +public: + QGraphicsBlurEffectPrivate() : filter(new QPixmapBlurFilter) {} + ~QGraphicsBlurEffectPrivate() { delete filter; } + + QPixmapBlurFilter *filter; +}; + +class QGraphicsDropShadowEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsDropShadowEffect) +public: + QGraphicsDropShadowEffectPrivate() : filter(new QPixmapDropShadowFilter) {} + ~QGraphicsDropShadowEffectPrivate() { delete filter; } + + QPixmapDropShadowFilter *filter; +}; + +QT_END_NAMESPACE + +#endif // QGRAPHICSEFFECT_P_H + diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 88fac14..2a364c9 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -582,6 +582,7 @@ #include <QtGui/qstyleoption.h> #include <QtGui/qevent.h> #include <QtGui/qinputcontext.h> +#include <QtGui/qgraphicseffect.h> #include <private/qgraphicsitem_p.h> #include <private/qgraphicswidget_p.h> @@ -1055,11 +1056,6 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) */ void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rect) { - if (!dirtyChildrenBoundingRect) { - *rect |= x->mapRect(childrenBoundingRect); - return; - } - for (int i = 0; i < children.size(); ++i) { QGraphicsItem *child = children.at(i); QGraphicsItemPrivate *childd = child->d_ptr.data(); @@ -1067,19 +1063,20 @@ void QGraphicsItemPrivate::childrenBoundingRectHelper(QTransform *x, QRectF *rec if (hasPos || childd->transformData) { // COMBINE QTransform matrix = childd->transformToParent(); - matrix *= *x; + if (x) + matrix *= *x; *rect |= matrix.mapRect(child->boundingRect()); if (!childd->children.isEmpty()) childd->childrenBoundingRectHelper(&matrix, rect); } else { - *rect |= x->mapRect(child->boundingRect()); + if (x) + *rect |= x->mapRect(child->boundingRect()); + else + *rect |= child->boundingRect(); if (!childd->children.isEmpty()) childd->childrenBoundingRectHelper(x, rect); } } - - childrenBoundingRect = *rect; - dirtyChildrenBoundingRect = 0; } void QGraphicsItemPrivate::initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, @@ -1223,6 +1220,7 @@ QGraphicsItem::~QGraphicsItem() d_ptr->setParentItemHelper(0); } + delete d_ptr->graphicsEffect; if (d_ptr->transformData) { for(int i = 0; i < d_ptr->transformData->graphicsTransforms.size(); ++i) { QGraphicsTransform *t = d_ptr->transformData->graphicsTransforms.at(i); @@ -2030,8 +2028,8 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo enabled = newEnabledVariant.toBool(); // Schedule redraw. - if (update && scene) - scene->d_func()->markDirty(q_ptr); + if (update) + q_ptr->update(); foreach (QGraphicsItem *child, children) { if (!newEnabled || !child->d_ptr->explicitlyDisabled) @@ -2135,8 +2133,8 @@ void QGraphicsItem::setSelected(bool selected) return; d_ptr->selected = newSelected; + update(); if (d_ptr->scene) { - d_ptr->scene->d_func()->markDirty(this); QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func(); if (selected) { sceneD->selectedItems << this; @@ -2243,6 +2241,118 @@ void QGraphicsItem::setOpacity(qreal opacity) } /*! + Returns a pointer to this item's effect if it has one; otherwise 0. + + \since 4.6 +*/ +QGraphicsEffect *QGraphicsItem::graphicsEffect() const +{ + return d_ptr->graphicsEffect; +} + +/*! + Sets \a effect as the item's effect. If there already is an effect installed + on this item, QGraphicsItem will delete the existing effect before installing + the new \a effect. + + If \a effect is the installed on a different item, setGraphicsEffect() will remove + the effect from the item and install it on this item. + + \note This function will apply the effect on itself and all its children. + + \since 4.6 +*/ +void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect) +{ + if (d_ptr->graphicsEffect == effect) + return; + + if (d_ptr->graphicsEffect && effect) { + delete d_ptr->graphicsEffect; + d_ptr->graphicsEffect = 0; + } + + if (!effect) { + // Unset current effect. + QGraphicsEffectPrivate *oldEffectPrivate = d_ptr->graphicsEffect->d_func(); + d_ptr->graphicsEffect = 0; + if (oldEffectPrivate) { + oldEffectPrivate->setGraphicsEffectSource(0); // deletes the current source. + if (d_ptr->scene) // Update the views directly. + d_ptr->scene->d_func()->markDirty(this, QRectF(), false, false, false, false, true); + } + } else { + // Set new effect. + QGraphicsEffectSourcePrivate *sourced = new QGraphicsItemEffectSourcePrivate(this); + QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced); + d_ptr->graphicsEffect = effect; + effect->d_func()->setGraphicsEffectSource(source); + } + + prepareGeometryChange(); +} + +/*! + \internal + \since 4.6 + Returns the effective bounding rect of the item. + If the item has no effect, this is the same as the item's bounding rect. + If the item has an effect, the effective rect can be larger than the item's + bouding rect, depending on the effect. + + \sa boundingRect() +*/ +QRectF QGraphicsItemPrivate::effectiveBoundingRect() const +{ + QGraphicsEffect *effect = graphicsEffect; + QRectF brect = effect && effect->isEnabled() ? effect->boundingRect() : q_ptr->boundingRect(); + if (ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + return brect; + + const QGraphicsItem *effectParent = parent; + while (effectParent) { + effect = effectParent->d_ptr->graphicsEffect; + if (effect && effect->isEnabled()) + brect = effect->boundingRectFor(brect); + if (effectParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + return brect; + effectParent = effectParent->d_ptr->parent; + } + + return brect; +} + +/*! + \internal + \since 4.6 + Returns the effective bounding rect of this item in scene coordinates, + by combining sceneTransform() with boundingRect(), taking into account + the effect that the item might have. + + If the item has no effect, this is the same as sceneBoundingRect(). + + \sa effectiveBoundingRect(), sceneBoundingRect() +*/ +QRectF QGraphicsItemPrivate::sceneEffectiveBoundingRect() const +{ + // Find translate-only offset + // COMBINE + QPointF offset; + const QGraphicsItem *parentItem = q_ptr; + const QGraphicsItemPrivate *itemd; + do { + itemd = parentItem->d_ptr; + if (itemd->transformData) + break; + offset += itemd->pos; + } while ((parentItem = itemd->parent)); + + QRectF br = effectiveBoundingRect(); + br.translate(offset); + return !parentItem ? br : parentItem->sceneTransform().mapRect(br); +} + +/*! Returns true if this item can accept drag and drop events; otherwise, returns false. By default, items do not accept drag and drop events; items are transparent to drag and drop. @@ -3769,10 +3879,10 @@ QRectF QGraphicsItem::childrenBoundingRect() const if (!d_ptr->dirtyChildrenBoundingRect) return d_ptr->childrenBoundingRect; - QRectF childRect; - QTransform x; - d_ptr->childrenBoundingRectHelper(&x, &childRect); - return childRect; + d_ptr->childrenBoundingRect = QRectF(); + d_ptr->childrenBoundingRectHelper(0, &d_ptr->childrenBoundingRect); + d_ptr->dirtyChildrenBoundingRect = 0; + return d_ptr->childrenBoundingRect; } /*! @@ -4723,6 +4833,13 @@ void QGraphicsItem::update(const QRectF &rect) if (rect.isEmpty() && !rect.isNull()) return; + // Make sure we notify effects about invalidated source. + QGraphicsItem *item = this; + do { + if (item->d_ptr->graphicsEffect) + item->d_ptr->notifyInvalidated = 1; + } while ((item = item->d_ptr->parent)); + if (CacheMode(d_ptr->cacheMode) != NoCache) { QGraphicsItemCache *cache = d_ptr->extraItemCache(); if (d_ptr->discardUpdateRequest(/* ignoreVisibleBit = */ false, @@ -6062,6 +6179,7 @@ void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event) void QGraphicsItem::focusInEvent(QFocusEvent *event) { Q_UNUSED(event); + update(); } /*! @@ -6073,6 +6191,7 @@ void QGraphicsItem::focusInEvent(QFocusEvent *event) void QGraphicsItem::focusOutEvent(QFocusEvent *event) { Q_UNUSED(event); + update(); } /*! @@ -6087,8 +6206,7 @@ void QGraphicsItem::focusOutEvent(QFocusEvent *event) void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - if (d_ptr->scene) - d_ptr->scene->d_func()->markDirty(this); + update(); } /*! @@ -6116,8 +6234,7 @@ void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - if (d_ptr->scene) - d_ptr->scene->d_func()->markDirty(this); + update(); } /*! @@ -6618,6 +6735,7 @@ void QGraphicsItem::prepareGeometryChange() d_ptr->scene->d_func()->dirtyGrowingItemsBoundingRect = true; d_ptr->geometryChanged = 1; d_ptr->paintedViewBoundingRectsNeedRepaint = 1; + d_ptr->notifyBoundingRectChanged = !d_ptr->inSetPosHelper; QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func(); scenePrivate->index->prepareBoundingRectChange(this); @@ -6641,8 +6759,11 @@ void QGraphicsItem::prepareGeometryChange() } QGraphicsItem *parent = this; - while ((parent = parent->d_ptr->parent)) + while ((parent = parent->d_ptr->parent)) { parent->d_ptr->dirtyChildrenBoundingRect = 1; + // ### Only do this if the parent's effect applies to the entire subtree. + parent->d_ptr->notifyBoundingRectChanged = 1; + } if (d_ptr->inSetPosHelper) return; @@ -10045,6 +10166,125 @@ int QGraphicsItemGroup::type() const return Type; } +QRectF QGraphicsItemEffectSourcePrivate::boundingRect(Qt::CoordinateSystem system) const +{ + const bool deviceCoordinates = (system == Qt::DeviceCoordinates); + if (!info && deviceCoordinates) { + // Device coordinates without info not yet supported. + qWarning("QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context"); + return QRectF(); + } + + QRectF rect = item->boundingRect(); + if (!item->d_ptr->children.isEmpty()) + rect |= item->childrenBoundingRect(); + + if (deviceCoordinates) { + Q_ASSERT(info->painter); + rect = info->painter->worldTransform().mapRect(rect); + } + + return rect; +} + +void QGraphicsItemEffectSourcePrivate::draw(QPainter *painter) +{ + if (!info) { + qWarning("QGraphicsEffectSource::draw: Can only begin as a result of QGraphicsEffect::draw"); + return; + } + + Q_ASSERT(item->d_ptr->scene); + QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func(); + if (painter == info->painter) { + scened->draw(item, painter, info->viewTransform, info->transformPtr, info->exposedRegion, + info->widget, info->opacity, info->effectTransform, info->wasDirtySceneTransform, + info->drawItem); + } else { + QTransform effectTransform = painter->worldTransform(); + effectTransform *= info->painter->worldTransform().inverted(); + if (info->effectTransform) + effectTransform *= *info->effectTransform; + scened->draw(item, painter, info->viewTransform, info->transformPtr, info->exposedRegion, + info->widget, info->opacity, &effectTransform, info->wasDirtySceneTransform, + info->drawItem); + } +} + +QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const +{ + const bool deviceCoordinates = (system == Qt::DeviceCoordinates); + if (!info && deviceCoordinates) { + // Device coordinates without info not yet supported. + qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); + return QPixmap(); + } + + if (!item->d_ptr->scene) + return QPixmap(); + QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func(); + + const QRectF sourceRect = boundingRect(system); + QRect effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect(); + if (offset) + *offset = effectRect.topLeft(); + + if (deviceCoordinates) { + // Clip to viewport rect. + int left, top, right, bottom; + effectRect.getCoords(&left, &top, &right, &bottom); + if (left < 0) { + if (offset) + offset->rx() += -left; + effectRect.setX(0); + } + if (top < 0) { + if (offset) + offset->ry() += -top; + effectRect.setY(0); + } + // NB! We use +-1 for historical reasons (see QRect documentation). + if (right + 1 > info->widget->width()) + effectRect.setRight(info->widget->width() - 1); + if (bottom + 1 > info->widget->height()) + effectRect.setBottom(info->widget->height() -1); + } + + if (effectRect.isEmpty()) + return QPixmap(); + + QPixmap pixmap(effectRect.size()); + pixmap.fill(Qt::transparent); + QPainter pixmapPainter(&pixmap); + pixmapPainter.setRenderHints(info ? info->painter->renderHints() : QPainter::TextAntialiasing); + + QTransform effectTransform = QTransform::fromTranslate(-effectRect.x(), -effectRect.y()); + if (deviceCoordinates && info->effectTransform) + effectTransform *= *info->effectTransform; + + if (!info) { + // Logical coordinates without info. + QTransform sceneTransform = item->sceneTransform(); + QTransform newEffectTransform = sceneTransform.inverted(); + newEffectTransform *= effectTransform; + scened->draw(item, &pixmapPainter, 0, &sceneTransform, 0, 0, qreal(1.0), + &newEffectTransform, false, true); + } else if (deviceCoordinates) { + // Device coordinates with info. + scened->draw(item, &pixmapPainter, info->viewTransform, info->transformPtr, info->exposedRegion, + info->widget, info->opacity, &effectTransform, info->wasDirtySceneTransform, + info->drawItem); + } else { + // Item coordinates with info. + QTransform newEffectTransform = info->transformPtr->inverted(); + newEffectTransform *= effectTransform; + scened->draw(item, &pixmapPainter, info->viewTransform, info->transformPtr, info->exposedRegion, + info->widget, info->opacity, &newEffectTransform, info->wasDirtySceneTransform, + info->drawItem); + } + return pixmap; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, QGraphicsItem *item) { diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 176d719..7af7c2f 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -63,6 +63,7 @@ QT_MODULE(Gui) class QBrush; class QCursor; class QFocusEvent; +class QGraphicsEffect; class QGraphicsItemGroup; class QGraphicsObject; class QGraphicsSceneContextMenuEvent; @@ -210,6 +211,10 @@ public: qreal effectiveOpacity() const; void setOpacity(qreal opacity); + // Effect + QGraphicsEffect *graphicsEffect() const; + void setGraphicsEffect(QGraphicsEffect *effect); + Qt::MouseButtons acceptedMouseButtons() const; void setAcceptedMouseButtons(Qt::MouseButtons buttons); @@ -447,6 +452,7 @@ private: friend class QGraphicsSceneIndexPrivate; friend class QGraphicsSceneBspTreeIndex; friend class QGraphicsSceneBspTreeIndexPrivate; + friend class QGraphicsItemEffectSourcePrivate; friend class QGraphicsTransformPrivate; friend class ::tst_QGraphicsItem; friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index fea82ae..11f6f53 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -60,6 +60,8 @@ #include "qgraphicstransform.h" #include <private/qgraphicstransform_p.h> +#include <private/qgraphicseffect_p.h> + #include <QtCore/qpoint.h> #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW @@ -121,6 +123,7 @@ public: scene(0), parent(0), transformData(0), + graphicsEffect(0), index(-1), siblingIndex(-1), itemDepth(-1), @@ -165,6 +168,8 @@ public: acceptedTouchBeginEvent(0), filtersDescendantEvents(0), sceneTransformTranslateOnly(0), + notifyBoundingRectChanged(0), + notifyInvalidated(0), mouseSetsFocus(1), globalStackingOrder(-1), q_ptr(0) @@ -218,6 +223,8 @@ public: void childrenBoundingRectHelper(QTransform *x, QRectF *rect); void initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, const QRegion &exposedRegion, bool allItems = false) const; + QRectF effectiveBoundingRect() const; + QRectF sceneEffectiveBoundingRect() const; virtual void resolveFont(uint inheritedMask) { @@ -418,6 +425,7 @@ public: QList<QGraphicsItem *> children; struct TransformData; TransformData *transformData; + QGraphicsEffect *graphicsEffect; QTransform sceneTransform; int index; int siblingIndex; @@ -468,8 +476,10 @@ public: quint32 acceptedTouchBeginEvent : 1; quint32 filtersDescendantEvents : 1; quint32 sceneTransformTranslateOnly : 1; + quint32 notifyBoundingRectChanged : 1; + quint32 notifyInvalidated : 1; quint32 mouseSetsFocus : 1; - quint32 unused : 3; // feel free to use + quint32 unused : 1; // feel free to use // Optional stacking order int globalStackingOrder; @@ -516,6 +526,74 @@ struct QGraphicsItemPrivate::TransformData } }; +struct QGraphicsItemPaintInfo +{ + inline QGraphicsItemPaintInfo(const QTransform *const xform1, const QTransform *const xform2, + const QTransform *const xform3, + QRegion *r, QWidget *w, QStyleOptionGraphicsItem *opt, + QPainter *p, qreal o, bool b1, bool b2) + : viewTransform(xform1), transformPtr(xform2), effectTransform(xform3), exposedRegion(r), widget(w), + option(opt), painter(p), opacity(o), wasDirtySceneTransform(b1), drawItem(b2) + {} + + const QTransform *viewTransform; + const QTransform *transformPtr; + const QTransform *effectTransform; + QRegion *exposedRegion; + QWidget *widget; + QStyleOptionGraphicsItem *option; + QPainter *painter; + qreal opacity; + quint32 wasDirtySceneTransform : 1; + quint32 drawItem : 1; +}; + +class QGraphicsItemEffectSourcePrivate : public QGraphicsEffectSourcePrivate +{ +public: + QGraphicsItemEffectSourcePrivate(QGraphicsItem *i) + : QGraphicsEffectSourcePrivate(), item(i), info(0) + {} + + inline void detach() + { item->setGraphicsEffect(0); } + + inline const QGraphicsItem *graphicsItem() const + { return item; } + + inline const QWidget *widget() const + { return 0; } + + inline void update() + { item->update(); } + + inline bool isPixmap() const + { + return (item->type() == QGraphicsPixmapItem::Type); + //|| (item->d_ptr->isObject && qobject_cast<QFxImage *>(q_func())); + } + + inline const QStyleOption *styleOption() const + { return info ? info->option : 0; } + + inline QRect deviceRect() const + { + if (!info || !info->widget) { + qWarning("QGraphicsEffectSource::deviceRect: Not yet implemented, lacking device context"); + return QRect(); + } + return info->widget->rect(); + } + + QRectF boundingRect(Qt::CoordinateSystem system) const; + void draw(QPainter *); + QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const; + + QGraphicsItem *item; + QGraphicsItemPaintInfo *info; +}; + + /*! \internal */ diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a8db74d..a819822 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -244,11 +244,13 @@ #include <QtGui/qtransform.h> #include <QtGui/qgesture.h> #include <QtGui/qinputcontext.h> +#include <QtGui/qgraphicseffect.h> #include <private/qapplication_p.h> #include <private/qobject_p.h> #ifdef Q_WS_X11 #include <private/qt_x11_p.h> #endif +#include <private/qgraphicseffect_p.h> QT_BEGIN_NAMESPACE @@ -4302,7 +4304,7 @@ void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform, QRegion *exposedRegion, QWidget *widget, - qreal parentOpacity) + qreal parentOpacity, const QTransform *const effectTransform) { Q_ASSERT(item); @@ -4351,11 +4353,12 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); bool drawItem = itemHasContents && !itemIsFullyTransparent; if (drawItem) { - const QRectF brect = adjustedItemBoundingRect(item); + const QRectF brect = adjustedItemEffectiveBoundingRect(item); ENSURE_TRANSFORM_PTR QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect() : transformPtr->mapRect(brect).toRect(); - item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); + if (widget) + item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); viewBoundingRect.adjust(-1, -1, 1, 1); drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) : !viewBoundingRect.isEmpty(); if (!drawItem) { @@ -4369,14 +4372,51 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * } } // else we know for sure this item has children we must process. + if (itemHasChildren && itemClipsChildrenToShape) + ENSURE_TRANSFORM_PTR; + + if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) { + ENSURE_TRANSFORM_PTR; + QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp, + painter, opacity, wasDirtyParentSceneTransform, drawItem); + QGraphicsEffectSource *source = item->d_ptr->graphicsEffect->d_func()->source; + QGraphicsItemEffectSourcePrivate *sourced = static_cast<QGraphicsItemEffectSourcePrivate *> + (source->d_func()); + sourced->info = &info; + const QTransform restoreTransform = painter->worldTransform(); + if (effectTransform) + painter->setWorldTransform(*transformPtr * *effectTransform); + else + painter->setWorldTransform(*transformPtr); + item->d_ptr->graphicsEffect->draw(painter, source); + painter->setWorldTransform(restoreTransform); + sourced->info = 0; + } else { + draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity, + effectTransform, wasDirtyParentSceneTransform, drawItem); + } +} + +void QGraphicsScenePrivate::draw(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform, + const QTransform *const transformPtr, QRegion *exposedRegion, QWidget *widget, + qreal opacity, const QTransform *effectTransform, + bool wasDirtyParentSceneTransform, bool drawItem) +{ + const bool itemIsFullyTransparent = (opacity < 0.0001); + const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + const bool itemHasChildren = !item->d_ptr->children.isEmpty(); + int i = 0; if (itemHasChildren) { item->d_ptr->ensureSortedChildren(); if (itemClipsChildrenToShape) { painter->save(); - ENSURE_TRANSFORM_PTR - painter->setWorldTransform(*transformPtr); + Q_ASSERT(transformPtr); + if (effectTransform) + painter->setWorldTransform(*transformPtr * *effectTransform); + else + painter->setWorldTransform(*transformPtr); painter->setClipPath(item->shape(), Qt::IntersectClip); } @@ -4389,15 +4429,15 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * break; if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) continue; - drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity); + drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform); } } // Draw item if (drawItem) { Q_ASSERT(!itemIsFullyTransparent); - Q_ASSERT(itemHasContents); - ENSURE_TRANSFORM_PTR + Q_ASSERT(!(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents)); + Q_ASSERT(transformPtr); item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion ? *exposedRegion : QRegion(), exposedRegion == 0); @@ -4406,8 +4446,12 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * if (savePainter) painter->save(); - if (!itemHasChildren || !itemClipsChildrenToShape) - painter->setWorldTransform(*transformPtr); + if (!itemHasChildren || !itemClipsChildrenToShape) { + if (effectTransform) + painter->setWorldTransform(*transformPtr * *effectTransform); + else + painter->setWorldTransform(*transformPtr); + } if (itemClipsToShape) painter->setClipPath(item->shape(), Qt::IntersectClip); @@ -4430,7 +4474,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * child->d_ptr->dirtySceneTransform = 1; if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity)) continue; - drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity); + drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform); } } @@ -4513,8 +4557,12 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b item->d_ptr->ignoreOpacity = 1; QGraphicsItem *p = item->d_ptr->parent; - while (p && !p->d_ptr->dirtyChildren) { + while (p) { p->d_ptr->dirtyChildren = 1; + if (p->d_ptr->graphicsEffect && p->d_ptr->graphicsEffect->isEnabled()) { + p->d_ptr->dirty = 1; + p->d_ptr->fullUpdatePending = 1; + } p = p->d_ptr->parent; } } @@ -4623,7 +4671,7 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool // Process item. if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) { const bool useCompatUpdate = views.isEmpty() || isSignalConnected(changedSignalIndex); - const QRectF itemBoundingRect = adjustedItemBoundingRect(item); + const QRectF itemBoundingRect = adjustedItemEffectiveBoundingRect(item); if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) { // This block of code is kept for compatibility. Since 4.5, by default diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 0ef9f04..26cb1c2 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -302,10 +302,12 @@ private: friend class QGraphicsViewPrivate; friend class QGraphicsWidget; friend class QGraphicsWidgetPrivate; + friend class QGraphicsEffect; friend class QGraphicsSceneIndex; friend class QGraphicsSceneIndexPrivate; friend class QGraphicsSceneBspTreeIndex; friend class QGraphicsSceneBspTreeIndexPrivate; + friend class QGraphicsItemEffectSourcePrivate; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers) diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index f1ddb5a..4b8791e 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -202,7 +202,11 @@ public: QRegion *exposedRegion, QWidget *widget); void drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform *const, - QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0)); + QRegion *exposedRegion, QWidget *widget, qreal parentOpacity = qreal(1.0), + const QTransform *const effectTransform = 0); + void draw(QGraphicsItem *, QPainter *, const QTransform *const, const QTransform *const, + QRegion *, QWidget *, qreal, const QTransform *const, bool, bool); + void markDirty(QGraphicsItem *item, const QRectF &rect = QRectF(), bool invalidateChildren = false, bool maybeDirtyClipPath = false, bool force = false, bool ignoreOpacity = false, bool removingItemFromScene = false); @@ -223,10 +227,21 @@ public: item->d_ptr->fullUpdatePending = 0; item->d_ptr->ignoreVisible = 0; item->d_ptr->ignoreOpacity = 0; + QGraphicsEffect::ChangeFlags flags; + if (item->d_ptr->notifyBoundingRectChanged) { + flags |= QGraphicsEffect::SourceBoundingRectChanged; + item->d_ptr->notifyBoundingRectChanged = 0; + } + if (item->d_ptr->notifyInvalidated) { + flags |= QGraphicsEffect::SourceInvalidated; + item->d_ptr->notifyInvalidated = 0; + } if (recursive) { for (int i = 0; i < item->d_ptr->children.size(); ++i) resetDirtyItem(item->d_ptr->children.at(i), recursive); } + if (flags && item->d_ptr->graphicsEffect) + item->d_ptr->graphicsEffect->sourceChanged(flags); } inline void ensureSortedTopLevelItems() @@ -280,6 +295,14 @@ static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) return boundingRect; } +static inline QRectF adjustedItemEffectiveBoundingRect(const QGraphicsItem *item) +{ + Q_ASSERT(item); + QRectF boundingRect(QGraphicsItemPrivate::get(item)->effectiveBoundingRect()); + _q_adjustRect(&boundingRect); + return boundingRect; +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index 7483bc1..b9c4ed8 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -172,7 +172,7 @@ void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex() if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) continue; - bsp.insertItem(item, item->sceneBoundingRect()); + bsp.insertItem(item, item->d_ptr->sceneEffectiveBoundingRect()); } } unindexedItems.clear(); @@ -352,7 +352,7 @@ void QGraphicsSceneBspTreeIndexPrivate::removeItem(QGraphicsItem *item, bool rec purgePending = true; removedItems << item; } else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { - bsp.removeItem(item, item->sceneBoundingRect()); + bsp.removeItem(item, item->d_ptr->sceneEffectiveBoundingRect()); } } else { unindexedItems.removeOne(item); diff --git a/src/gui/gui.pro b/src/gui/gui.pro index ad74e47..eb3a33f 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -35,6 +35,7 @@ include(graphicsview/graphicsview.pri) include(util/util.pri) include(statemachine/statemachine.pri) include(math3d/math3d.pri) +include(effects/effects.pri) contains(QT_CONFIG, egl): include(egl/egl.pri) diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp index 968ecd7..bc14b5e 100644 --- a/src/gui/image/qpixmapfilter.cpp +++ b/src/gui/image/qpixmapfilter.cpp @@ -93,6 +93,9 @@ public: \value DropShadowFilter A filter that is used to add a drop shadow to an image. See QPixmapDropShadowFilter for more information. + \value BlurFilter A filter that is used to blur an image using + a simple blur radius. See QPixmapBlurFilter + for more information. \value UserFilter The first filter type that can be used for application-specific purposes. @@ -475,6 +478,227 @@ void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const Q } } +/*! + \class QPixmapBlurFilter + \since 4.6 + \ingroup multimedia + + \brief The QPixmapBlurFilter class provides blur filtering + for pixmaps. + + QPixmapBlurFilter implements a blur pixmap filter, + which is applied when \l{QPixmapFilter::}{draw()} is called. + + The filter lets you specialize the radius of the blur as well + as the quality. + + By default, the blur effect is produced by applying an exponential + filter generated from the specified blurRadius(). Paint engines + may override this with a custom blur that is faster on the + underlying hardware. + + \sa {Pixmap Filters Example}, QPixmapConvolutionFilter, QPixmapDropShadowFilter + + \internal +*/ + +class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate +{ +public: + QPixmapBlurFilterPrivate() : radius(5), quality(Qt::FastTransformation) {} + + int radius; + Qt::TransformationMode quality; +}; + + +/*! + Constructs a pixmap blur filter. + + \internal +*/ +QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent) + : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent) +{ +} + +/*! + Destructor of pixmap blur filter. + + \internal +*/ +QPixmapBlurFilter::~QPixmapBlurFilter() +{ +} + +/*! + Sets the radius of the blur filter. Higher radius produces increased blurriness. + + \internal +*/ +void QPixmapBlurFilter::setRadius(int radius) +{ + Q_D(QPixmapBlurFilter); + d->radius = radius; +} + +/*! + Gets the radius of the blur filter. + + \internal +*/ +int QPixmapBlurFilter::radius() const +{ + Q_D(const QPixmapBlurFilter); + return d->radius; +} + +/*! + Setting the quality to FastTransformation causes the implementation + to trade off visual quality to blur the image faster. Setting the + quality to SmoothTransformation 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. + + \internal +*/ +void QPixmapBlurFilter::setQuality(Qt::TransformationMode quality) +{ + Q_D(QPixmapBlurFilter); + d->quality = quality; +} + +/*! + Gets the quality of the blur filter. + + \internal +*/ +Qt::TransformationMode QPixmapBlurFilter::quality() const +{ + Q_D(const QPixmapBlurFilter); + return d->quality; +} + +/*! + \reimp + + \internal +*/ +QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const +{ + Q_D(const QPixmapBlurFilter); + const qreal delta = d->radius * 2; + return rect.adjusted(-delta, -delta, delta, delta); +} + +// Blur the image according to the blur radius +// Based on exponential blur algorithm by Jani Huhtanen +// (maximum radius is set to 16) +static QImage blurred(const QImage& image, const QRect& rect, int radius) +{ + int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 }; + int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1]; + + QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + int r1 = rect.top(); + int r2 = rect.bottom(); + int c1 = rect.left(); + int c2 = rect.right(); + + int bpl = result.bytesPerLine(); + int rgba[4]; + unsigned char* p; + + for (int col = c1; col <= c2; col++) { + p = result.scanLine(r1) + col * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p += bpl; + for (int j = r1; j < r2; j++, p += bpl) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int row = r1; row <= r2; row++) { + p = result.scanLine(row) + c1 * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p += 4; + for (int j = c1; j < c2; j++, p += 4) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int col = c1; col <= c2; col++) { + p = result.scanLine(r2) + col * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p -= bpl; + for (int j = r1; j < r2; j++, p -= bpl) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int row = r1; row <= r2; row++) { + p = result.scanLine(row) + c2 * 4; + for (int i = 0; i < 4; i++) + rgba[i] = p[i] << 4; + + p -= 4; + for (int j = c1; j < c2; j++, p -= 4) + for (int i = 0; i < 4; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + return result; +} + +/*! + \reimp + + \internal +*/ +void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect) const +{ + Q_D(const QPixmapBlurFilter); + if (!painter->isActive()) + return; + + if (d->radius == 0) { + painter->drawPixmap(srcRect.translated(p), src, srcRect); + return; + } + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast<QPaintEngineEx *>(painter->paintEngine())->createPixmapFilter(type()) : 0; + QPixmapBlurFilter *blurFilter = static_cast<QPixmapBlurFilter*>(filter); + if (blurFilter) { + blurFilter->setRadius(d->radius); + blurFilter->setQuality(d->quality); + blurFilter->draw(painter, p, src, srcRect); + delete blurFilter; + return; + } + + QImage srcImage; + QImage destImage; + + if (srcRect.isNull()) { + srcImage = src.toImage(); + destImage = blurred(srcImage, srcImage.rect(), d->radius); + } else { + QRect rect = srcRect.toAlignedRect().intersected(src.rect()); + + srcImage = src.copy(rect).toImage(); + destImage = blurred(srcImage, srcImage.rect(), d->radius); + } + + painter->drawImage(p, destImage); +} + // grayscales the image to dest (could be same). If rect isn't defined // destination image size is used to determine the dimension of grayscaling // process. @@ -626,16 +850,11 @@ class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate { public: QPixmapDropShadowFilterPrivate() - : offset(8, 8), - radius(1), - color(63, 63, 63, 255) { - } + : offset(8, 8), color(63, 63, 63, 180), blurFilter(new QPixmapBlurFilter) {} QPointF offset; - qreal radius; QColor color; - - QPixmapConvolutionFilter *convolution; + QPixmapBlurFilter *blurFilter; }; /*! @@ -680,9 +899,7 @@ QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent) : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent) { Q_D(QPixmapDropShadowFilter); - d->convolution = new QPixmapConvolutionFilter; - - setBlurRadius(1); + d->blurFilter->setRadius(1); } /*! @@ -693,7 +910,7 @@ QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent) QPixmapDropShadowFilter::~QPixmapDropShadowFilter() { Q_D(QPixmapDropShadowFilter); - delete d->convolution; + delete d->blurFilter; } /*! @@ -705,10 +922,10 @@ QPixmapDropShadowFilter::~QPixmapDropShadowFilter() \internal */ -qreal QPixmapDropShadowFilter::blurRadius() const +int QPixmapDropShadowFilter::blurRadius() const { Q_D(const QPixmapDropShadowFilter); - return d->radius; + return d->blurFilter->radius(); } /*! @@ -720,18 +937,10 @@ qreal QPixmapDropShadowFilter::blurRadius() const \internal */ -void QPixmapDropShadowFilter::setBlurRadius(qreal radius) +void QPixmapDropShadowFilter::setBlurRadius(int radius) { Q_D(QPixmapDropShadowFilter); - - d->radius = radius; - - int dim = 2 * qRound(radius) + 1; - QVarLengthArray<qreal> arr(dim * dim); - qreal f = 1 / qreal(dim * dim); - for (int i = 0; i < dim * dim; ++i) - arr[i] = f; - d->convolution->setConvolutionKernel(arr.data(), dim, dim); + d->blurFilter->setRadius(radius); } /*! @@ -805,10 +1014,11 @@ QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const { Q_D(const QPixmapDropShadowFilter); - qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - d->radius); - qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - d->radius); - qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + d->radius); - qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + d->radius); + const qreal delta = qreal(d->blurFilter->radius() * 2); + qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - delta); + qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - delta); + qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + delta); + qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + delta); return QRectF(x1, y1, x2 - x1, y2 - y1); } @@ -823,7 +1033,7 @@ void QPixmapDropShadowFilter::draw(QPainter *p, { Q_D(const QPixmapDropShadowFilter); - QPixmap tmp = src.isNull() ? px : px.copy(src.toRect()); + QImage tmp = src.isNull() ? px.toImage() : px.copy(src.toRect()).toImage(); QPainter tmpPainter(&tmp); // blacken the image... @@ -831,10 +1041,13 @@ void QPixmapDropShadowFilter::draw(QPainter *p, tmpPainter.fillRect(0, 0, tmp.width(), tmp.height(), d->color); tmpPainter.end(); + const QPixmap pixTmp = QPixmap::fromImage(tmp); + // draw the blurred drop shadow... - d->convolution->draw(p, pos + d->offset, tmp); + d->blurFilter->draw(p, pos + d->offset, pixTmp); // Draw the actual pixmap... p->drawPixmap(pos, px, src); } + QT_END_NAMESPACE diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h index b8588bd..29d919d 100644 --- a/src/gui/image/qpixmapfilter_p.h +++ b/src/gui/image/qpixmapfilter_p.h @@ -78,6 +78,7 @@ public: ConvolutionFilter, ColorizeFilter, DropShadowFilter, + BlurFilter, UserFilter = 1024 }; @@ -117,6 +118,30 @@ private: int columns() const; }; +class QPixmapBlurFilterPrivate; + +class Q_GUI_EXPORT QPixmapBlurFilter : public QPixmapFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapBlurFilter) + +public: + QPixmapBlurFilter(QObject *parent = 0); + ~QPixmapBlurFilter(); + + void setRadius(int radius); + void setQuality(Qt::TransformationMode mode); + + int radius() const; + Qt::TransformationMode quality() const; + + QRectF boundingRectFor(const QRectF &rect) const; + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const; + +private: + friend class QGLPixmapBlurFilter; +}; + class QPixmapColorizeFilterPrivate; class Q_GUI_EXPORT QPixmapColorizeFilter : public QPixmapFilter @@ -147,8 +172,8 @@ public: QRectF boundingRectFor(const QRectF &rect) const; void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src = QRectF()) const; - qreal blurRadius() const; - void setBlurRadius(qreal radius); + int blurRadius() const; + void setBlurRadius(int radius); QColor color() const; void setColor(const QColor &color); diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 4de08fd..bbda469 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -186,6 +186,7 @@ QWidgetPrivate::QWidgetPrivate(int version) , widgetItem(0) , extraPaintEngine(0) , polished(0) + , graphicsEffect(0) , inheritedFontResolveMask(0) , inheritedPaletteResolveMask(0) , leftmargin(0) @@ -242,6 +243,8 @@ QWidgetPrivate::~QWidgetPrivate() if (extra) deleteExtra(); + + delete graphicsEffect; } QWindowSurface *QWidgetPrivate::createDefaultWindowSurface() @@ -1628,9 +1631,9 @@ bool QWidgetPrivate::isOverlapped(const QRect &rect) const continue; } - if (qRectIntersects(sibling->data->crect, r)) { + if (qRectIntersects(sibling->d_func()->effectiveRectFor(sibling->data->crect), r)) { const QWExtra *siblingExtra = sibling->d_func()->extra; - if (siblingExtra && siblingExtra->hasMask + if (siblingExtra && siblingExtra->hasMask && !sibling->d_func()->graphicsEffect && !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) { continue; } @@ -1729,7 +1732,7 @@ QRect QWidgetPrivate::clipRect() const const QWidget * w = q; if (!w->isVisible()) return QRect(); - QRect r = q->rect(); + QRect r = effectiveRectFor(q->rect()); int ox = 0; int oy = 0; while (w @@ -1871,12 +1874,14 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt break; QWidgetPrivate *pd = w->parentWidget()->d_func(); const int myIndex = pd->children.indexOf(const_cast<QWidget *>(w)); + const QRect widgetGeometry = w->d_func()->effectiveRectFor(w->data->crect); for (int i = myIndex + 1; i < pd->children.size(); ++i) { QWidget *sibling = qobject_cast<QWidget *>(pd->children.at(i)); if (!sibling || !sibling->isVisible() || sibling->isWindow()) continue; - if (!qRectIntersects(sibling->data->crect, w->data->crect)) + const QRect siblingGeometry = sibling->d_func()->effectiveRectFor(sibling->data->crect); + if (!qRectIntersects(siblingGeometry, widgetGeometry)) continue; if (dirtyClipBoundingRect) { @@ -1884,7 +1889,7 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt dirtyClipBoundingRect = false; } - if (!qRectIntersects(sibling->data->crect, clipBoundingRect.translated(parentOffset))) + if (!qRectIntersects(siblingGeometry, clipBoundingRect.translated(parentOffset))) continue; if (dirtyParentClip) { @@ -1896,7 +1901,8 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt const QRect siblingClipRect(sibling->d_func()->clipRect()); QRegion siblingDirty(parentClip); siblingDirty &= (siblingClipRect.translated(siblingPos)); - const bool hasMask = sibling->d_func()->extra && sibling->d_func()->extra->hasMask; + const bool hasMask = sibling->d_func()->extra && sibling->d_func()->extra->hasMask + && !sibling->d_func()->graphicsEffect; if (hasMask) siblingDirty &= sibling->d_func()->extra->mask.translated(siblingPos); if (siblingDirty.isEmpty()) @@ -1907,7 +1913,7 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt siblingDirty.translate(-parentOffset); sourceRegion -= siblingDirty; } else { - sourceRegion -= sibling->data->crect.translated(-parentOffset); + sourceRegion -= siblingGeometry.translated(-parentOffset); } } else { if (hasDirtySiblingsAbove) @@ -1938,6 +1944,11 @@ void QWidgetPrivate::clipToEffectiveMask(QRegion ®ion) const const QWidget *w = q; QPoint offset; + if (graphicsEffect) { + w = q->parentWidget(); + offset -= data.crect.topLeft(); + } + while (w) { const QWidgetPrivate *wd = w->d_func(); if (wd->extra && wd->extra->hasMask) @@ -1971,6 +1982,12 @@ void QWidgetPrivate::updateIsOpaque() // hw: todo: only needed if opacity actually changed setDirtyOpaqueRegion(); + if (graphicsEffect) { + // ### We should probably add QGraphicsEffect::isOpaque at some point. + setOpaque(false); + return; + } + Q_Q(QWidget); #ifdef Q_WS_X11 if (q->testAttribute(Qt::WA_X11OpenGLOverlay)) { @@ -4948,6 +4965,54 @@ void QWidgetPrivate::setSoftKeys_sys(const QList<QAction*> &softkeys) } #endif // !defined(Q_OS_SYMBIAN) +QGraphicsEffect *QWidget::graphicsEffect() const +{ + Q_D(const QWidget); + return d->graphicsEffect; +} + +/*! + Sets \a effect as the widget's effect. If there already is an effect installed + on this widget, QWidget will delete the existing effect before installing + the new \a effect. + + If \a effect is the installed on a different widget, setGraphicsEffect() will remove + the effect from the widget and install it on this widget. + + \note This function will apply the effect on itself and all its children. + + \since 4.6 +*/ +void QWidget::setGraphicsEffect(QGraphicsEffect *effect) +{ + Q_D(QWidget); + if (d->graphicsEffect == effect) + return; + + if (d->graphicsEffect && effect) { + delete d->graphicsEffect; + d->graphicsEffect = 0; + } + + if (!effect) { + // Unset current effect. + QGraphicsEffectPrivate *oldEffectPrivate = d->graphicsEffect->d_func(); + d->graphicsEffect = 0; + if (oldEffectPrivate) { + oldEffectPrivate->setGraphicsEffectSource(0); // deletes the current source. + } + } else { + // Set new effect. + QGraphicsEffectSourcePrivate *sourced = new QWidgetEffectSourcePrivate(this); + QGraphicsEffectSource *source = new QGraphicsEffectSource(*sourced); + d->graphicsEffect = effect; + effect->d_func()->setGraphicsEffectSource(source); + } + + d->updateIsOpaque(); + update(); +} + bool QWidgetPrivate::isAboutToShow() const { if (data.in_show) @@ -5092,6 +5157,33 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP return; Q_Q(QWidget); + if (graphicsEffect && graphicsEffect->isEnabled()) { + QGraphicsEffectSource *source = graphicsEffect->d_func()->source; + QWidgetEffectSourcePrivate *sourced = static_cast<QWidgetEffectSourcePrivate *> + (source->d_func()); + if (!sourced->context) { + QWidgetPaintContext context(pdev, rgn, offset, flags, sharedPainter, backingStore); + sourced->context = &context; + if (!sharedPainter) { + QPaintEngine *paintEngine = pdev->paintEngine(); + paintEngine->d_func()->systemClip = rgn.translated(offset); + QPainter p(pdev); + p.translate(offset); + context.painter = &p; + graphicsEffect->draw(&p, source); + paintEngine->d_func()->systemClip = QRegion(); + } else { + context.painter = sharedPainter; + sharedPainter->save(); + sharedPainter->translate(offset); + graphicsEffect->draw(sharedPainter, source); + sharedPainter->restore(); + } + sourced->context = 0; + return; + } + } + const bool asRoot = flags & DrawAsRoot; const bool alsoOnScreen = flags & DrawPaintOnScreen; const bool recursive = flags & DrawRecursive; @@ -5124,7 +5216,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP if (sharedPainter) paintEngine->d_func()->systemClip = toBePainted; else - paintEngine->setSystemRect(q->data->crect); + paintEngine->d_func()->systemRect = q->data->crect; //paint the background if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground)) @@ -5163,7 +5255,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP if (paintEngine) { restoreRedirected(); if (!sharedPainter) - paintEngine->setSystemRect(QRect()); + paintEngine->d_func()->systemRect = QRect(); else paintEngine->d_func()->currentClipWidget = 0; paintEngine->d_func()->systemClip = QRegion(); @@ -5226,7 +5318,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis dirtyBoundingRect = false; } - if (qRectIntersects(boundingRect, x->data->crect)) { + if (qRectIntersects(boundingRect, x->d_func()->effectiveRectFor(x->data->crect))) { #ifdef Q_BACKINGSTORE_SUBSURFACES if (x->windowSurface() == currentSurface) #endif @@ -5244,7 +5336,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis QWidgetPrivate *wd = w->d_func(); const QPoint widgetPos(w->data->crect.topLeft()); - const bool hasMask = wd->extra && wd->extra->hasMask; + const bool hasMask = wd->extra && wd->extra->hasMask && !wd->graphicsEffect; if (index > 0) { QRegion wr(rgn); @@ -5259,7 +5351,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis if (w->updatesEnabled() && (!w->d_func()->extra || !w->d_func()->extra->proxyWidget)) { QRegion wRegion(rgn); - wRegion &= w->data->crect; + wRegion &= wd->effectiveRectFor(w->data->crect); wRegion.translate(-widgetPos); if (hasMask) wRegion &= wd->extra->mask; @@ -5267,6 +5359,93 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis } } +QRectF QWidgetEffectSourcePrivate::boundingRect(Qt::CoordinateSystem system) const +{ + if (system != Qt::DeviceCoordinates) + return m_widget->rect(); + + if (!context) { + // Device coordinates without context not yet supported. + qWarning("QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context"); + return QRectF(); + } + + return context->painter->worldTransform().mapRect(m_widget->rect()); +} + +void QWidgetEffectSourcePrivate::draw(QPainter *painter) +{ + if (!context || context->painter != painter) { + m_widget->render(painter); + return; + } + + // The region saved in the context is neither clipped to the rect + // nor the mask, so we have to clip it here before calling drawWidget. + QRegion toBePainted = context->rgn; + toBePainted &= m_widget->rect(); + QWidgetPrivate *wd = qt_widget_private(m_widget); + if (wd->extra && wd->extra->hasMask) + toBePainted &= wd->extra->mask; + + wd->drawWidget(context->pdev, toBePainted, context->offset, context->flags, + context->sharedPainter, context->backingStore); +} + +QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const +{ + const bool deviceCoordinates = (system == Qt::DeviceCoordinates); + if (!context && deviceCoordinates) { + // Device coordinates without context not yet supported. + qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); + return QPixmap(); + } + + QPoint pixmapOffset; + QRectF sourceRect = m_widget->rect(); + + if (deviceCoordinates) { + const QTransform &painterTransform = context->painter->worldTransform(); + sourceRect = painterTransform.mapRect(sourceRect); + pixmapOffset = painterTransform.map(pixmapOffset); + } + + QRect effectRect = m_widget->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect(); + if (offset) + *offset = effectRect.topLeft(); + + if (deviceCoordinates) { + // Clip to device rect. + int left, top, right, bottom; + effectRect.getCoords(&left, &top, &right, &bottom); + if (left < 0) { + if (offset) + offset->rx() += -left; + effectRect.setX(0); + } + if (top < 0) { + if (offset) + offset->ry() += -top; + effectRect.setY(0); + } + // NB! We use +-1 for historical reasons (see QRect documentation). + QPaintDevice *device = context->painter->device(); + const int deviceWidth = device->width(); + const int deviceHeight = device->height(); + if (right + 1 > deviceWidth) + effectRect.setRight(deviceWidth - 1); + if (bottom + 1 > deviceHeight) + effectRect.setBottom(deviceHeight -1); + } + + pixmapOffset -= effectRect.topLeft(); + + QPixmap pixmap(effectRect.size()); + pixmap.fill(Qt::transparent); + m_widget->render(&pixmap, pixmapOffset); + return pixmap; +} + /*! \internal diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index a40b52d..2484ba6 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -96,6 +96,7 @@ class QWindowSurface; class QLocale; class QGraphicsProxyWidget; class QGestureManager; +class QGraphicsEffect; #if defined(Q_WS_X11) class QX11Info; #endif @@ -353,6 +354,9 @@ public: const QRegion &sourceRegion = QRegion(), RenderFlags renderFlags = RenderFlags(DrawWindowBackground | DrawChildren)); + QGraphicsEffect *graphicsEffect() const; + void setGraphicsEffect(QGraphicsEffect *effect); + public Q_SLOTS: void setWindowTitle(const QString &); #ifndef QT_NO_STYLE_STYLESHEET diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 4bd7222..ea5a53e 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -2567,7 +2567,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); qt_mac_event_release(this); if(testAttribute(Qt::WA_WState_Created)) { @@ -2673,7 +2673,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) OSViewRef old_id = 0; if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); // Maintain the glWidgets list on parent change: remove "our" gl widgets // from the list on the old parent and grandparents. @@ -4169,12 +4169,12 @@ void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isM setWSGeometry(false, oldRect); if (isResize && QApplicationPrivate::graphicsSystem()) { invalidateBuffer(q->rect()); - if (extra && !extra->mask.isEmpty()) { + if (extra && !graphicsEffect && !extra->mask.isEmpty()) { QRegion oldRegion(extra->mask.translated(oldp)); oldRegion &= oldRect; q->parentWidget()->d_func()->invalidateBuffer(oldRegion); } else { - q->parentWidget()->d_func()->invalidateBuffer(oldRect); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(oldRect)); } } } diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 8e58498..ed0abce 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -62,6 +62,7 @@ #include "QtGui/qsizepolicy.h" #include "QtGui/qstyle.h" #include "QtGui/qapplication.h" +#include <private/qgraphicseffect_p.h> #ifdef Q_WS_WIN #include "QtCore/qt_windows.h" @@ -460,6 +461,13 @@ public: return extra ? extra->nativeChildrenForced : false; } + inline QRect effectiveRectFor(const QRect &rect) const + { + if (graphicsEffect && graphicsEffect->isEnabled()) + return graphicsEffect->boundingRectFor(rect).toAlignedRect(); + return rect; + } + QSize adjustedSize() const; inline void handleSoftwareInputPanel(Qt::MouseButton button, bool clickCausedFocus) @@ -504,6 +512,7 @@ public: QWidgetItemV2 *widgetItem; QPaintEngine *extraPaintEngine; mutable const QMetaObject *polished; + QGraphicsEffect *graphicsEffect; // All widgets are added into the allWidgets set. Once // they receive a window id they are also added to the mapper. // This should just ensure that all widgets are deleted by QApplication @@ -681,6 +690,57 @@ public: }; +struct QWidgetPaintContext +{ + inline QWidgetPaintContext(QPaintDevice *d, const QRegion &r, const QPoint &o, int f, + QPainter *p, QWidgetBackingStore *b) + : pdev(d), rgn(r), offset(o), flags(f), sharedPainter(p), backingStore(b), painter(0) {} + + QPaintDevice *pdev; + QRegion rgn; + QPoint offset; + int flags; + QPainter *sharedPainter; + QWidgetBackingStore *backingStore; + QPainter *painter; +}; + +class QWidgetEffectSourcePrivate : public QGraphicsEffectSourcePrivate +{ +public: + QWidgetEffectSourcePrivate(QWidget *widget) + : QGraphicsEffectSourcePrivate(), m_widget(widget), context(0) + {} + + inline void detach() + { m_widget->setGraphicsEffect(0); } + + inline const QGraphicsItem *graphicsItem() const + { return 0; } + + inline const QWidget *widget() const + { return m_widget; } + + inline void update() + { m_widget->update(); } + + inline bool isPixmap() const + { return false; } + + inline const QStyleOption *styleOption() const + { return 0; } + + inline QRect deviceRect() const + { return m_widget->window()->rect(); } + + QRectF boundingRect(Qt::CoordinateSystem system) const; + void draw(QPainter *p); + QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const; + + QWidget *m_widget; + QWidgetPaintContext *context; +}; + inline QWExtra *QWidgetPrivate::extraData() const { return extra; diff --git a/src/gui/kernel/qwidget_qws.cpp b/src/gui/kernel/qwidget_qws.cpp index 09bffe6..f8ac252 100644 --- a/src/gui/kernel/qwidget_qws.cpp +++ b/src/gui/kernel/qwidget_qws.cpp @@ -258,7 +258,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) Q_D(QWidget); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); if (testAttribute(Qt::WA_WState_Created)) { @@ -317,7 +317,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) Q_Q(QWidget); bool wasCreated = q->testAttribute(Qt::WA_WState_Created); if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); #ifndef QT_NO_CURSOR QCursor oldcurs; bool setcurs=q->testAttribute(Qt::WA_SetCursor); @@ -814,7 +814,7 @@ void QWidgetPrivate::lower_sys() QWSChangeAltitudeCommand::Lower); } else if (QWidget *p = q->parentWidget()) { setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(q->geometry()); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); } } @@ -823,7 +823,7 @@ void QWidgetPrivate::stackUnder_sys(QWidget*) Q_Q(QWidget); if (QWidget *p = q->parentWidget()) { setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(q->geometry()); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); } } diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 4fc37b1..40e3fa2 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -535,7 +535,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); if (testAttribute(Qt::WA_WState_Created)) { setAttribute(Qt::WA_WState_Created, false); @@ -601,7 +601,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) Q_Q(QWidget); bool wasCreated = q->testAttribute(Qt::WA_WState_Created); if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); WId old_winid = data.winid; // hide and reparent our own window away. Otherwise we might get diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index 5e3897b..62aba45 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -971,7 +971,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); if (testAttribute(Qt::WA_WState_Created)) { setAttribute(Qt::WA_WState_Created, false); @@ -1054,7 +1054,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) QTLWExtra *topData = maybeTopData(); bool wasCreated = q->testAttribute(Qt::WA_WState_Created); if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); extern void qPRCreate(const QWidget *, Window); #ifndef QT_NO_CURSOR QCursor oldcurs; diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 5321ce2..0fffaef 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -559,7 +559,8 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up } const QPoint offset = widget->mapTo(tlw, QPoint()); - if (qt_region_strictContains(dirty, widget->rect().translated(offset))) { + const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect()); + if (qt_region_strictContains(dirty, widgetRect.translated(offset))) { if (updateImmediately) sendUpdateRequest(tlw, updateImmediately); return; // Already dirty. @@ -567,7 +568,10 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up if (invalidateBuffer) { const bool eventAlreadyPosted = !dirty.isEmpty(); - dirty += rgn.translated(offset); + if (widget->d_func()->graphicsEffect) + dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset); + else + dirty += rgn.translated(offset); if (!eventAlreadyPosted || updateImmediately) sendUpdateRequest(tlw, updateImmediately); return; @@ -580,8 +584,12 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up } if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, widget->rect())) - widget->d_func()->dirty += rgn; + if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { + if (widget->d_func()->graphicsEffect) + widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()); + else + widget->d_func()->dirty += rgn; + } } else { addDirtyWidget(widget, rgn); } @@ -625,7 +633,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd return; } - const QRect translatedRect(rect.translated(widget->mapTo(tlw, QPoint()))); + const QRect widgetRect = widget->d_func()->effectiveRectFor(rect); + const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint()))); if (qt_region_strictContains(dirty, translatedRect)) { if (updateImmediately) sendUpdateRequest(tlw, updateImmediately); @@ -647,8 +656,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd } if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, rect)) - widget->d_func()->dirty += rect; + if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) + widget->d_func()->dirty += widgetRect; } else { addDirtyWidget(widget, rect); } @@ -884,7 +893,7 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) && !isOverlapped(sourceRect) && !isOverlapped(destRect); if (!accelerateMove) { - QRegion parentR(parentRect); + QRegion parentR(effectiveRectFor(parentRect)); if (!extra || !extra->hasMask) { parentR -= newRect; } else { @@ -1388,7 +1397,7 @@ void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const Q const QRect newWidgetRect(q->rect()); const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height()); - if (!staticContents) { + if (!staticContents || graphicsEffect) { QRegion staticChildren; QWidgetBackingStore *bs = 0; if (offset.isNull() && (bs = maybeBackingStore())) @@ -1408,19 +1417,19 @@ void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const Q return; // Invalidate newly exposed area of the parent. - if (extra && extra->hasMask) { + if (!graphicsEffect && extra && extra->hasMask) { QRegion parentExpose(extra->mask.translated(oldPos)); parentExpose &= QRect(oldPos, oldSize); if (hasStaticChildren) parentExpose -= data.crect; // Offset is unchanged, safe to do this. q->parentWidget()->d_func()->invalidateBuffer(parentExpose); } else { - if (hasStaticChildren) { + if (hasStaticChildren && !graphicsEffect) { QRegion parentExpose(QRect(oldPos, oldSize)); parentExpose -= data.crect; // Offset is unchanged, safe to do this. q->parentWidget()->d_func()->invalidateBuffer(parentExpose); } else { - q->parentWidget()->d_func()->invalidateBuffer(QRect(oldPos, oldSize)); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize))); } } return; @@ -1478,7 +1487,7 @@ void QWidgetPrivate::invalidateBuffer(const QRegion &rgn) QRegion wrgn(rgn); wrgn &= clipRect(); - if (extra && extra->hasMask) + if (!graphicsEffect && extra && extra->hasMask) wrgn &= extra->mask; if (wrgn.isEmpty()) return; @@ -1506,7 +1515,7 @@ void QWidgetPrivate::invalidateBuffer(const QRect &rect) if (wRect.isEmpty()) return; - if (!extra || !extra->hasMask) { + if (graphicsEffect || !extra || !extra->hasMask) { tlwExtra->backingStore->markDirty(wRect, q, false, true); return; } diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h index ddc0a59..b21d504 100644 --- a/src/gui/painting/qbackingstore_p.h +++ b/src/gui/painting/qbackingstore_p.h @@ -144,9 +144,13 @@ private: inline void addDirtyWidget(QWidget *widget, const QRegion &rgn) { if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { - widget->d_func()->dirty = rgn; + QWidgetPrivate *widgetPrivate = widget->d_func(); + if (widgetPrivate->graphicsEffect) + widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); + else + widgetPrivate->dirty = rgn; dirtyWidgets.append(widget); - widget->d_func()->inDirtyList = true; + widgetPrivate->inDirtyList = true; } } diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index f1e83dc..bed2ab3 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -211,6 +211,7 @@ public: Direct3D, Pdf, OpenVG, + OpenGL2, User = 50, // first user type id MaxUser = 100 // last user type id diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h index 7ea68d2..d3cd6f9 100644 --- a/src/gui/painting/qpaintengine_p.h +++ b/src/gui/painting/qpaintengine_p.h @@ -72,6 +72,7 @@ public: QPaintDevice *pdev; QPaintEngine *q_ptr; QRegion systemClip; + QRect systemRect; QRegion systemViewport; QTransform systemTransform; QWidget *currentClipWidget; @@ -117,9 +118,6 @@ public: virtual void systemStateChanged() { } void drawBoxTextItem(const QPointF &p, const QTextItemInt &ti); - -private: - QRect systemRect; }; QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp new file mode 100644 index 0000000..a82caa0 --- /dev/null +++ b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcustomshaderstage_p.h" +#include "qglengineshadermanager_p.h" +#include "qpaintengineex_opengl2_p.h" +#include <private/qpainter_p.h> + +class QGLCustomShaderStagePrivate +{ +public: + QGLCustomShaderStagePrivate() : + m_manager(0) {} + + QGLEngineShaderManager* m_manager; + QByteArray m_source; +}; + + + + +QGLCustomShaderStage::QGLCustomShaderStage() + : d_ptr(new QGLCustomShaderStagePrivate) +{ +} + +QGLCustomShaderStage::~QGLCustomShaderStage() +{ + Q_D(QGLCustomShaderStage); + if (d->m_manager) + d->m_manager->removeCustomStage(this); +} + +void QGLCustomShaderStage::setUniformsDirty() +{ + Q_D(QGLCustomShaderStage); + if (d->m_manager) + d->m_manager->setDirty(); // ### Probably a bit overkill! +} + +bool QGLCustomShaderStage::setOnPainter(QPainter* p) +{ + Q_D(QGLCustomShaderStage); + if (p->paintEngine()->type() != QPaintEngine::OpenGL2) { + qWarning("QGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2"); + return false; + } + + // Might as well go through the paint engine to get to the context + const QGLContext* ctx = static_cast<QGL2PaintEngineEx*>(p->paintEngine())->context(); + d->m_manager = QGLEngineShaderManager::managerForContext(ctx); + Q_ASSERT(d->m_manager); + + d->m_manager->setCustomStage(this); + return true; +} + +void QGLCustomShaderStage::removeFromPainter(QPainter* p) +{ + Q_D(QGLCustomShaderStage); + if (p->paintEngine()->type() != QPaintEngine::OpenGL2) + return; + + // Might as well go through the paint engine to get to the context + const QGLContext* ctx = static_cast<QGL2PaintEngineEx*>(p->paintEngine())->context(); + d->m_manager = QGLEngineShaderManager::managerForContext(ctx); + Q_ASSERT(d->m_manager); + + // Just set the stage to null, don't call removeCustomStage(). + // 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); +} + +const char* QGLCustomShaderStage::source() const +{ + Q_D(const QGLCustomShaderStage); + return d->m_source.constData(); +} + +void QGLCustomShaderStage::setSource(const QByteArray& s) +{ + Q_D(QGLCustomShaderStage); + d->m_source = s; +} diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h new file mode 100644 index 0000000..70e9ff0 --- /dev/null +++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QGL_CUSTOM_SHADER_STAGE_H +#define QGL_CUSTOM_SHADER_STAGE_H + +#include <QGLShaderProgram> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenGL) + +class QGLCustomShaderStagePrivate; +class Q_OPENGL_EXPORT QGLCustomShaderStage +{ + Q_DECLARE_PRIVATE(QGLCustomShaderStage); +public: + QGLCustomShaderStage(); + virtual ~QGLCustomShaderStage(); + virtual void setUniforms(QGLShaderProgram*) = 0; + + void setUniformsDirty(); + + bool setOnPainter(QPainter*); + void removeFromPainter(QPainter*); + const char* source() const; + +protected: + void setSource(const QByteArray&); + +private: + QGLCustomShaderStagePrivate* d_ptr; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 42b36a2..795c8b0 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -86,6 +86,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) maskType(NoMask), useTextureCoords(false), compositionMode(QPainter::CompositionMode_SourceOver), + customSrcStage(0), blitShaderProg(0), simpleShaderProg(0), currentShaderProg(0) @@ -131,6 +132,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader; code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader; code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader; + code[CustomImageSrcFragmentShader] = ""; // Supplied by app. code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader; code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader; code[TextureBrushSrcWithPatternFragmentShader] = qglslTextureBrushSrcWithPatternFragmentShader; @@ -159,7 +161,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(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) { + if (i != CustomImageSrcFragmentShader && qglEngineShaderSourceCode[i] == 0) { int enumIndex = staticMetaObject.indexOfEnumerator("ShaderName"); QMetaEnum m = staticMetaObject.enumerator(enumIndex); @@ -308,6 +310,44 @@ void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode) shaderProgNeedsChanging = true; //### } +void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage) +{ + // If the custom shader has changed, then destroy the previous compilation. + if (customSrcStage && stage && customSrcStage != stage) + removeCustomStage(customSrcStage); + + customSrcStage = stage; + shaderProgNeedsChanging = true; +} + +void QGLEngineShaderManager::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--); + } + } + + shaderProgNeedsChanging = true; +} + +void QGLEngineShaderManager::removeCustomStage(QGLCustomShaderStage* stage) +{ + Q_UNUSED(stage); // Currently we only support one at a time... + + QGLShader *compiledShader = compiledShaders[CustomImageSrcFragmentShader]; + + if (!compiledShader) + return; + + compiledShaders[CustomImageSrcFragmentShader] = 0; + customSrcStage = 0; + shaderProgNeedsChanging = true; +} + + QGLShaderProgram* QGLEngineShaderManager::currentProgram() { return currentShaderProg->program; @@ -332,6 +372,12 @@ bool QGLEngineShaderManager::useCorrectShaderProg() if (!shaderProgNeedsChanging) return false; + bool useCustomSrc = customSrcStage != 0; + if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc) { + useCustomSrc = false; + qWarning("QGLEngineShaderManager - Ignoring custom shader stage for non image src"); + } + QGLEngineShaderProg requiredProgram; requiredProgram.program = 0; @@ -363,7 +409,11 @@ bool QGLEngineShaderManager::useCorrectShaderProg() qCritical("QGLEngineShaderManager::useCorrectShaderProg() - I'm scared, Qt::NoBrush style is set"); break; case QGLEngineShaderManager::ImageSrc: - srcPixelFragShaderName = ImageSrcFragmentShader; + srcPixelFragShaderName = useCustomSrc ? CustomImageSrcFragmentShader : ImageSrcFragmentShader; + positionVertexShaderName = PositionOnlyVertexShader; + break; + case QGLEngineShaderManager::NonPremultipliedImageSrc: + srcPixelFragShaderName = NonPremultipliedImageSrcFragmentShader; positionVertexShaderName = PositionOnlyVertexShader; break; case QGLEngineShaderManager::PatternSrc: @@ -375,10 +425,6 @@ bool QGLEngineShaderManager::useCorrectShaderProg() positionVertexShaderName = isAffine ? AffinePositionWithTextureBrushVertexShader : PositionWithTextureBrushVertexShader; break; - case QGLEngineShaderManager::NonPremultipliedImageSrc: - srcPixelFragShaderName = NonPremultipliedImageSrcFragmentShader; - positionVertexShaderName = PositionOnlyVertexShader; - break; case Qt::SolidPattern: srcPixelFragShaderName = SolidBrushSrcFragmentShader; positionVertexShaderName = PositionOnlyVertexShader; @@ -409,7 +455,6 @@ bool QGLEngineShaderManager::useCorrectShaderProg() requiredProgram.positionVertexShader = compiledShaders[positionVertexShaderName]; requiredProgram.srcPixelFragShader = compiledShaders[srcPixelFragShaderName]; - const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; const bool hasMask = maskType != QGLEngineShaderManager::NoMask; @@ -498,62 +543,63 @@ bool QGLEngineShaderManager::useCorrectShaderProg() else requiredProgram.compositionFragShader = 0; - // At this point, requiredProgram is fully populated so try to find the program in the cache + bool foundProgramInCache = false; for (int i = 0; i < cachedPrograms.size(); ++i) { - QGLEngineShaderProg &prog = cachedPrograms[i]; - if ( (prog.mainVertexShader == requiredProgram.mainVertexShader) - && (prog.positionVertexShader == requiredProgram.positionVertexShader) - && (prog.mainFragShader == requiredProgram.mainFragShader) - && (prog.srcPixelFragShader == requiredProgram.srcPixelFragShader) - && (prog.compositionFragShader == requiredProgram.compositionFragShader) ) - { - currentShaderProg = &prog; - currentShaderProg->program->enable(); - shaderProgNeedsChanging = false; - return true; + if (cachedPrograms[i] == requiredProgram) { + currentShaderProg = &cachedPrograms[i]; + foundProgramInCache = true; + break; } } - // Shader program not found in cache, create it now. - requiredProgram.program = new QGLShaderProgram(ctx, this); - requiredProgram.program->addShader(requiredProgram.mainVertexShader); - requiredProgram.program->addShader(requiredProgram.positionVertexShader); - requiredProgram.program->addShader(requiredProgram.mainFragShader); - requiredProgram.program->addShader(requiredProgram.srcPixelFragShader); - requiredProgram.program->addShader(requiredProgram.maskFragShader); - requiredProgram.program->addShader(requiredProgram.compositionFragShader); - - // We have to bind the vertex attribute names before the program is linked: - requiredProgram.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); - if (useTextureCoords) - requiredProgram.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); - - requiredProgram.program->link(); - if (!requiredProgram.program->isLinked()) { - QString error; - qWarning() << "Shader program failed to link," + // If the shader program's not found in the cache, create it now. + if (!foundProgramInCache) { + requiredProgram.program = new QGLShaderProgram(ctx, this); + requiredProgram.program->addShader(requiredProgram.mainVertexShader); + requiredProgram.program->addShader(requiredProgram.positionVertexShader); + requiredProgram.program->addShader(requiredProgram.mainFragShader); + requiredProgram.program->addShader(requiredProgram.srcPixelFragShader); + requiredProgram.program->addShader(requiredProgram.maskFragShader); + requiredProgram.program->addShader(requiredProgram.compositionFragShader); + + // We have to bind the vertex attribute names before the program is linked: + requiredProgram.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + if (useTextureCoords) + requiredProgram.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + + requiredProgram.program->link(); + if (!requiredProgram.program->isLinked()) { + QString error; + qWarning() << "Shader program failed to link," #if defined(QT_DEBUG) - << '\n' - << " Shaders Used:" << '\n' - << " mainVertexShader = " << requiredProgram.mainVertexShader->objectName() << '\n' - << " positionVertexShader = " << requiredProgram.positionVertexShader->objectName() << '\n' - << " mainFragShader = " << requiredProgram.mainFragShader->objectName() << '\n' - << " srcPixelFragShader = " << requiredProgram.srcPixelFragShader->objectName() << '\n' - << " maskFragShader = " << requiredProgram.maskFragShader->objectName() << '\n' - << " compositionFragShader = "<< requiredProgram.compositionFragShader->objectName() << '\n' + << '\n' + << " Shaders Used:" << '\n' + << " mainVertexShader = " << requiredProgram.mainVertexShader->objectName() << '\n' + << " positionVertexShader = " << requiredProgram.positionVertexShader->objectName() << '\n' + << " mainFragShader = " << requiredProgram.mainFragShader->objectName() << '\n' + << " srcPixelFragShader = " << requiredProgram.srcPixelFragShader->objectName() << '\n' + << " maskFragShader = " << requiredProgram.maskFragShader->objectName() << '\n' + << " compositionFragShader = "<< requiredProgram.compositionFragShader->objectName() << '\n' #endif - << " Error Log:" << '\n' - << " " << requiredProgram.program->log(); - qWarning() << error; + << " Error Log:" << '\n' + << " " << requiredProgram.program->log(); + qWarning() << error; + delete requiredProgram.program; + } else { + cachedPrograms.append(requiredProgram); + // taking the address here is safe since + // cachePrograms isn't resized anywhere else + currentShaderProg = &cachedPrograms.last(); + } } - else { - cachedPrograms.append(requiredProgram); - // taking the address here is safe since - // cachePrograms isn't resized anywhere else - currentShaderProg = &cachedPrograms.last(); + + if (currentShaderProg) { currentShaderProg->program->enable(); + if (useCustomSrc) + customSrcStage->setUniforms(currentShaderProg->program); } + shaderProgNeedsChanging = false; return true; } @@ -563,8 +609,27 @@ void QGLEngineShaderManager::compileNamedShader(QGLEngineShaderManager::ShaderNa if (compiledShaders[name]) return; - QGLShader *newShader = new QGLShader(type, ctx, this); - newShader->compile(qglEngineShaderSourceCode[name]); + QGLShader *newShader; + + QByteArray source; + if (name == CustomImageSrcFragmentShader) { + source = customSrcStage->source(); + source += qglslCustomSrcFragmentShader; + + newShader = customShaderCache.object(source); + if (!newShader) { + newShader = new QGLShader(type, ctx, this); + newShader->compile(source); + customShaderCache.insert(source, newShader); + + connect(newShader, SIGNAL(destroyed(QObject *)), + this, SLOT(shaderDestroyed(QObject *))); + } + } else { + source = qglEngineShaderSourceCode[name]; + newShader = new QGLShader(type, ctx, this); + newShader->compile(source); + } #if defined(QT_DEBUG) // Name the shader for easier debugging diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index b314998..1ee75df 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -199,19 +199,23 @@ O = Global Opacity - CUSTOM SHADER CODE (idea, depricated) + CUSTOM SHADER CODE ================== The use of custom shader code is supported by the engine for drawImage and drawPixmap calls. This is implemented via hooks in the fragment pipeline. + The custom shader is passed to the engine as a partial fragment shader - (QGLCustomizedShader). The shader will implement a pre-defined method name - which Qt's fragment pipeline will call. There are two different hooks which - can be implemented as custom shader code: + (QGLCustomShaderStage). The shader will implement a pre-defined method name + which Qt's fragment pipeline will call: + + lowp vec4 customShader(sampler2d src, vec2 srcCoords) - mediump vec4 customShader(sampler2d src, vec2 srcCoords) - mediump vec4 customShaderWithDest(sampler2d dest, sampler2d src, vec2 srcCoords) + The provided src and srcCoords parameters can be used to sample from the + source image. + Transformations, clipping, opacity, and composition modes set using QPainter + will be respected when using the custom shader hook. */ #ifndef QGLENGINE_SHADER_MANAGER_H @@ -221,6 +225,7 @@ #include <QGLShaderProgram> #include <QPainter> #include <private/qgl_p.h> +#include <private/qglcustomshaderstage_p.h> QT_BEGIN_HEADER @@ -228,7 +233,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(OpenGL) - struct QGLEngineShaderProg { QGLShader* mainVertexShader; @@ -240,6 +244,17 @@ struct QGLEngineShaderProg QGLShaderProgram* program; QVector<uint> uniformLocations; + + 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 + ); + } }; /* @@ -260,7 +275,7 @@ struct QGLEngineCachedShaderProg static const GLuint QT_VERTEX_COORDS_ATTR = 0; static const GLuint QT_TEXTURE_COORDS_ATTR = 1; -class QGLEngineShaderManager : public QObject +class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject { Q_OBJECT public: @@ -305,6 +320,8 @@ public: void setUseGlobalOpacity(bool); void setMaskType(MaskType); void setCompositionMode(QPainter::CompositionMode); + void setCustomStage(QGLCustomShaderStage* stage); + void removeCustomStage(QGLCustomShaderStage* stage); uint getUniformLocation(Uniform id); @@ -344,8 +361,10 @@ public: MainFragmentShader, ImageSrcFragmentShader, + CustomSrcFragmentShader, ImageSrcWithPatternFragmentShader, NonPremultipliedImageSrcFragmentShader, + CustomImageSrcFragmentShader, SolidBrushSrcFragmentShader, TextureBrushSrcFragmentShader, TextureBrushSrcWithPatternFragmentShader, @@ -389,6 +408,8 @@ public: Q_ENUMS(ShaderName) #endif +private slots: + void shaderDestroyed(QObject *shader); private: QGLContext* ctx; @@ -401,6 +422,7 @@ private: MaskType maskType; bool useTextureCoords; QPainter::CompositionMode compositionMode; + QGLCustomShaderStage* customSrcStage; QGLShaderProgram* blitShaderProg; QGLShaderProgram* simpleShaderProg; @@ -408,6 +430,7 @@ private: // TODO: Possibly convert to a LUT QList<QGLEngineShaderProg> cachedPrograms; + QCache<QByteArray, QGLShader> customShaderCache; QGLShader* compiledShaders[TotalShaderCount]; diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h index c80d6e1..cf930f3 100644 --- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h @@ -290,6 +290,14 @@ static const char* const qglslImageSrcFragmentShader = "\ return texture2D(imageTexture, textureCoords); \ }"; +static const char* const qglslCustomSrcFragmentShader = "\ + varying highp vec2 textureCoords; \ + uniform sampler2D imageTexture; \ + lowp vec4 customShader(sampler2D texture, vec2 coords); \ + lowp vec4 srcPixel() { \ + return customShader(imageTexture, textureCoords); \ + }"; + static const char* const qglslImageSrcWithPatternFragmentShader = "\ varying highp vec2 textureCoords; \ uniform lowp vec4 patternColor; \ diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index c3f2de1..7a3fa56 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -720,6 +720,12 @@ void QGL2PaintEngineEx::sync() d->needsSync = true; } +const QGLContext *QGL2PaintEngineEx::context() +{ + Q_D(QGL2PaintEngineEx); + return d->ctx; +} + void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) { if (newMode == mode) @@ -1116,6 +1122,21 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel()); } +void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src) +{ + Q_D(QGL2PaintEngineEx); + ensureActive(); + d->transferMode(ImageDrawingMode); + + QGLContext *ctx = d->ctx; + glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_2D, textureId); + + d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, + state()->renderHints & QPainter::SmoothPixmapTransform, textureId); + d->drawTexture(dest, src, size, false); +} + void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem) { Q_D(QGL2PaintEngineEx); @@ -1720,6 +1741,14 @@ QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState() { } +QPixmapFilter *QGL2PaintEngineEx::createPixmapFilter(int type) const +{ + const QGLContext *ctx = QGLContext::currentContext(); + if (ctx) + return ctx->d_func()->createPixmapFilter(type); + return 0; +} + QT_END_NAMESPACE #include "qpaintengineex_opengl2.moc" diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index fa216bc..7b734e3 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -90,8 +90,7 @@ public: bool hasRectangleClip; }; - -class QGL2PaintEngineEx : public QPaintEngineEx +class Q_OPENGL_EXPORT QGL2PaintEngineEx : public QPaintEngineEx { Q_DECLARE_PRIVATE(QGL2PaintEngineEx) public: @@ -118,12 +117,13 @@ public: virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); - virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor); + virtual void drawTexture(const QRectF &r, GLuint textureId, const QSize &size, const QRectF &sr); + virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); - Type type() const { return OpenGL; } + Type type() const { return OpenGL2; } void setState(QPainterState *s); QPainterState *createState(QPainterState *orig) const; @@ -135,10 +135,15 @@ public: } virtual void sync(); + const QGLContext* context(); + + QPixmapFilter *createPixmapFilter(int type) const; + private: Q_DISABLE_COPY(QGL2PaintEngineEx) }; + class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate { Q_DECLARE_PUBLIC(QGL2PaintEngineEx) @@ -147,7 +152,7 @@ public: q(q_ptr), width(0), height(0), ctx(0), - currentBrush( &(q->state()->brush) ), + currentBrush(0), inverseScale(1), shaderManager(0) { } diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 4231721..458aa7e 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -38,6 +38,7 @@ SOURCES += qgl.cpp \ !contains(QT_CONFIG, opengles1):!contains(QT_CONFIG, opengles1cl) { HEADERS += qglshaderprogram.h \ qglpixmapfilter_p.h \ + qgraphicsshadereffect.h \ qgraphicssystem_gl_p.h \ qwindowsurface_gl_p.h \ qpixmapdata_gl_p.h \ @@ -45,17 +46,20 @@ SOURCES += qgl.cpp \ gl2paintengineex/qglengineshadermanager_p.h \ gl2paintengineex/qgl2pexvertexarray_p.h \ gl2paintengineex/qpaintengineex_opengl2_p.h \ - gl2paintengineex/qglengineshadersource_p.h + gl2paintengineex/qglengineshadersource_p.h \ + gl2paintengineex/qglcustomshaderstage_p.h SOURCES += qglshaderprogram.cpp \ qglpixmapfilter.cpp \ + qgraphicsshadereffect.cpp \ qgraphicssystem_gl.cpp \ qwindowsurface_gl.cpp \ qpixmapdata_gl.cpp \ gl2paintengineex/qglgradientcache.cpp \ gl2paintengineex/qglengineshadermanager.cpp \ gl2paintengineex/qgl2pexvertexarray.cpp \ - gl2paintengineex/qpaintengineex_opengl2.cpp + gl2paintengineex/qpaintengineex_opengl2.cpp \ + gl2paintengineex/qglcustomshaderstage.cpp } diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index f8e4226..29f6440 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -40,20 +40,24 @@ ****************************************************************************/ #include "private/qpixmapfilter_p.h" +#include "private/qpixmapdata_gl_p.h" +#include "private/qpaintengineex_opengl2_p.h" +#include "private/qglengineshadermanager_p.h" #include "qglpixmapfilter_p.h" #include "qgraphicssystem_gl_p.h" #include "qpaintengine_opengl_p.h" +#include "qcache.h" -#include "qglpixelbuffer.h" +#include "qglframebufferobject.h" #include "qglshaderprogram.h" #include "qgl_p.h" #include "private/qapplication_p.h" +#include "private/qmath_p.h" QT_BEGIN_NAMESPACE - void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const { const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, true, false); @@ -97,6 +101,27 @@ private: mutable int m_kernelHeight; }; +class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter> +{ +public: + QGLPixmapBlurFilter(); + ~QGLPixmapBlurFilter(); + + void setUniforms(QGLShaderProgram *program); + +protected: + bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; + +private: + static QByteArray generateBlurShader(int radius, bool gaussianBlur); + + mutable QGLShader *m_shader; + + mutable QSize m_textureSize; + + QGLShaderProgram *m_program; +}; + extern QGLWidget *qt_gl_share_widget(); QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const @@ -105,6 +130,8 @@ QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const case QPixmapFilter::ColorizeFilter: return new QGLPixmapColorizeFilter; + case QPixmapFilter::BlurFilter: + return new QGLPixmapBlurFilter; case QPixmapFilter::ConvolutionFilter: return new QGLPixmapConvolutionFilter; @@ -281,4 +308,250 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const return true; } +QGLPixmapBlurFilter::QGLPixmapBlurFilter() +{ +} + +QGLPixmapBlurFilter::~QGLPixmapBlurFilter() +{ +} + +bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const +{ + QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this); + filter->setSource(generateBlurShader(radius(), quality() == Qt::SmoothTransformation)); + + QGLFramebufferObjectFormat format; + format.setInternalFormat(src.hasAlphaChannel() ? GL_RGBA : GL_RGB); + QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format); + + if (!fbo) + return false; + + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(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); + + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); + + engine->syncState(); + painter->save(); + + // ensure GL_LINEAR filtering is used + painter->setRenderHint(QPainter::SmoothPixmapTransform); + + // prepare for updateUniforms + m_textureSize = src.size(); + + // first pass, to fbo + fbo->bind(); + if (src.hasAlphaChannel()) { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + } + + filter->setOnPainter(painter); + + QTransform transform = engine->state()->matrix; + if (!transform.isIdentity()) { + engine->state()->matrix = QTransform(); + engine->transformChanged(); + } + + engine->drawPixmap(src.rect().translated(0, painter->device()->height() - fbo->height()), + src, src.rect()); + + if (!transform.isIdentity()) { + engine->state()->matrix = transform; + engine->transformChanged(); + } + + fbo->release(); + + // second pass, to widget + m_program->setUniformValue("delta", 0.0, 1.0); + engine->drawTexture(src.rect().translated(pos.x(), pos.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height())); + filter->removeFromPainter(painter); + + painter->restore(); + + qgl_fbo_pool()->release(fbo); + + return true; +} + +void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program) +{ + program->setUniformValue("invTextureSize", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height()); + program->setUniformValue("delta", 1.0, 0.0); + + m_program = program; +} + +static inline qreal gaussian(qreal dx, qreal sigma) +{ + return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma); +} + +QByteArray QGLPixmapBlurFilter::generateBlurShader(int radius, bool gaussianBlur) +{ + Q_ASSERT(radius >= 1); + + QByteArray source; + source.reserve(1000); + + source.append("uniform highp vec2 invTextureSize;\n"); + + bool separateXY = true; + bool clip = false; + + if (separateXY) { + source.append("uniform highp vec2 delta;\n"); + + if (clip) + source.append("uniform highp vec2 clip;\n"); + } else if (clip) { + source.append("uniform highp vec4 clip;\n"); + } + + source.append("mediump vec4 customShader(sampler2D src, vec2 srcCoords) {\n"); + + QVector<qreal> sampleOffsets; + QVector<qreal> weights; + + if (gaussianBlur) { + QVector<qreal> gaussianComponents; + + qreal sigma = radius / 1.65; + + qreal sum = 0; + for (int i = -radius; i <= radius; ++i) { + float value = gaussian(i, sigma); + gaussianComponents << value; + sum += value; + } + + // normalize + for (int i = 0; i < gaussianComponents.size(); ++i) + gaussianComponents[i] /= sum; + + for (int i = 0; i < gaussianComponents.size() - 1; i += 2) { + qreal weight = gaussianComponents.at(i) + gaussianComponents.at(i + 1); + qreal offset = i - radius + gaussianComponents.at(i + 1) / weight; + + sampleOffsets << offset; + weights << weight; + } + + // odd size ? + if (gaussianComponents.size() & 1) { + sampleOffsets << radius; + weights << gaussianComponents.last(); + } + } else { + for (int i = 0; i < radius; ++i) { + sampleOffsets << 2 * i - radius + 0.5; + weights << qreal(1); + } + sampleOffsets << radius; + weights << qreal(0.5); + } + + int currentVariable = 1; + source.append(" mediump vec4 sample = vec4(0.0);\n"); + source.append(" mediump vec2 coord;\n"); + + qreal weightSum = 0; + if (separateXY) { + source.append(" mediump float c;\n"); + for (int i = 0; i < sampleOffsets.size(); ++i) { + qreal delta = sampleOffsets.at(i); + + ++currentVariable; + + QByteArray coordinate = "srcCoords"; + if (delta != qreal(0)) { + coordinate.append(" + invTextureSize * delta * float("); + coordinate.append(QByteArray::number(delta)); + coordinate.append(")"); + } + + source.append(" coord = "); + source.append(coordinate); + source.append(";\n"); + + if (clip) { + source.append(" c = dot(coord, delta);\n"); + source.append(" if (c > clip.x && c < clip.y)\n "); + } + + source.append(" sample += texture2D(src, coord)"); + + weightSum += weights.at(i); + if (weights.at(i) != qreal(1)) { + source.append(" * float("); + source.append(QByteArray::number(weights.at(i))); + source.append(");\n"); + } else { + source.append(";\n"); + } + } + } else { + for (int y = 0; y < sampleOffsets.size(); ++y) { + for (int x = 0; x < sampleOffsets.size(); ++x) { + QByteArray coordinate = "srcCoords"; + + qreal dx = sampleOffsets.at(x); + qreal dy = sampleOffsets.at(y); + + if (dx != qreal(0) || dy != qreal(0)) { + coordinate.append(" + invTextureSize * vec2(float("); + coordinate.append(QByteArray::number(dx)); + coordinate.append("), float("); + coordinate.append(QByteArray::number(dy)); + coordinate.append("))"); + } + + source.append(" coord = "); + source.append(coordinate); + source.append(";\n"); + + if (clip) + source.append(" if (coord.x > clip.x && coord.x < clip.y && coord.y > clip.z && coord.y < clip.w)\n "); + + source.append(" sample += texture2D(src, coord)"); + + ++currentVariable; + + weightSum += weights.at(x) * weights.at(y); + if ((weights.at(x) != qreal(1) || weights.at(y) != qreal(1))) { + source.append(" * float("); + source.append(QByteArray::number(weights.at(x) * weights.at(y))); + source.append(");\n"); + } else { + source.append(";\n"); + } + } + } + } + + source.append(" return "); + if (!gaussianBlur) { + source.append("float("); + if (separateXY) + source.append(QByteArray::number(1 / weightSum)); + else + source.append(QByteArray::number(1 / weightSum)); + source.append(") * "); + } + source.append("sample;\n"); + source.append("}\n"); + + return source; +} + QT_END_NAMESPACE diff --git a/src/opengl/qgraphicsshadereffect.cpp b/src/opengl/qgraphicsshadereffect.cpp new file mode 100644 index 0000000..e8741b5 --- /dev/null +++ b/src/opengl/qgraphicsshadereffect.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicsshadereffect.h" +#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL) +#include "qglshaderprogram.h" +#include "gl2paintengineex/qglcustomshaderstage_p.h" +#define QGL_HAVE_CUSTOM_SHADERS 1 +#endif +#include <QtGui/qpainter.h> +#include <QtGui/qgraphicsitem.h> +#include <QtGui/private/qgraphicseffect_p.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +/*! + \class QGraphicsShaderEffect + \brief The QGraphicsShaderEffect class is the base class for creating + custom GLSL shader effects in a QGraphicsScene. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + + The specific effect is defined by a fragment of GLSL source code + supplied to setPixelShaderFragment(). This source code must define a + function called \c{srcPixel()} that returns the source pixel value + to use in the paint engine's shader program. The shader fragment + is linked with the regular shader code used by the GL2 paint engine + to construct a complete QGLShaderProgram. + + The following example shader converts the incoming pixmap to + grayscale and then applies a colorize operation using the + \c effectColor value: + + \code + static char const colorizeShaderCode[] = + "varying highp vec2 textureCoords;\n" + "uniform sampler2D imageTexture;\n" + "uniform lowp vec4 effectColor;\n" + "lowp vec4 srcPixel() {\n" + " vec4 src = texture2D(imageTexture, textureCoords);\n" + " float gray = dot(src.rgb, vec3(0.212671, 0.715160, 0.072169));\n" + " vec4 colorize = 1.0-((1.0-gray)*(1.0-effectColor));\n" + " return vec4(colorize.rgb, src.a);\n" + "}"; + \endcode + + To use this shader code, it is necessary to define a subclass + of QGraphicsShaderEffect as follows: + + \code + class ColorizeEffect : public QGraphicsShaderEffect + { + Q_OBJECT + public: + ColorizeEffect(QObject *parent = 0) + : QGraphicsShaderEffect(parent), color(Qt::black) + { + setPixelShaderFragment(colorizeShaderCode); + } + + QColor effectColor() const { return color; } + void setEffectColor(const QColor& c) + { + color = c; + setUniformsDirty(); + } + + protected: + void setUniforms(QGLShaderProgram *program) + { + program->setUniformValue("effectColor", color); + } + + private: + QColor color; + }; + \endcode + + The setUniforms() function is called when the effect is about + to be used for drawing to give the subclass the opportunity to + set effect-specific uniform variables. + + QGraphicsShaderEffect is only supported when the GL2 paint engine + is in use. When any other paint engine is in use (GL1, raster, etc), + the drawItem() method will draw its item argument directly with + no effect applied. + + \sa QGrapicsEffect +*/ + +static const char qglslDefaultImageFragmentShader[] = "\ + varying highp vec2 textureCoords; \ + uniform sampler2D imageTexture; \ + lowp vec4 srcPixel() { \ + return texture2D(imageTexture, textureCoords); \ + }\n"; + +#ifdef QGL_HAVE_CUSTOM_SHADERS + +class QGLCustomShaderEffectStage : public QGLCustomShaderStage +{ +public: + QGLCustomShaderEffectStage + (QGraphicsShaderEffect *e, const QByteArray& source) + : QGLCustomShaderStage(), + effect(e) + { + setSource(source); + } + + void setUniforms(QGLShaderProgram *program); + + QGraphicsShaderEffect *effect; +}; + +void QGLCustomShaderEffectStage::setUniforms(QGLShaderProgram *program) +{ + effect->setUniforms(program); +} + +#endif + +class QGraphicsShaderEffectPrivate : public QGraphicsEffectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsShaderEffect) +public: + QGraphicsShaderEffectPrivate() + : pixelShaderFragment(qglslDefaultImageFragmentShader) +#ifdef QGL_HAVE_CUSTOM_SHADERS + , customShaderStage(0) +#endif + { + } + + QByteArray pixelShaderFragment; +#ifdef QGL_HAVE_CUSTOM_SHADERS + QGLCustomShaderEffectStage *customShaderStage; +#endif +}; + +/*! + Constructs a shader effect and attaches it to \a parent. +*/ +QGraphicsShaderEffect::QGraphicsShaderEffect(QObject *parent) + : QGraphicsEffect(*new QGraphicsShaderEffectPrivate(), parent) +{ +} + +/*! + Destroys this shader effect. +*/ +QGraphicsShaderEffect::~QGraphicsShaderEffect() +{ +#ifdef QGL_HAVE_CUSTOM_SHADERS + Q_D(QGraphicsShaderEffect); + delete d->customShaderStage; +#endif +} + +/*! + Returns the source code for the pixel shader fragment for + this shader effect. The default is a shader that copies + its incoming pixmap directly to the output with no effect + applied. + + \sa setPixelShaderFragment() +*/ +QByteArray QGraphicsShaderEffect::pixelShaderFragment() const +{ + Q_D(const QGraphicsShaderEffect); + return d->pixelShaderFragment; +} + +/*! + Sets the source code for the pixel shader fragment for + this shader effect to \a code. + + The \a code must define a GLSL function called \c{srcPixel()} + that returns the source pixel value to use in the paint engine's + shader program. The following is the default pixel shader fragment, + which draws a pixmap with no effect applied: + + \code + varying highp vec2 textureCoords; + uniform sampler2D imageTexture; + lowp vec4 srcPixel() { + return texture2D(imageTexture, textureCoords); + } + \endcode + + \sa pixelShaderFragment(), setUniforms() +*/ +void QGraphicsShaderEffect::setPixelShaderFragment(const QByteArray& code) +{ + Q_D(QGraphicsShaderEffect); + if (d->pixelShaderFragment != code) { + d->pixelShaderFragment = code; +#ifdef QGL_HAVE_CUSTOM_SHADERS + delete d->customShaderStage; + d->customShaderStage = 0; +#endif + } +} + +/*! + \reimp +*/ +void QGraphicsShaderEffect::draw(QPainter *painter, QGraphicsEffectSource *source) +{ + Q_D(QGraphicsShaderEffect); + +#ifdef QGL_HAVE_CUSTOM_SHADERS + // Set the custom shader on the paint engine. The setOnPainter() + // call may fail if the paint engine is not GL2. In that case, + // we fall through to drawing the pixmap normally. + if (!d->customShaderStage) { + d->customShaderStage = new QGLCustomShaderEffectStage + (this, d->pixelShaderFragment); + } + bool usingShader = d->customShaderStage->setOnPainter(painter); + + QPoint offset; + if (source->isPixmap()) { + // No point in drawing in device coordinates (pixmap will be scaled anyways). + const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset); + painter->drawPixmap(offset, pixmap); + } else { + // Draw pixmap in device coordinates to avoid pixmap scaling. + const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset); + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + painter->drawPixmap(offset, pixmap); + painter->setWorldTransform(restoreTransform); + } + + // Remove the custom shader to return to normal painting operations. + if (usingShader) + d->customShaderStage->removeFromPainter(painter); +#else + source->draw(painter); +#endif +} + +/*! + Sets the custom uniform variables on this shader effect to + be dirty. The setUniforms() function will be called the next + time the shader program corresponding to this effect is used. + + This function is typically called by subclasses when an + effect-specific parameter is changed by the application. + + \sa setUniforms() +*/ +void QGraphicsShaderEffect::setUniformsDirty() +{ +#ifdef QGL_HAVE_CUSTOM_SHADERS + Q_D(QGraphicsShaderEffect); + if (d->customShaderStage) + d->customShaderStage->setUniformsDirty(); +#endif +} + +/*! + Sets custom uniform variables on the current GL context when + \a program is about to be used by the paint engine. + + This function should be overridden if the shader set with + setPixelShaderFragment() has additional parameters beyond + those that the paint engine normally sets itself. + + \sa setUniformsDirty() +*/ +void QGraphicsShaderEffect::setUniforms(QGLShaderProgram *program) +{ + Q_UNUSED(program); +} + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE diff --git a/src/opengl/qgraphicsshadereffect.h b/src/opengl/qgraphicsshadereffect.h new file mode 100644 index 0000000..1612431 --- /dev/null +++ b/src/opengl/qgraphicsshadereffect.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSHADEREFFECT_H +#define QGRAPHICSSHADEREFFECT_H + +#include <QtGui/qgraphicseffect.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(OpenGL) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGLShaderProgram; +class QGLCustomShaderEffectStage; +class QGraphicsShaderEffectPrivate; + +class Q_OPENGL_EXPORT QGraphicsShaderEffect : public QGraphicsEffect +{ + Q_OBJECT +public: + QGraphicsShaderEffect(QObject *parent = 0); + virtual ~QGraphicsShaderEffect(); + + QByteArray pixelShaderFragment() const; + void setPixelShaderFragment(const QByteArray& code); + +protected: + void draw(QPainter *painter, QGraphicsEffectSource *source); + void setUniformsDirty(); + virtual void setUniforms(QGLShaderProgram *program); + +private: + Q_DECLARE_PRIVATE(QGraphicsShaderEffect) + Q_DISABLE_COPY(QGraphicsShaderEffect) + + friend class QGLCustomShaderEffectStage; +}; + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSSHADEREFFECT_H diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index 8de1aae..a441a23 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -55,6 +55,85 @@ QT_BEGIN_NAMESPACE extern QGLWidget* qt_gl_share_widget(); +/*! + \class QGLFramebufferObjectPool + \since 4.6 + + \brief The QGLFramebufferObject class provides a pool of framebuffer + objects for offscreen rendering purposes. + + When requesting an FBO of a given size and format, an FBO of the same + format and a size at least as big as the requested size will be returned. + + \internal +*/ + +static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo) +{ + return qAbs(size.width() * size.height() - fbo->width() * fbo->height()); +} + +QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat) +{ + QGLFramebufferObject *chosen = 0; + QGLFramebufferObject *candidate = 0; + for (int i = 0; !chosen && i < m_fbos.size(); ++i) { + QGLFramebufferObject *fbo = m_fbos.at(i); + + QGLFramebufferObjectFormat format = fbo->format(); + if (format.samples() == requestFormat.samples() + && format.attachment() == requestFormat.attachment() + && format.textureTarget() == requestFormat.textureTarget() + && format.internalFormat() == requestFormat.internalFormat()) + { + // choose the fbo with a matching format and the closest size + if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo)) + candidate = fbo; + } + + if (candidate) { + m_fbos.removeOne(candidate); + + const QSize fboSize = candidate->size(); + QSize sz = fboSize; + + if (sz.width() < requestSize.width()) + sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5))); + if (sz.height() < requestSize.height()) + sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5))); + + // wasting too much space? + if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 2.5) + sz = requestSize; + + if (sz != fboSize) { + delete candidate; + qDebug() << "Resizing fbo in pool:" << sz; + candidate = new QGLFramebufferObject(sz, requestFormat); + } + + chosen = candidate; + } + } + + if (!chosen) { + qDebug() << "Creating new fbo in pool:" << requestSize; + chosen = new QGLFramebufferObject(requestSize, requestFormat); + } + + if (!chosen->isValid()) { + delete chosen; + chosen = 0; + } + + return chosen; +} + +void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo) +{ + m_fbos << fbo; +} + class QGLShareContextScope { public: @@ -112,6 +191,8 @@ QGLPixmapData::~QGLPixmapData() if (!shareWidget) return; + delete m_engine; + if (m_texture.id) { QGLShareContextScope ctx(shareWidget->context()); glDeleteTextures(1, &m_texture.id); @@ -332,8 +413,11 @@ struct TextureBuffer QGL2PaintEngineEx *engine; }; -static QVector<TextureBuffer> textureBufferStack; -static int currentTextureBuffer = 0; +Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool) +QGLFramebufferObjectPool* qgl_fbo_pool() +{ + return _qgl_fbo_pool(); +} void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const { @@ -380,10 +464,9 @@ void QGLPixmapData::swapBuffers() copyBackFromRenderFbo(false); m_renderFbo->release(); - --currentTextureBuffer; + qgl_fbo_pool()->release(m_renderFbo); m_renderFbo = 0; - m_engine = 0; } void QGLPixmapData::makeCurrent() @@ -398,19 +481,6 @@ void QGLPixmapData::doneCurrent() m_renderFbo->release(); } -static TextureBuffer createTextureBuffer(const QSize &size, QGL2PaintEngineEx *engine = 0) -{ - TextureBuffer buffer; - QGLFramebufferObjectFormat fmt; - fmt.setAttachment(QGLFramebufferObject::CombinedDepthStencil); - fmt.setSamples(4); - - buffer.fbo = new QGLFramebufferObject(size, fmt); - buffer.engine = engine ? engine : new QGL2PaintEngineEx; - - return buffer; -} - bool QGLPixmapData::useFramebufferObjects() { return QGLFramebufferObject::hasOpenGLFramebufferObjects() @@ -423,7 +493,7 @@ QPaintEngine* QGLPixmapData::paintEngine() const if (!isValid()) return 0; - if (m_engine) + if (m_renderFbo) return m_engine; if (useFramebufferObjects()) { @@ -433,33 +503,16 @@ QPaintEngine* QGLPixmapData::paintEngine() const qt_gl_share_widget()->makeCurrent(); QGLShareContextScope ctx(qt_gl_share_widget()->context()); - if (textureBufferStack.size() <= currentTextureBuffer) { - textureBufferStack << createTextureBuffer(size()); - } else { - QSize sz = textureBufferStack.at(currentTextureBuffer).fbo->size(); - if (sz.width() < w || sz.height() < h) { - if (sz.width() < w) - sz.setWidth(qMax(w, qRound(sz.width() * 1.5))); - if (sz.height() < h) - sz.setHeight(qMax(h, qRound(sz.height() * 1.5))); - - // wasting too much space? - if (sz.width() * sz.height() > w * h * 2.5) - sz = QSize(w, h); - - delete textureBufferStack.at(currentTextureBuffer).fbo; - textureBufferStack[currentTextureBuffer] = - createTextureBuffer(sz, textureBufferStack.at(currentTextureBuffer).engine); - qDebug() << "Creating new pixmap texture buffer:" << sz; - } - } - - if (textureBufferStack.at(currentTextureBuffer).fbo->isValid()) { - m_renderFbo = textureBufferStack.at(currentTextureBuffer).fbo; - m_engine = textureBufferStack.at(currentTextureBuffer).engine; + QGLFramebufferObjectFormat format; + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format.setSamples(4); + format.setInternalFormat(m_hasAlpha ? GL_RGBA : GL_RGB); - ++currentTextureBuffer; + m_renderFbo = qgl_fbo_pool()->acquire(size(), format); + if (m_renderFbo) { + if (!m_engine) + m_engine = new QGL2PaintEngineEx; return m_engine; } diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index 7a5e197..236dcae 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -62,6 +62,19 @@ QT_BEGIN_NAMESPACE class QPaintEngine; class QGLFramebufferObject; +class QGLFramebufferObjectFormat; + +class QGLFramebufferObjectPool +{ +public: + QGLFramebufferObject *acquire(const QSize &size, const QGLFramebufferObjectFormat &format); + void release(QGLFramebufferObject *fbo); + +private: + QList<QGLFramebufferObject *> m_fbos; +}; + +QGLFramebufferObjectPool* qgl_fbo_pool(); class QGLPixmapData : public QPixmapData { |