summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/corelib/global/qnamespace.h5
-rw-r--r--src/gui/graphicsview/graphicsview.pri6
-rw-r--r--src/gui/graphicsview/qgraphicseffect.cpp876
-rw-r--r--src/gui/graphicsview/qgraphicseffect.h291
-rw-r--r--src/gui/graphicsview/qgraphicseffect_p.h195
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp284
-rw-r--r--src/gui/graphicsview/qgraphicsitem.h8
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h77
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp73
-rw-r--r--src/gui/graphicsview/qgraphicsscene.h2
-rw-r--r--src/gui/graphicsview/qgraphicsscene_p.h25
-rw-r--r--src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp4
-rw-r--r--src/gui/image/qpixmapfilter.cpp225
-rw-r--r--src/gui/image/qpixmapfilter_p.h25
-rw-r--r--src/gui/painting/qpaintengine.h1
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage.cpp123
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage_p.h92
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp175
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h39
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h8
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp29
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h15
-rw-r--r--src/opengl/opengl.pro8
-rw-r--r--src/opengl/qglpixmapfilter.cpp277
-rw-r--r--src/opengl/qgraphicsshadereffect.cpp321
-rw-r--r--src/opengl/qgraphicsshadereffect.h87
-rw-r--r--src/opengl/qpixmapdata_gl.cpp139
-rw-r--r--src/opengl/qpixmapdata_gl_p.h13
28 files changed, 3267 insertions, 156 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index f172d77..1366c08 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1567,6 +1567,11 @@ public:
Uninitialized
};
+ enum CoordinateSystem {
+ DeviceCoordinates,
+ LogicalCoordinates
+ };
+
enum TouchPointState {
TouchPointPressed = 0x01,
TouchPointMoved = 0x02,
diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri
index 9d232e4..a3095e7 100644
--- a/src/gui/graphicsview/graphicsview.pri
+++ b/src/gui/graphicsview/graphicsview.pri
@@ -22,7 +22,10 @@ HEADERS += graphicsview/qgraphicsgridlayout.h \
graphicsview/qgraphicsview_p.h \
graphicsview/qgraphicswidget.h \
graphicsview/qgraphicswidget_p.h \
+ graphicsview/qgraphicseffect.h \
+ graphicsview/qgraphicseffect_p.h \
graphicsview/qgridlayoutengine_p.h
+
SOURCES += graphicsview/qgraphicsgridlayout.cpp \
graphicsview/qgraphicsitem.cpp \
graphicsview/qgraphicsitemanimation.cpp \
@@ -41,4 +44,5 @@ SOURCES += graphicsview/qgraphicsgridlayout.cpp \
graphicsview/qgraphicsview.cpp \
graphicsview/qgraphicswidget.cpp \
graphicsview/qgraphicswidget_p.cpp \
- graphicsview/qgridlayoutengine.cpp
+ graphicsview/qgridlayoutengine.cpp \
+ graphicsview/qgraphicseffect.cpp
diff --git a/src/gui/graphicsview/qgraphicseffect.cpp b/src/gui/graphicsview/qgraphicseffect.cpp
new file mode 100644
index 0000000..b04af7a
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicseffect.cpp
@@ -0,0 +1,876 @@
+/****************************************************************************
+**
+** 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 QGraphicsBloomEffect - applies a blooming / glowing effect
+ \o QGraphicsFrameEffect - adds a frame to the item
+ \o QGraphicsShadowEffect - 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"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <QtGui/qimage.h>
+#include <QtGui/qpainter.h>
+#include <QtCore/qrect.h>
+
+/*
+
+ List of known drawbacks which are being discussed:
+
+ * No d-pointer yet.
+
+ * No auto test yet.
+
+ * No API documentation yet.
+
+ * The API is far from being finalized.
+
+ * Most of the effect implementation is not efficient,
+ as this is still a proof of concept only.
+
+ * Painting artifacts occasionally occur when e.g. moving
+ an item over another item that has a large effective
+ bounding rect.
+
+ * Item transformation is not taken into account.
+ For example, the effective bounding rect is calculated at
+ item coordinate (fast), but the painting is mostly
+ done at device coordinate.
+
+ * Coordinate mode: item vs device. Most effects make sense only
+ in device coordinate. Should we keep both options open?
+ See also above transformation issue.
+
+ * Right now the pixmap for effect drawing is stored in each item.
+ There can be problems with coordinates, see above.
+
+ * There is a lot of duplication in drawItems() for each effect.
+
+ * Port to use the new layer feature in QGraphicsView.
+ This should solve the above pixmap problem.
+
+ * Frame effect is likely useless. However it is very useful
+ to check than the effective bounding rect is handled properly.
+
+ * Proper exposed region and rect for style option are missing.
+
+ * Pixelize effect is using raster only, because there is no
+ pixmap filter for it. We need to implement the missing pixmap filter.
+
+ * Blur effect is using raster only, with exponential blur algorithm.
+ Perhaps use stack blur (better approximate Gaussian blur) instead?
+ QPixmapConvolutionFilter is too slow for this simple blur effect.
+
+ * Bloom and shadow effect are also raster only. Same reason as above.
+
+ * Make it work with widgets (QGraphicsWidget).
+
+*/
+
+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 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(*new QGraphicsEffectPrivate, 0)
+{
+ // ### parent?
+}
+
+/*!
+ \internal
+*/
+QGraphicsEffect::QGraphicsEffect(QGraphicsEffectPrivate &dd)
+ : QObject(dd, 0)
+{
+}
+
+/*!
+ 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();
+}
+
+/*!
+ 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()
+ : QGraphicsEffect(*new QGraphicsGrayscaleEffectPrivate)
+{
+}
+
+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()
+ : QGraphicsEffect(*new QGraphicsColorizeEffectPrivate)
+{}
+
+QGraphicsColorizeEffect::~QGraphicsColorizeEffect()
+{}
+
+QColor QGraphicsColorizeEffect::color() const
+{
+ Q_D(const QGraphicsColorizeEffect);
+ return d->filter->color();
+}
+
+void QGraphicsColorizeEffect::setColor(const QColor &c)
+{
+ Q_D(QGraphicsColorizeEffect);
+ d->filter->setColor(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()
+ : QGraphicsEffect(*new QGraphicsPixelizeEffectPrivate)
+{
+}
+
+QGraphicsPixelizeEffect::~QGraphicsPixelizeEffect()
+{
+}
+
+int QGraphicsPixelizeEffect::pixelSize() const
+{
+ Q_D(const QGraphicsPixelizeEffect);
+ return d->pixelSize;
+}
+
+void QGraphicsPixelizeEffect::setPixelSize(int size)
+{
+ Q_D(QGraphicsPixelizeEffect);
+ d->pixelSize = 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()
+ : QGraphicsEffect(*new QGraphicsBlurEffectPrivate)
+{
+}
+
+QGraphicsBlurEffect::~QGraphicsBlurEffect()
+{
+}
+
+// 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;
+}
+
+int QGraphicsBlurEffect::blurRadius() const
+{
+ Q_D(const QGraphicsBlurEffect);
+ return d->filter->radius();
+}
+
+void QGraphicsBlurEffect::setBlurRadius(int radius)
+{
+ Q_D(QGraphicsBlurEffect);
+ d->filter->setRadius(radius);
+ updateBoundingRect();
+}
+
+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);
+}
+
+QGraphicsBloomEffect::QGraphicsBloomEffect()
+ : QGraphicsEffect(*new QGraphicsBloomEffectPrivate)
+{
+}
+
+QGraphicsBloomEffect::~QGraphicsBloomEffect()
+{
+}
+
+int QGraphicsBloomEffect::blurRadius() const
+{
+ Q_D(const QGraphicsBloomEffect);
+ return d->blurRadius;
+}
+
+void QGraphicsBloomEffect::setBlurRadius(int radius)
+{
+ Q_D(QGraphicsBloomEffect);
+ d->blurRadius = radius;
+ updateBoundingRect();
+}
+
+qreal QGraphicsBloomEffect::opacity() const
+{
+ Q_D(const QGraphicsBloomEffect);
+ return d->opacity;
+}
+
+void QGraphicsBloomEffect::setOpacity(qreal alpha)
+{
+ Q_D(QGraphicsBloomEffect);
+ d->opacity = alpha;
+}
+
+QRectF QGraphicsBloomEffect::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QGraphicsBloomEffect);
+ const qreal delta = d->blurRadius * 3;
+ return rect.adjusted(-delta, -delta, delta, delta);
+}
+
+// Change brightness (positive integer) of each pixel
+static QImage brightened(const QImage& image, int brightness)
+{
+ int tab[ 256 ];
+ for (int i = 0; i < 256; ++i)
+ tab[i] = qMin(i + brightness, 255);
+
+ QImage img = image.convertToFormat(QImage::Format_ARGB32);
+ for (int y = 0; y < img.height(); y++) {
+ QRgb* line = (QRgb*)(img.scanLine(y));
+ for (int x = 0; x < img.width(); x++) {
+ QRgb c = line[x];
+ line[x] = qRgba(tab[qRed(c)], tab[qGreen(c)], tab[qBlue(c)], qAlpha(c));
+ }
+ }
+
+ return img;
+}
+
+// Composite two QImages using given composition mode and opacity
+static QImage composited(const QImage& img1, const QImage& img2, qreal opacity, QPainter::CompositionMode mode)
+{
+ QImage result = img1.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ QPainter painter(&result);
+ painter.setCompositionMode(mode);
+ painter.setOpacity(opacity);
+ painter.drawImage(0, 0, img2);
+ painter.end();
+ return result;
+}
+
+void QGraphicsBloomEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsBloomEffect);
+ if (d->blurRadius <= 0) {
+ source->draw(painter);
+ return;
+ }
+
+ QPoint offset;
+ const int radius = d->blurRadius;
+
+ if (source->isPixmap()) {
+ // No point in drawing in device coordinates (pixmap will be scaled anyways).
+ const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+
+ // bloom routine
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ QImage overlay = blurred(img, img.rect(), radius);
+ overlay = brightened(overlay, 70);
+ img = composited(img, overlay, d->opacity, QPainter::CompositionMode_Overlay);
+
+ painter->drawImage(offset, img);
+ return;
+ }
+
+ // Draw pixmap in device coordinates to avoid pixmap scaling.
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+
+ // bloom routine
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ QImage overlay = blurred(img, img.rect(), radius);
+ overlay = brightened(overlay, 70);
+ img = composited(img, overlay, d->opacity, QPainter::CompositionMode_Overlay);
+
+ // Draw using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawImage(offset, img);
+ painter->setWorldTransform(restoreTransform);
+}
+
+QGraphicsFrameEffect::QGraphicsFrameEffect()
+ : QGraphicsEffect(*new QGraphicsFrameEffectPrivate)
+{
+}
+
+QGraphicsFrameEffect::~QGraphicsFrameEffect()
+{
+}
+
+QColor QGraphicsFrameEffect::frameColor() const
+{
+ Q_D(const QGraphicsFrameEffect);
+ return d->color;
+}
+
+void QGraphicsFrameEffect::setFrameColor(const QColor &c)
+{
+ Q_D(QGraphicsFrameEffect);
+ d->color = c;
+}
+
+qreal QGraphicsFrameEffect::frameWidth() const
+{
+ Q_D(const QGraphicsFrameEffect);
+ return d->width;
+}
+
+void QGraphicsFrameEffect::setFrameWidth(qreal frameWidth)
+{
+ Q_D(QGraphicsFrameEffect);
+ d->width = frameWidth;
+ updateBoundingRect();
+}
+
+qreal QGraphicsFrameEffect::frameOpacity() const
+{
+ Q_D(const QGraphicsFrameEffect);
+ return d->alpha;
+}
+
+void QGraphicsFrameEffect::setFrameOpacity(qreal opacity)
+{
+ Q_D(QGraphicsFrameEffect);
+ d->alpha = opacity;
+}
+
+QRectF QGraphicsFrameEffect::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QGraphicsFrameEffect);
+ return rect.adjusted(-d->width, -d->width, d->width, d->width);
+}
+
+void QGraphicsFrameEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsFrameEffect);
+ painter->save();
+ painter->setOpacity(painter->opacity() * d->alpha);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(d->color);
+ painter->drawRoundedRect(boundingRect(), 20, 20, Qt::RelativeSize);
+ painter->restore();
+
+ source->draw(painter);
+}
+
+QGraphicsShadowEffect::QGraphicsShadowEffect()
+ : QGraphicsEffect(*new QGraphicsShadowEffectPrivate)
+{
+}
+
+QGraphicsShadowEffect::~QGraphicsShadowEffect()
+{
+}
+
+QPointF QGraphicsShadowEffect::shadowOffset() const
+{
+ Q_D(const QGraphicsShadowEffect);
+ return d->offset;
+}
+
+void QGraphicsShadowEffect::setShadowOffset(const QPointF &ofs)
+{
+ Q_D(QGraphicsShadowEffect);
+ d->offset = ofs;
+ updateBoundingRect();
+}
+
+int QGraphicsShadowEffect::blurRadius() const
+{
+ Q_D(const QGraphicsShadowEffect);
+ return d->radius;
+}
+
+void QGraphicsShadowEffect::setBlurRadius(int blurRadius)
+{
+ Q_D(QGraphicsShadowEffect);
+ d->radius = blurRadius;
+ updateBoundingRect();
+}
+
+qreal QGraphicsShadowEffect::opacity() const
+{
+ Q_D(const QGraphicsShadowEffect);
+ return d->alpha;
+}
+
+void QGraphicsShadowEffect::setOpacity(qreal opacity)
+{
+ Q_D(QGraphicsShadowEffect);
+ d->alpha = opacity;
+}
+
+QRectF QGraphicsShadowEffect::boundingRectFor(const QRectF &rect) const
+{
+ Q_D(const QGraphicsShadowEffect);
+ QRectF shadowRect = rect.translated(d->offset);
+ QRectF blurRect = shadowRect;
+ qreal delta = d->radius * 3;
+ blurRect.adjust(-delta, -delta, delta, delta);
+ blurRect |= rect;
+ return blurRect;
+}
+
+void QGraphicsShadowEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
+{
+ Q_D(QGraphicsShadowEffect);
+ if (d->radius <= 0 && d->offset.isNull()) {
+ source->draw(painter);
+ return;
+ }
+
+ const QTransform &transform = painter->worldTransform();
+ const QPointF offset(d->offset.x() * transform.m11(), d->offset.y() * transform.m22());
+ const QPoint shadowOffset = offset.toPoint();
+ const QRectF sourceRect = source->boundingRect(Qt::DeviceCoordinates);
+ const QRectF shadowRect = sourceRect.translated(offset);
+
+ QRectF blurRect = shadowRect;
+ qreal delta = d->radius * 3;
+ blurRect.adjust(-delta, -delta, delta, delta);
+ blurRect |= sourceRect;
+
+ QRect effectRect = blurRect.toAlignedRect();
+ const QRect deviceRect = source->deviceRect();
+ const bool fullyInsideDeviceRect = effectRect.x() >= deviceRect.x()
+ && effectRect.right() <= deviceRect.right()
+ && effectRect.y() >= deviceRect.y()
+ && effectRect.bottom() <= deviceRect.bottom();
+ if (!fullyInsideDeviceRect) {
+ // Clip to device rect to avoid huge pixmaps.
+ effectRect &= source->deviceRect();
+ effectRect |= effectRect.translated(-shadowOffset);
+ if (effectRect.isEmpty())
+ return; // nothing to paint;
+ }
+
+ QPixmap pixmap(effectRect.size());
+ pixmap.fill(Qt::transparent);
+ QPainter pixmapPainter(&pixmap);
+ pixmapPainter.setRenderHints(painter->renderHints());
+ pixmapPainter.setWorldTransform(painter->worldTransform());
+ if (effectRect.x() != 0 || effectRect.y() != 0)
+ pixmapPainter.translate(-effectRect.topLeft());
+ source->draw(&pixmapPainter);
+ pixmapPainter.end();
+
+ QImage img = pixmap.toImage();
+ QImage shadowImage(img.size(), QImage::Format_ARGB32);
+ shadowImage.fill(qRgba(0, 0, 0, d->alpha * 255));
+ shadowImage.setAlphaChannel(img.alphaChannel());
+ shadowImage = blurred(shadowImage, shadowImage.rect(), d->radius);
+
+ // Draw using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawImage(effectRect.topLeft() + shadowOffset, shadowImage);
+ painter->drawPixmap(effectRect.topLeft(), pixmap);
+ painter->setWorldTransform(restoreTransform);
+}
+
+#endif
diff --git a/src/gui/graphicsview/qgraphicseffect.h b/src/gui/graphicsview/qgraphicseffect.h
new file mode 100644
index 0000000..d171b1b
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicseffect.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** 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>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGraphicsItem;
+class QStyleOption;
+class QColor;
+class QPainter;
+class QRectF;
+class QRect;
+class QPixmap;
+
+class QGraphicsEffectSourcePrivate;
+class Q_GUI_EXPORT QGraphicsEffectSource : public QObject
+{
+ Q_OBJECT
+public:
+ ~QGraphicsEffectSource();
+ const QGraphicsItem *graphicsItem() 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;
+};
+
+class QGraphicsEffectPrivate;
+class Q_GUI_EXPORT QGraphicsEffect : public QObject
+{
+ Q_OBJECT
+ Q_FLAGS(ChangeFlags)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+public:
+ enum ChangeFlag {
+ SourceAttached = 0x1,
+ SourceDetached = 0x2,
+ SourceBoundingRectChanged = 0x4,
+ SourceInvalidated = 0x8
+ };
+ Q_DECLARE_FLAGS(ChangeFlags, ChangeFlag);
+
+ QGraphicsEffect();
+ virtual ~QGraphicsEffect();
+
+ // ### make protected?
+ 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
+
+protected:
+ QGraphicsEffect(QGraphicsEffectPrivate &d);
+ 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;
+};
+
+class QGraphicsGrayscaleEffectPrivate;
+class Q_GUI_EXPORT QGraphicsGrayscaleEffect: public QGraphicsEffect
+{
+ Q_OBJECT
+public:
+ QGraphicsGrayscaleEffect();
+ ~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
+public:
+ QGraphicsColorizeEffect();
+ ~QGraphicsColorizeEffect();
+
+ QColor color() const;
+ void setColor(const QColor &c);
+
+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
+public:
+ QGraphicsPixelizeEffect();
+ ~QGraphicsPixelizeEffect();
+
+ int pixelSize() const;
+ void setPixelSize(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
+public:
+ QGraphicsBlurEffect();
+ ~QGraphicsBlurEffect();
+
+ int blurRadius() const;
+ void setBlurRadius(int blurRadius);
+
+protected:
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsBlurEffect)
+ Q_DISABLE_COPY(QGraphicsBlurEffect)
+};
+
+class QGraphicsBloomEffectPrivate;
+class Q_GUI_EXPORT QGraphicsBloomEffect: public QGraphicsEffect {
+ Q_OBJECT
+public:
+ QGraphicsBloomEffect();
+ ~QGraphicsBloomEffect();
+
+ int blurRadius() const;
+ void setBlurRadius(int blurRadius);
+
+ qreal opacity() const;
+ void setOpacity(qreal opacity);
+
+protected:
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsBloomEffect)
+ Q_DISABLE_COPY(QGraphicsBloomEffect)
+};
+
+class QGraphicsFrameEffectPrivate;
+class Q_GUI_EXPORT QGraphicsFrameEffect: public QGraphicsEffect {
+ Q_OBJECT
+public:
+ QGraphicsFrameEffect();
+ ~QGraphicsFrameEffect();
+
+ QColor frameColor() const;
+ void setFrameColor(const QColor &c);
+
+ qreal frameWidth() const;
+ void setFrameWidth(qreal frameWidth);
+
+ qreal frameOpacity() const;
+ void setFrameOpacity(qreal opacity);
+
+protected:
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsFrameEffect)
+ Q_DISABLE_COPY(QGraphicsFrameEffect)
+};
+
+class QGraphicsShadowEffectPrivate;
+class Q_GUI_EXPORT QGraphicsShadowEffect: public QGraphicsEffect {
+ Q_OBJECT
+public:
+ QGraphicsShadowEffect();
+ ~QGraphicsShadowEffect();
+
+ QPointF shadowOffset() const;
+ void setShadowOffset(const QPointF &ofs);
+ inline void setShadowOffset(qreal dx, qreal dy)
+ { setShadowOffset(QPointF(dx, dy)); }
+ inline void setShadowOffset(qreal d)
+ { setShadowOffset(QPointF(d, d)); }
+
+ int blurRadius() const;
+ void setBlurRadius(int blurRadius);
+
+ qreal opacity() const;
+ void setOpacity(qreal opacity);
+
+protected:
+ QRectF boundingRectFor(const QRectF &rect) const;
+ void draw(QPainter *painter, QGraphicsEffectSource *source);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsShadowEffect)
+ Q_DISABLE_COPY(QGraphicsShadowEffect)
+};
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif // QGRAPHICSEFFECT_H
diff --git a/src/gui/graphicsview/qgraphicseffect_p.h b/src/gui/graphicsview/qgraphicseffect_p.h
new file mode 100644
index 0000000..6d546cc
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicseffect_p.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** 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"
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+#include <private/qobject_p.h>
+#include <private/qpixmapfilter_p.h>
+
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+#include <QtGui/qcolor.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 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), blurRadius(4) {}
+ ~QGraphicsBlurEffectPrivate() { delete filter; }
+
+ QPixmapBlurFilter *filter;
+ int blurRadius;
+};
+
+class QGraphicsBloomEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsBlurEffect)
+public:
+ QGraphicsBloomEffectPrivate() : blurRadius(6), opacity(0.7) {}
+
+ int blurRadius;
+ qreal opacity;
+};
+
+class QGraphicsFrameEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsFrameEffect)
+public:
+ QGraphicsFrameEffectPrivate() : color(Qt::blue), width(5), alpha(0.6) {}
+
+ QColor color;
+ qreal width;
+ qreal alpha;
+};
+
+class QGraphicsShadowEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsShadowEffect)
+public:
+ QGraphicsShadowEffectPrivate() : offset(4, 4), radius(8), alpha(0.7) {}
+
+ QPointF offset;
+ int radius;
+ qreal alpha;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_GRAPHICSVIEW
+
+#endif // QGRAPHICSEFFECT_P_H
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index 8d9a1f8..38b80fc 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -1038,11 +1038,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;
@@ -1050,19 +1045,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,
@@ -1201,6 +1197,7 @@ QGraphicsItem::~QGraphicsItem()
else
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);
@@ -2004,8 +2001,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)
@@ -2109,8 +2106,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;
@@ -2218,6 +2215,119 @@ 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 won't let you install another. You must first
+ delete the existing effect (returned by graphicsEffect()) before you can call
+ setGraphicsEffect() with the new 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) {
+ // ### This seems wrong - the effect should automatically be deleted.
+ qWarning("QGraphicsItem::setGraphicsEffect: Attempting to set QGraphicsEffect "
+ "%p on %p, which already has an effect installed", effect, this);
+ return;
+ }
+
+ 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();
+}
+
+/*!
+ \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 QGraphicsItem::effectiveBoundingRect() const
+{
+ QGraphicsEffect *effect = d_ptr->graphicsEffect;
+ QRectF brect = effect && effect->isEnabled() ? effect->boundingRect() : boundingRect();
+ if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
+ return brect;
+
+ const QGraphicsItem *effectParent = d_ptr->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;
+}
+
+/*!
+ \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 QGraphicsItem::sceneEffectiveBoundingRect() const
+{
+ // Find translate-only offset
+ // COMBINE
+ QPointF offset;
+ const QGraphicsItem *parentItem = this;
+ 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.
@@ -3743,10 +3853,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;
}
/*!
@@ -4656,6 +4766,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,
@@ -5995,6 +6112,7 @@ void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event)
void QGraphicsItem::focusInEvent(QFocusEvent *event)
{
Q_UNUSED(event);
+ update();
}
/*!
@@ -6006,6 +6124,7 @@ void QGraphicsItem::focusInEvent(QFocusEvent *event)
void QGraphicsItem::focusOutEvent(QFocusEvent *event)
{
Q_UNUSED(event);
+ update();
}
/*!
@@ -6020,8 +6139,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();
}
/*!
@@ -6049,8 +6167,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();
}
/*!
@@ -6551,6 +6668,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);
@@ -6574,8 +6692,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;
@@ -9938,6 +10059,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 f142b0f..58f1c36 100644
--- a/src/gui/graphicsview/qgraphicsitem.h
+++ b/src/gui/graphicsview/qgraphicsitem.h
@@ -62,6 +62,7 @@ QT_MODULE(Gui)
class QBrush;
class QCursor;
class QFocusEvent;
+class QGraphicsEffect;
class QGraphicsItemGroup;
class QGraphicsObject;
class QGraphicsSceneContextMenuEvent;
@@ -208,6 +209,12 @@ public:
qreal effectiveOpacity() const;
void setOpacity(qreal opacity);
+ // Effect
+ QGraphicsEffect *graphicsEffect() const;
+ void setGraphicsEffect(QGraphicsEffect *effect);
+ QRectF effectiveBoundingRect() const;
+ QRectF sceneEffectiveBoundingRect() const;
+
Qt::MouseButtons acceptedMouseButtons() const;
void setAcceptedMouseButtons(Qt::MouseButtons buttons);
@@ -445,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 805b554..4763a4f 100644
--- a/src/gui/graphicsview/qgraphicsitem_p.h
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -57,6 +57,7 @@
#include "qset.h"
#include "qpixmapcache.h"
#include "qgraphicsview_p.h"
+#include "qgraphicseffect_p.h"
#include "qgraphicstransform.h"
#include "qgraphicstransform_p.h"
@@ -104,7 +105,8 @@ public:
ExtraCursor,
ExtraCacheData,
ExtraMaxDeviceCoordCacheSize,
- ExtraBoundingRegionGranularity
+ ExtraBoundingRegionGranularity,
+ ExtraGestures
};
enum AncestorFlag {
@@ -121,6 +123,7 @@ public:
scene(0),
parent(0),
transformData(0),
+ graphicsEffect(0),
index(-1),
siblingIndex(-1),
depth(0),
@@ -165,6 +168,8 @@ public:
acceptedTouchBeginEvent(0),
filtersDescendantEvents(0),
sceneTransformTranslateOnly(0),
+ notifyBoundingRectChanged(0),
+ notifyInvalidated(0),
globalStackingOrder(-1),
q_ptr(0)
{
@@ -414,6 +419,7 @@ public:
QList<QGraphicsItem *> children;
struct TransformData;
TransformData *transformData;
+ QGraphicsEffect *graphicsEffect;
QTransform sceneTransform;
int index;
int siblingIndex;
@@ -463,7 +469,9 @@ public:
quint32 acceptedTouchBeginEvent : 1;
quint32 filtersDescendantEvents : 1;
quint32 sceneTransformTranslateOnly : 1;
- quint32 unused : 5; // feel free to use
+ quint32 notifyBoundingRectChanged : 1;
+ quint32 notifyInvalidated : 1;
+ quint32 unused : 3; // feel free to use
// Optional stacking order
int globalStackingOrder;
@@ -509,6 +517,71 @@ 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 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 da4a347..7d0876f 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -208,6 +208,7 @@
#ifndef QT_NO_GRAPHICSVIEW
+#include "qgraphicseffect_p.h"
#include "qgraphicsitem.h"
#include "qgraphicsitem_p.h"
#include "qgraphicslayout.h"
@@ -4303,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);
@@ -4352,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) {
@@ -4370,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);
}
@@ -4390,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);
@@ -4407,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);
@@ -4431,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);
}
}
@@ -4514,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;
}
}
@@ -4624,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() || (connectedSignals[0] & changedSignalMask);
- 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 c0c6e75..67a2983 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 a4bbdd2..64ee785 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(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 07d23b7..55a493d 100644
--- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
+++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
@@ -173,7 +173,7 @@ void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex()
if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
continue;
- bsp.insertItem(item, item->sceneBoundingRect());
+ bsp.insertItem(item, item->sceneEffectiveBoundingRect());
}
}
unindexedItems.clear();
@@ -353,7 +353,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->sceneEffectiveBoundingRect());
}
} else {
unindexedItems.removeOne(item);
diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp
index 184bd65..579d057 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.
@@ -837,4 +1061,5 @@ void QPixmapDropShadowFilter::draw(QPainter *p,
// 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 51292b3..ac393a1 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
diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h
index 57c9e2d..5298b6a 100644
--- a/src/gui/painting/qpaintengine.h
+++ b/src/gui/painting/qpaintengine.h
@@ -210,6 +210,7 @@ public:
Direct3D,
Pdf,
OpenVG,
+ OpenGL2,
User = 50, // first user type id
MaxUser = 100 // last user type id
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 d7c91b8..0692277 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 99711bd40..884e687 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 4e32f91..e379aa3 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 a3475c7..10259d2 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -690,6 +690,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)
@@ -1086,6 +1092,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);
@@ -1690,6 +1711,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 906817b..a5fe8a1 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 868484e..c658557 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 5a06763..1e72fd8 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..e8d9ae3
--- /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()
+ : QGraphicsEffect(*new QGraphicsShaderEffectPrivate())
+{
+}
+
+/*!
+ 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..29b7c9f
--- /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();
+ 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 e3ee2b2..f9d09b7 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 671f9a7..14fb072 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
{