diff options
author | Ariya Hidayat <ariya.hidayat@nokia.com> | 2009-06-02 11:57:45 (GMT) |
---|---|---|
committer | Ariya Hidayat <ariya.hidayat@nokia.com> | 2009-06-02 12:06:04 (GMT) |
commit | 96773d4b21ce288b26857159dfbb553a81ae3a94 (patch) | |
tree | 2e8611e4737233a636d6249ce7d90e98bd657a75 | |
parent | f001cda07f6aa026d59e448b49212c0182ed895c (diff) | |
download | Qt-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.pri | 2 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicseffect.cpp | 556 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicseffect.h | 258 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 119 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.h | 9 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem_p.h | 8 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 97 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.h | 6 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview.cpp | 21 |
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(); |