summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAriya Hidayat <ariya.hidayat@nokia.com>2009-06-02 11:57:45 (GMT)
committerAriya Hidayat <ariya.hidayat@nokia.com>2009-06-02 12:06:04 (GMT)
commit96773d4b21ce288b26857159dfbb553a81ae3a94 (patch)
tree2e8611e4737233a636d6249ce7d90e98bd657a75
parentf001cda07f6aa026d59e448b49212c0182ed895c (diff)
downloadQt-96773d4b21ce288b26857159dfbb553a81ae3a94.zip
Qt-96773d4b21ce288b26857159dfbb553a81ae3a94.tar.gz
Qt-96773d4b21ce288b26857159dfbb553a81ae3a94.tar.bz2
Another n-th attempt at making an API for the effect framework.
The implementation is not efficient, it serves as the proof-of-concept only. Check the notes in qgraphicseffect.cpp for details.
-rw-r--r--src/gui/graphicsview/graphicsview.pri2
-rw-r--r--src/gui/graphicsview/qgraphicseffect.cpp556
-rw-r--r--src/gui/graphicsview/qgraphicseffect.h258
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp119
-rw-r--r--src/gui/graphicsview/qgraphicsitem.h9
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h8
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp97
-rw-r--r--src/gui/graphicsview/qgraphicsscene.h6
-rw-r--r--src/gui/graphicsview/qgraphicsview.cpp21
9 files changed, 1063 insertions, 13 deletions
diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri
index 02d9bb1..f18bafc 100644
--- a/src/gui/graphicsview/graphicsview.pri
+++ b/src/gui/graphicsview/graphicsview.pri
@@ -2,6 +2,7 @@
HEADERS += \
graphicsview/qgraphicsitem.h \
+ graphicsview/qgraphicseffect.h \
graphicsview/qgraphicsitem_p.h \
graphicsview/qgraphicsitemanimation.h \
graphicsview/qgraphicsscene.h \
@@ -13,6 +14,7 @@ HEADERS += \
SOURCES += \
graphicsview/qgraphicsitem.cpp \
+ graphicsview/qgraphicseffect.cpp \
graphicsview/qgraphicsitemanimation.cpp \
graphicsview/qgraphicsscene.cpp \
graphicsview/qgraphicsscene_bsp.cpp \
diff --git a/src/gui/graphicsview/qgraphicseffect.cpp b/src/gui/graphicsview/qgraphicseffect.cpp
new file mode 100644
index 0000000..1d0e485
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicseffect.cpp
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** 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 "qgraphicseffect.h"
+
+#ifndef QT_NO_GRAPHICSVIEW
+
+#include <QtGui/qimage.h>
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qgraphicsscene.h>
+#include <QtGui/qpainter.h>
+
+#include <image/qpixmapfilter_p.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).
+
+*/
+
+QGraphicsEffect::QGraphicsEffect()
+{
+}
+
+QGraphicsEffect::~QGraphicsEffect()
+{
+}
+
+QRectF QGraphicsEffect::boundingRectFor(const QGraphicsItem *item)
+{
+ // default is to give the item's bounding rect
+ // do NOT call item->effectiveBoundRect() because
+ // that function will call this one (infinite loop)
+ return item->boundingRect();
+}
+
+// this helper function is only for subclasses of QGraphicsEffect
+// the implementation is trivial, but this allows us to keep
+// QGraphicsScene::drawItem() as a protected function
+// (since QGraphicsEffect is a friend of QGraphicsScene)
+QPixmap* QGraphicsEffect::drawItemOnPixmap(QPainter *painter, QGraphicsItem *item,
+ const QStyleOptionGraphicsItem *option, QWidget *widget, int flags)
+{
+ if (!item->scene())
+ return 0;
+ return item->scene()->drawItemOnPixmap(painter, item, option, widget, flags);
+}
+
+QGraphicsGrayscaleEffect::QGraphicsGrayscaleEffect(): QGraphicsEffect()
+{
+ filter = new QPixmapColorizeFilter;
+ filter->setColor(Qt::black);
+}
+
+QGraphicsGrayscaleEffect::~QGraphicsGrayscaleEffect()
+{
+ delete filter;
+}
+
+void QGraphicsGrayscaleEffect::drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0);
+ if (!pixmap)
+ return;
+
+ // Draw the pixmap with the filter using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ filter->draw(painter, deviceRect.topLeft(), *pixmap, pixmap->rect());
+ painter->setWorldTransform(restoreTransform);
+}
+
+QGraphicsColorizeEffect::QGraphicsColorizeEffect(): QGraphicsEffect()
+{
+ filter = new QPixmapColorizeFilter;
+}
+
+QGraphicsColorizeEffect::~QGraphicsColorizeEffect()
+{
+ delete filter;
+}
+
+QColor QGraphicsColorizeEffect::color() const
+{
+ return filter->color();
+}
+
+void QGraphicsColorizeEffect::setColor(const QColor &c)
+{
+ filter->setColor(c);
+}
+
+void QGraphicsColorizeEffect::drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0);
+ if (!pixmap)
+ return;
+
+ // Draw the pixmap with the filter using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ filter->draw(painter, deviceRect.topLeft(), *pixmap, pixmap->rect());
+ painter->setWorldTransform(restoreTransform);
+}
+
+QGraphicsPixelizeEffect::QGraphicsPixelizeEffect(): QGraphicsEffect(), size(2)
+{
+}
+
+QGraphicsPixelizeEffect::~QGraphicsPixelizeEffect()
+{
+}
+
+void QGraphicsPixelizeEffect::drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0);
+ if (!pixmap)
+ return;
+
+ // pixelize routine
+ QImage img = pixmap->toImage().convertToFormat(QImage::Format_ARGB32);
+ if (size > 0) {
+ int width = img.width();
+ int height = img.height();
+ for (int y = 0; y < height; y += size) {
+ int ys = qMin(height - 1, y + size / 2);
+ QRgb *sbuf = reinterpret_cast<QRgb*>(img.scanLine(ys));
+ for (int x = 0; x < width; x += size) {
+ int xs = qMin(width - 1, x + size / 2);
+ QRgb color = sbuf[xs];
+ for (int yi = 0; yi < qMin(size, height - y); ++yi) {
+ QRgb *buf = reinterpret_cast<QRgb*>(img.scanLine(y + yi));
+ for (int xi = 0; xi < qMin(size, width - x); ++xi)
+ buf[x + xi] = color;
+ }
+ }
+ }
+ }
+
+ // Draw using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawImage(deviceRect.topLeft(), img);
+ painter->setWorldTransform(restoreTransform);
+}
+
+
+QGraphicsBlurEffect::QGraphicsBlurEffect(): QGraphicsEffect(), radius(6)
+{
+}
+
+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;
+}
+
+QRectF QGraphicsBlurEffect::boundingRectFor(const QGraphicsItem *item)
+{
+ qreal delta = radius * 3;
+ QRectF blurRect = item->boundingRect();
+ blurRect.adjust(-delta, -delta, delta, delta);
+ return blurRect;
+}
+
+void QGraphicsBlurEffect::drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0);
+ if (!pixmap)
+ return;
+
+ // blur routine
+ QImage img = pixmap->toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ img = blurred(img, img.rect(), radius);
+
+ // Draw using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawImage(deviceRect.topLeft() - QPointF(radius * 3, radius * 3), img);
+ painter->setWorldTransform(restoreTransform);
+}
+
+
+QGraphicsBloomEffect::QGraphicsBloomEffect(): QGraphicsEffect(), radius(6), alpha(0.7)
+{
+}
+
+QGraphicsBloomEffect::~QGraphicsBloomEffect()
+{
+}
+
+QRectF QGraphicsBloomEffect::boundingRectFor(const QGraphicsItem *item)
+{
+ qreal delta = radius * 3;
+ QRectF blurRect = item->boundingRect();
+ blurRect.adjust(-delta, -delta, delta, delta);
+ return blurRect;
+}
+
+// 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::drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0);
+ if (!pixmap)
+ return;
+
+ // 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, alpha, QPainter::CompositionMode_Overlay);
+
+ // Draw using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawImage(deviceRect.topLeft() - QPointF(radius * 3, radius * 3), img);
+ painter->setWorldTransform(restoreTransform);
+}
+
+QGraphicsFrameEffect::QGraphicsFrameEffect()
+ : QGraphicsEffect()
+ , color(Qt::blue)
+ , width(5)
+ , alpha(0.6)
+{
+}
+
+QGraphicsFrameEffect::~QGraphicsFrameEffect()
+{
+}
+
+QRectF QGraphicsFrameEffect::boundingRectFor(const QGraphicsItem *item)
+{
+ QRectF frameRect = item->boundingRect();
+ frameRect.adjust(-width, -width, width, width);
+ return frameRect;
+}
+
+void QGraphicsFrameEffect::drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0);
+ if (!pixmap)
+ return;
+
+ QRectF frameRect = deviceBounds;
+ frameRect.adjust(-width, -width, width, width);
+
+ painter->save();
+ painter->setWorldTransform(QTransform());
+
+ painter->save();
+ painter->setOpacity(painter->opacity() * alpha);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(color);
+ painter->drawRoundedRect(frameRect, 20, 20, Qt::RelativeSize);
+ painter->restore();
+
+ painter->drawPixmap(frameRect.topLeft(), *pixmap);
+
+ painter->restore();
+}
+
+
+QGraphicsShadowEffect::QGraphicsShadowEffect()
+ : QGraphicsEffect()
+ , offset(4,4)
+ , radius(8)
+ , alpha(0.7)
+{
+}
+
+QGraphicsShadowEffect::~QGraphicsShadowEffect()
+{
+}
+
+QRectF QGraphicsShadowEffect::boundingRectFor(const QGraphicsItem *item)
+{
+ QRectF shadowRect = item->boundingRect();
+ shadowRect.adjust(offset.x(), offset.y(), offset.x(), offset.y());
+ QRectF blurRect = shadowRect;
+ qreal delta = radius * 3;
+ blurRect.adjust(-delta, -delta, delta, delta);
+ QRectF totalRect = blurRect.united(item->boundingRect());
+ return totalRect;
+}
+
+void QGraphicsShadowEffect::drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ QRectF shadowRect = deviceBounds;
+ shadowRect.adjust(offset.x(), offset.y(), offset.x(), offset.y());
+ QRectF blurRect = shadowRect;
+ qreal delta = radius * 3;
+ blurRect.adjust(-delta, -delta, delta, delta);
+ QRectF totalRect = blurRect.united(deviceRect);
+
+ QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0);
+ if (!pixmap)
+ return;
+
+ QImage img = pixmap->toImage();
+ QImage shadowImage(img.size(), QImage::Format_ARGB32);
+ shadowImage.fill(qRgba(0, 0, 0, alpha * 255));
+ shadowImage.setAlphaChannel(img.alphaChannel());
+ shadowImage = blurred(shadowImage, shadowImage.rect(), radius);
+
+ // Draw using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+
+ QRect shadowAlignedRect = shadowRect.toAlignedRect();
+
+ qreal shadowx = blurRect.x() + delta;
+ qreal shadowy = blurRect.y() + delta;
+ if (blurRect.x() < deviceRect.x())
+ shadowx = blurRect.x() + offset.x();
+ if (blurRect.y() < deviceRect.y())
+ shadowy = blurRect.y() + offset.y();
+ painter->drawImage(shadowx, shadowy, shadowImage);
+
+ qreal itemx = qMin(blurRect.x(), deviceBounds.x());
+ qreal itemy = qMin(blurRect.y(), deviceBounds.y());
+ painter->drawPixmap(itemx, itemy, *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..9f6fb07
--- /dev/null
+++ b/src/gui/graphicsview/qgraphicseffect.h
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** 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/qglobal.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qvariant.h>
+#include <QtGui/qcolor.h>
+
+QT_FORWARD_DECLARE_CLASS(QGraphicsItem);
+QT_FORWARD_DECLARE_CLASS(QStyleOptionGraphicsItem);
+QT_FORWARD_DECLARE_CLASS(QPainter);
+QT_FORWARD_DECLARE_CLASS(QPixmap);
+QT_FORWARD_DECLARE_CLASS(QWidget);
+QT_FORWARD_DECLARE_CLASS(QPixmapColorizeFilter);
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class Q_GUI_EXPORT QGraphicsEffect {
+public:
+
+ QGraphicsEffect();
+ virtual ~QGraphicsEffect();
+
+ virtual QRectF boundingRectFor(const QGraphicsItem *item);
+
+ virtual void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0) = 0;
+
+protected:
+ QPixmap* drawItemOnPixmap(QPainter *painter, QGraphicsItem *item,
+ const QStyleOptionGraphicsItem *option, QWidget *widget, int flags);
+
+private:
+ Q_DISABLE_COPY(QGraphicsEffect);
+};
+
+class Q_GUI_EXPORT QGraphicsGrayscaleEffect: public QGraphicsEffect {
+public:
+
+ QGraphicsGrayscaleEffect();
+ ~QGraphicsGrayscaleEffect();
+
+ void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0);
+
+private:
+ QPixmapColorizeFilter *filter;
+ Q_DISABLE_COPY(QGraphicsGrayscaleEffect);
+};
+
+class Q_GUI_EXPORT QGraphicsColorizeEffect: public QGraphicsEffect {
+public:
+
+ QGraphicsColorizeEffect();
+ ~QGraphicsColorizeEffect();
+
+ QColor color() const;
+ void setColor(const QColor &c);
+
+ void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0);
+
+private:
+ QPixmapColorizeFilter *filter;
+ Q_DISABLE_COPY(QGraphicsColorizeEffect);
+};
+
+class Q_GUI_EXPORT QGraphicsPixelizeEffect: public QGraphicsEffect {
+public:
+
+ QGraphicsPixelizeEffect();
+ ~QGraphicsPixelizeEffect();
+
+ int pixelSize() const { return size; }
+ void setPixelSize(int pixelSize) { size = pixelSize; }
+
+ void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0);
+
+private:
+ int size;
+ Q_DISABLE_COPY(QGraphicsPixelizeEffect);
+};
+
+class Q_GUI_EXPORT QGraphicsBlurEffect: public QGraphicsEffect {
+public:
+
+ QGraphicsBlurEffect();
+ ~QGraphicsBlurEffect();
+
+ int blurRadius() const { return radius; }
+ void setBlurRadius(int blurRadius) { radius = blurRadius; }
+
+ QRectF boundingRectFor(const QGraphicsItem *item);
+
+ void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0);
+
+private:
+ int radius;
+ Q_DISABLE_COPY(QGraphicsBlurEffect);
+};
+
+class Q_GUI_EXPORT QGraphicsBloomEffect: public QGraphicsEffect {
+public:
+
+ QGraphicsBloomEffect();
+ ~QGraphicsBloomEffect();
+
+ int blurRadius() const { return radius; }
+ void setBlurRadius(int blurRadius) { radius = blurRadius; }
+
+ QRectF boundingRectFor(const QGraphicsItem *item);
+
+ qreal opacity() const { return alpha; }
+ void setOpacity(qreal opacity) { alpha = opacity; }
+
+ void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0);
+
+private:
+ int radius;
+ qreal alpha;
+ Q_DISABLE_COPY(QGraphicsBloomEffect);
+};
+
+class Q_GUI_EXPORT QGraphicsFrameEffect: public QGraphicsEffect {
+public:
+
+ QGraphicsFrameEffect();
+ ~QGraphicsFrameEffect();
+
+ QColor frameColor() const { return color; }
+ void setFrameColor(const QColor &c) { color = c; }
+
+ qreal frameWidth() const { return width; }
+ void setFrameWidth(qreal frameWidth) { width = frameWidth; }
+
+ qreal frameOpacity() const { return alpha; }
+ void setFrameOpacity(qreal opacity) { alpha = opacity; }
+
+
+ QRectF boundingRectFor(const QGraphicsItem *item);
+
+ void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0);
+private:
+ QColor color;
+ qreal width;
+ qreal alpha;
+ Q_DISABLE_COPY(QGraphicsFrameEffect);
+};
+
+class Q_GUI_EXPORT QGraphicsShadowEffect: public QGraphicsEffect {
+public:
+
+ QGraphicsShadowEffect();
+ ~QGraphicsShadowEffect();
+
+ QPointF shadowOffset() const;
+ void setShadowOffset(const QPointF &ofs) { offset = ofs; }
+ inline void setShadowOffset(qreal dx, qreal dy) { setShadowOffset(QPointF(dx, dy)); }
+ inline void setShadowOffset(qreal d) { setShadowOffset(QPointF(d, d)); }
+
+ int blurRadius() const { return radius; }
+ void setBlurRadius(int blurRadius) { radius = blurRadius; }
+
+ qreal opacity() const { return alpha; }
+ void setOpacity(qreal opacity) { alpha = opacity; }
+
+ QRectF boundingRectFor(const QGraphicsItem *item);
+
+ void drawItem(QGraphicsItem *item, QPainter *painter,
+ const QStyleOptionGraphicsItem *option = 0,
+ QWidget *widget = 0);
+
+protected:
+ QPointF offset;
+ int radius;
+ qreal alpha;
+
+private:
+ Q_DISABLE_COPY(QGraphicsShadowEffect);
+};
+
+
+
+Q_DECLARE_METATYPE(QGraphicsEffect *)
+Q_DECLARE_METATYPE(QGraphicsGrayscaleEffect *)
+Q_DECLARE_METATYPE(QGraphicsColorizeEffect *)
+Q_DECLARE_METATYPE(QGraphicsPixelizeEffect *)
+Q_DECLARE_METATYPE(QGraphicsBlurEffect *)
+Q_DECLARE_METATYPE(QGraphicsBloomEffect *)
+Q_DECLARE_METATYPE(QGraphicsFrameEffect *)
+Q_DECLARE_METATYPE(QGraphicsShadowEffect *)
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif // QGRAPHICSEFFECT_H
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index b9e462b..f0a256a 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -531,6 +531,7 @@
#ifndef QT_NO_GRAPHICSVIEW
+#include "qgraphicseffect.h"
#include "qgraphicsscene.h"
#include "qgraphicsscene_p.h"
#include "qgraphicssceneevent.h"
@@ -2047,6 +2048,124 @@ void QGraphicsItem::setOpacity(qreal opacity)
}
/*!
+ \since 4.6
+ Returns this item's \e effect if it has one; otherwise,
+ returns 0.
+*/
+QGraphicsEffect *QGraphicsItem::effect() const
+{
+ QGraphicsEffect *fx = 0;
+ if (d_ptr->hasEffect)
+ fx = d_ptr->extra(QGraphicsItemPrivate::ExtraEffect).value<QGraphicsEffect*>();
+
+ return fx;
+}
+
+/*!
+ \since 4.6
+ Sets \e effect as the item's effect. It will replace the previous effect
+ the item might have.
+*/
+void QGraphicsItem::setEffect(QGraphicsEffect *effect)
+{
+ if (effect) {
+ d_ptr->hasEffect = true;
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraEffect, QVariant::fromValue(effect));
+ } else {
+ d_ptr->hasEffect = false;
+ d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraEffect);
+ void *ptr = d_ptr->extra(QGraphicsItemPrivate::ExtraEffectPixmap).value<void*>();
+ QPixmap *pixmap = reinterpret_cast<QPixmap*>(ptr);
+ delete pixmap;
+ d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraEffectPixmap);
+ }
+
+ update();
+}
+
+/*!
+ \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 *fx = effect();
+ if (fx)
+ return fx->boundingRectFor(this);
+
+ return boundingRect();
+}
+
+/*!
+ \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
+ QPointF offset;
+ const QGraphicsItem *parentItem = this;
+ const QGraphicsItemPrivate *itemd;
+ do {
+ itemd = parentItem->d_ptr;
+ if (itemd->hasTransform)
+ break;
+ offset += itemd->pos;
+ } while ((parentItem = itemd->parent));
+
+ QRectF br = effectiveBoundingRect();
+ br.translate(offset);
+ return !parentItem ? br : parentItem->sceneTransform().mapRect(br);
+}
+
+/*!
+ \internal
+
+ Used by QGraphicsScene.
+*/
+QPixmap *QGraphicsItem::effectPixmap()
+{
+ if (!d_ptr->hasEffect)
+ return 0;
+
+ // the exact size of the pixmap is not a big deal
+ // as long as it contains the effective bounding rect
+ // TODO: use smart resizing etc
+ // TODO: store per device and do everything in device coordinate?
+ // TODO: use layer
+ QRect rect = effectiveBoundingRect().toAlignedRect();
+
+ void *ptr = d_ptr->extra(QGraphicsItemPrivate::ExtraEffectPixmap).value<void*>();
+ QPixmap *pixmap = reinterpret_cast<QPixmap*>(ptr);
+ bool avail = true;
+ if (!pixmap)
+ avail = false;
+ if (avail && pixmap->size() != rect.size())
+ avail = false;
+
+ if (!avail) {
+ delete pixmap;
+ pixmap = new QPixmap(rect.size());
+ pixmap->fill(Qt::transparent);
+ ptr = reinterpret_cast<void*>(pixmap);
+ d_ptr->setExtra(QGraphicsItemPrivate::ExtraEffectPixmap, QVariant::fromValue(ptr));
+ }
+
+ return pixmap;
+}
+
+/*!
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.
diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h
index f6ee197..0c85245 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 QGraphicsSceneContextMenuEvent;
class QGraphicsSceneDragDropEvent;
@@ -198,6 +199,12 @@ public:
qreal effectiveOpacity() const;
void setOpacity(qreal opacity);
+ // Effect
+ QGraphicsEffect *effect() const;
+ void setEffect(QGraphicsEffect *effect);
+ QRectF effectiveBoundingRect() const;
+ QRectF sceneEffectiveBoundingRect() const;
+
Qt::MouseButtons acceptedMouseButtons() const;
void setAcceptedMouseButtons(Qt::MouseButtons buttons);
@@ -418,6 +425,8 @@ protected:
void removeFromIndex();
void prepareGeometryChange();
+ QPixmap *effectPixmap();
+
private:
Q_DISABLE_COPY(QGraphicsItem)
Q_DECLARE_PRIVATE(QGraphicsItem)
diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h
index bd81fe5..a2cb4ab 100644
--- a/src/gui/graphicsview/qgraphicsitem_p.h
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -113,7 +113,9 @@ public:
ExtraBoundingRegionGranularity,
ExtraOpacity,
ExtraEffectiveOpacity,
- ExtraDecomposedTransform
+ ExtraDecomposedTransform,
+ ExtraEffect,
+ ExtraEffectPixmap,
};
enum AncestorFlag {
@@ -155,6 +157,7 @@ public:
dirtyClipPath(1),
emptyClipPath(0),
inSetPosHelper(0),
+ hasEffect(0),
flags(0),
allChildrenCombineOpacity(1),
hasDecomposedTransform(0),
@@ -345,12 +348,13 @@ public:
quint32 inSetPosHelper : 1;
// New 32 bits
+ quint32 hasEffect : 1;
quint32 flags : 10;
quint32 allChildrenCombineOpacity : 1;
quint32 hasDecomposedTransform : 1;
quint32 dirtyTransform : 1;
quint32 dirtyTransformComponents : 1;
- quint32 padding : 18; // feel free to use
+ quint32 padding : 17; // feel free to use
// Optional stacking order
int globalStackingOrder;
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
index 1fc4567..bf8ca0a 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -210,6 +210,7 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000;
#ifndef QT_NO_GRAPHICSVIEW
+#include "qgraphicseffect.h"
#include "qgraphicsitem.h"
#include "qgraphicsitem_p.h"
#include "qgraphicslayout.h"
@@ -309,6 +310,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;
+}
+
static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent)
{
hover->setWidget(mouseEvent->widget());
@@ -392,7 +401,7 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::estimateItemsInRect(const QRectF &
for (int i = 0; i < unindexedItems.size(); ++i) {
if (QGraphicsItem *item = unindexedItems.at(i)) {
if (!item->d_ptr->itemDiscovered && item->d_ptr->visible && !(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) {
- QRectF boundingRect = item->sceneBoundingRect();
+ QRectF boundingRect = item->sceneEffectiveBoundingRect();
if (QRectF_intersects(boundingRect, rect)) {
item->d_ptr->itemDiscovered = 1;
items << item;
@@ -435,7 +444,7 @@ void QGraphicsScenePrivate::addToIndex(QGraphicsItem *item)
{
if (indexMethod == QGraphicsScene::BspTreeIndex) {
if (item->d_func()->index != -1) {
- bspTree.insertItem(item, item->sceneBoundingRect());
+ bspTree.insertItem(item, item->sceneEffectiveBoundingRect());
foreach (QGraphicsItem *child, item->children())
child->addToIndex();
} else {
@@ -455,7 +464,7 @@ void QGraphicsScenePrivate::removeFromIndex(QGraphicsItem *item)
if (indexMethod == QGraphicsScene::BspTreeIndex) {
int index = item->d_func()->index;
if (index != -1) {
- bspTree.removeItem(item, item->sceneBoundingRect());
+ bspTree.removeItem(item, item->sceneEffectiveBoundingRect());
freeItemIndexes << index;
indexedItems[index] = 0;
item->d_func()->index = -1;
@@ -512,7 +521,7 @@ void QGraphicsScenePrivate::_q_updateIndex()
QRectF unindexedItemsBoundingRect;
for (int i = 0; i < unindexedItems.size(); ++i) {
if (QGraphicsItem *item = unindexedItems.at(i)) {
- unindexedItemsBoundingRect |= item->sceneBoundingRect();
+ unindexedItemsBoundingRect |= item->sceneEffectiveBoundingRect();
if (!freeItemIndexes.isEmpty()) {
int freeIndex = freeItemIndexes.takeFirst();
item->d_func()->index = freeIndex;
@@ -559,7 +568,7 @@ void QGraphicsScenePrivate::_q_updateIndex()
// Insert all unindexed items into the tree.
for (int i = 0; i < unindexedItems.size(); ++i) {
if (QGraphicsItem *item = unindexedItems.at(i)) {
- QRectF rect = item->sceneBoundingRect();
+ QRectF rect = item->sceneEffectiveBoundingRect();
if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)
continue;
if (indexMethod == QGraphicsScene::BspTreeIndex)
@@ -612,7 +621,7 @@ void QGraphicsScenePrivate::_q_emitUpdated()
// Ensure all dirty items's current positions are recorded in the list of
// updated rects.
for (int i = 0; i < dirtyItems.size(); ++i)
- updatedRects += dirtyItems.at(i)->sceneBoundingRect();
+ updatedRects += dirtyItems.at(i)->sceneEffectiveBoundingRect();
// Notify the changes to anybody interested.
QList<QRectF> oldUpdatedRects;
@@ -1465,7 +1474,7 @@ QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect,
// ### _q_adjustedRect is only needed because QRectF::intersects,
// QRectF::contains and QTransform::map() and friends don't work with
// flat rectangles.
- const QRectF br(adjustedItemBoundingRect(item));
+ const QRectF br(adjustedItemEffectiveBoundingRect(item));
if (mode >= Qt::ContainsItemBoundingRect) {
// Rect intersects/contains item's bounding rect
QRectF mbr = x.mapRect(br);
@@ -4716,6 +4725,19 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
QGraphicsItemPrivate *itemd = item->d_ptr;
QGraphicsItem::CacheMode cacheMode = QGraphicsItem::CacheMode(itemd->cacheMode);
+ bool noCache = cacheMode == QGraphicsItem::NoCache ||
+#ifdef Q_WS_X11
+ !X11->use_xrender;
+#else
+ false;
+#endif
+
+ // Render using effect, works now only for no cache mode
+ if (noCache && itemd->hasEffect && item->effect()) {
+ item->effect()->drawItem(item, painter, option, widget);
+ return;
+ }
+
// Render directly, using no cache.
if (cacheMode == QGraphicsItem::NoCache
#ifdef Q_WS_X11
@@ -5011,6 +5033,63 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
}
}
+// FIXME: merge this with drawItems (needs refactoring)
+QPixmap* QGraphicsScene::drawItemOnPixmap(QPainter *painter,
+ QGraphicsItem *item,
+ const QStyleOptionGraphicsItem *option,
+ QWidget *widget,
+ int flags)
+{
+ // TODO: use for choosing item or device coordinate
+ // FIXME: how about source, dest, and exposed rects?
+ Q_UNUSED(flags);
+
+ // Item's (local) bounding rect, including the effect
+ QRectF brect = item->effectiveBoundingRect();
+ QRectF adjustedBrect(brect);
+ _q_adjustRect(&adjustedBrect);
+ if (adjustedBrect.isEmpty())
+ return 0;
+
+ // Find the item's bounds in device coordinates.
+ QRectF deviceBounds = painter->worldTransform().mapRect(brect);
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return 0;
+
+ // If widget, check if it intersects or not
+ QRect viewRect = widget ? widget->rect() : QRect();
+ if (widget && !viewRect.intersects(deviceRect))
+ return 0;
+
+ // Create offscreen pixmap
+ // TODO: use the pixmap from the layer
+ QPixmap *targetPixmap = item->effectPixmap();
+ if (!targetPixmap)
+ targetPixmap = new QPixmap(deviceRect.size());
+
+ // FIXME: this is brute force
+ QRegion pixmapExposed;
+ pixmapExposed += targetPixmap->rect();
+
+ // Construct an item-to-pixmap transform.
+ QPointF p = deviceRect.topLeft();
+ QTransform itemToPixmap = painter->worldTransform();
+ if (!p.isNull())
+ itemToPixmap *= QTransform::fromTranslate(-p.x(), -p.y());
+
+ // Calculate the style option's exposedRect.
+ QStyleOptionGraphicsItem fxOption = *option;
+ fxOption.exposedRect = brect.adjusted(-1, -1, 1, 1);
+
+ // Render
+ _q_paintIntoCache(targetPixmap, item, pixmapExposed, itemToPixmap, painter->renderHints(),
+ &fxOption, false);
+
+ return targetPixmap;
+}
+
+
/*!
Paints the given \a items using the provided \a painter, after the
background has been drawn, and before the foreground has been
@@ -5278,10 +5357,10 @@ void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect)
// This block of code is kept for compatibility. Since 4.5, by default
// QGraphicsView does not connect the signal and we use the below
// method of delivering updates.
- update(item->sceneBoundingRect());
+ update(item->sceneEffectiveBoundingRect());
} else {
// ### Remove _q_adjustedRects().
- QRectF boundingRect(adjustedItemBoundingRect(item));
+ QRectF boundingRect(adjustedItemEffectiveBoundingRect(item));
if (!rect.isNull()) {
QRectF adjustedRect(rect);
_q_adjustRect(&adjustedRect);
diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h
index 9802f87..c463736 100644
--- a/src/gui/graphicsview/qgraphicsscene.h
+++ b/src/gui/graphicsview/qgraphicsscene.h
@@ -262,6 +262,11 @@ protected:
const QStyleOptionGraphicsItem options[],
QWidget *widget = 0);
+ QPixmap* drawItemOnPixmap(QPainter *painter, QGraphicsItem *item,
+ const QStyleOptionGraphicsItem *option, QWidget *widget, int flags);
+
+
+
protected Q_SLOTS:
bool focusNextPrevChild(bool next);
@@ -288,6 +293,7 @@ private:
friend class QGraphicsViewPrivate;
friend class QGraphicsWidget;
friend class QGraphicsWidgetPrivate;
+ friend class QGraphicsEffect;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers)
diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp
index 10b837a..9ad7249 100644
--- a/src/gui/graphicsview/qgraphicsview.cpp
+++ b/src/gui/graphicsview/qgraphicsview.cpp
@@ -803,6 +803,16 @@ static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item)
boundingRect.adjust(0, -0.00001, 0, 0.00001);
return boundingRect;
}
+static inline QRectF adjustedItemEffectiveBoundingRect(const QGraphicsItem *item)
+{
+ Q_ASSERT(item);
+ QRectF boundingRect(item->effectiveBoundingRect());
+ if (!boundingRect.width())
+ boundingRect.adjust(-0.00001, 0, 0.00001, 0);
+ if (!boundingRect.height())
+ boundingRect.adjust(0, -0.00001, 0, 0.00001);
+ return boundingRect;
+}
/*!
\internal
@@ -811,12 +821,19 @@ void QGraphicsViewPrivate::itemUpdated(QGraphicsItem *item, const QRectF &rect)
{
if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate)
return;
+
+ // Delayed update can work only if the item has no effect.
+ // Reason: imagine the effect extends the effective (painting) bounding
+ // rect outside the item's bounding rect. If the item is moved around,
+ // delayed update only take into account the effective bounding rect
+ // of the new position, not the old one, thereby leaving painting trails.
+ if (!item->d_ptr->hasEffect)
if (item->d_ptr->dirty)
updateLater();
QRectF updateRect = rect;
if ((item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) || item->d_ptr->children.isEmpty()) {
- updateRect &= adjustedItemBoundingRect(item);
+ updateRect &= adjustedItemEffectiveBoundingRect(item);
if (updateRect.isEmpty())
return;
}
@@ -874,7 +891,7 @@ void QGraphicsViewPrivate::_q_updateLaterSlot()
continue;
}
QTransform x = item->sceneTransform() * viewTransform;
- updateRect(x.mapRect(item->boundingRect()).toAlignedRect() & vr);
+ updateRect(x.mapRect(item->effectiveBoundingRect()).toAlignedRect() & vr);
}
dirtyRectCount += dirtyRects.size();