From e646d08593dc18cad4e59176c2fe8c10fa5b9497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Nilsen?= Date: Thu, 23 Jul 2009 17:11:54 +0200 Subject: Make QGraphicsEffect independent of QGraphicsItem. This makes it possible to basically implement support for graphics effects on pretty much everything, e.g. QWidget. We currently only support effects on QGraphicsItem, but there's more to come :-) --- examples/graphicsview/blurpicker/blureffect.cpp | 19 +- examples/graphicsview/blurpicker/blureffect.h | 11 +- examples/graphicsview/blurpicker/blurpicker.cpp | 4 +- examples/graphicsview/lighting/lighting.cpp | 2 +- examples/graphicsview/lighting/shadoweffect.cpp | 19 +- examples/graphicsview/lighting/shadoweffect.h | 11 +- src/gui/graphicsview/qgraphicseffect.cpp | 234 ++++++++++++++---------- src/gui/graphicsview/qgraphicseffect.h | 58 +++--- src/gui/graphicsview/qgraphicseffect_p.h | 27 ++- src/gui/graphicsview/qgraphicsitem.cpp | 91 +++------ src/gui/graphicsview/qgraphicsitem.h | 2 - src/gui/graphicsview/qgraphicsitem_p.h | 45 ++++- src/gui/graphicsview/qgraphicsscene.cpp | 72 ++------ src/gui/graphicsview/qgraphicsscene.h | 5 - 14 files changed, 297 insertions(+), 303 deletions(-) diff --git a/examples/graphicsview/blurpicker/blureffect.cpp b/examples/graphicsview/blurpicker/blureffect.cpp index 6c2095d..43791c6 100644 --- a/examples/graphicsview/blurpicker/blureffect.cpp +++ b/examples/graphicsview/blurpicker/blureffect.cpp @@ -43,28 +43,27 @@ #include -BlurEffect::BlurEffect() +BlurEffect::BlurEffect(QGraphicsItem *item) : QGraphicsBlurEffect() - , m_baseLine(200) + , m_baseLine(200), item(item) { } -void BlurEffect::adjustForItem(const QGraphicsItem *item) +void BlurEffect::adjustForItem() { qreal y = m_baseLine - item->pos().y(); qreal radius = qBound(0.0, y / 32, 16.0); setBlurRadius(radius); } -QRectF BlurEffect::boundingRectFor(const QGraphicsItem *item) +QRectF BlurEffect::boundingRect() const { - adjustForItem(item); - return QGraphicsBlurEffect::boundingRectFor(item); + const_cast(this)->adjustForItem(); + return QGraphicsBlurEffect::boundingRect(); } -void BlurEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void BlurEffect::draw(QPainter *painter) { - adjustForItem(item); - QGraphicsBlurEffect::drawItem(item, painter, option, widget); + adjustForItem(); + QGraphicsBlurEffect::draw(painter); } diff --git a/examples/graphicsview/blurpicker/blureffect.h b/examples/graphicsview/blurpicker/blureffect.h index cafd910..2aea8bf 100644 --- a/examples/graphicsview/blurpicker/blureffect.h +++ b/examples/graphicsview/blurpicker/blureffect.h @@ -48,21 +48,20 @@ class BlurEffect: public QGraphicsBlurEffect { public: - BlurEffect(); + BlurEffect(QGraphicsItem *item); void setBaseLine(qreal y) { m_baseLine = y; } - QRectF boundingRectFor(const QGraphicsItem *item); + QRectF boundingRect() const; - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); + void draw(QPainter *painter); private: - void adjustForItem(const QGraphicsItem *item); + void adjustForItem(); private: qreal m_baseLine; + QGraphicsItem *item; }; #endif // BLUREFFECT_H diff --git a/examples/graphicsview/blurpicker/blurpicker.cpp b/examples/graphicsview/blurpicker/blurpicker.cpp index 10ce44f..9904dfa 100644 --- a/examples/graphicsview/blurpicker/blurpicker.cpp +++ b/examples/graphicsview/blurpicker/blurpicker.cpp @@ -79,7 +79,7 @@ void BlurPicker::updateIconPositions() pos -= QPointF(40, 40); icon->setPos(pos); baseline = qMax(baseline, ys); - static_cast(icon->effect())->setBaseLine(baseline); + static_cast(icon->graphicsEffect())->setBaseLine(baseline); } m_scene.update(); @@ -103,7 +103,7 @@ void BlurPicker::setupScene() QPixmap pixmap(names[i]); QGraphicsPixmapItem *icon = m_scene.addPixmap(pixmap); icon->setZValue(1); - icon->setGraphicsEffect(new BlurEffect); + icon->setGraphicsEffect(new BlurEffect(icon)); m_icons << icon; } diff --git a/examples/graphicsview/lighting/lighting.cpp b/examples/graphicsview/lighting/lighting.cpp index fff2204..63f0a6c 100644 --- a/examples/graphicsview/lighting/lighting.cpp +++ b/examples/graphicsview/lighting/lighting.cpp @@ -98,7 +98,7 @@ void Lighting::setupScene() item->setPen(QPen(Qt::black)); item->setBrush(QBrush(Qt::white)); - item->setGraphicsEffect(new ShadowEffect(m_lightSource)); + item->setGraphicsEffect(new ShadowEffect(item, m_lightSource)); item->setZValue(1); item->setPos(i * 80, j * 80); m_scene.addItem(item); diff --git a/examples/graphicsview/lighting/shadoweffect.cpp b/examples/graphicsview/lighting/shadoweffect.cpp index c1d384a..e2dd864 100644 --- a/examples/graphicsview/lighting/shadoweffect.cpp +++ b/examples/graphicsview/lighting/shadoweffect.cpp @@ -43,14 +43,14 @@ #include -ShadowEffect::ShadowEffect(QGraphicsItem *source) +ShadowEffect::ShadowEffect(QGraphicsItem *item, QGraphicsItem *source) : QGraphicsShadowEffect() - , m_lightSource(source) + , item(item), m_lightSource(source) { setBlurRadius(8); } -void ShadowEffect::adjustForItem(const QGraphicsItem *item) +void ShadowEffect::adjustForItem() { QPointF delta = item->pos() - m_lightSource->pos(); setShadowOffset(delta.toPoint() / 30); @@ -61,15 +61,14 @@ void ShadowEffect::adjustForItem(const QGraphicsItem *item) setOpacity(qBound(0.4, 1 - dd / 200.0, 0.7)); } -QRectF ShadowEffect::boundingRectFor(const QGraphicsItem *item) +QRectF ShadowEffect::boundingRect() const { - adjustForItem(item); - return QGraphicsShadowEffect::boundingRectFor(item); + const_cast(this)->adjustForItem(); + return QGraphicsShadowEffect::boundingRect(); } -void ShadowEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void ShadowEffect::draw(QPainter *painter) { - adjustForItem(item); - QGraphicsShadowEffect::drawItem(item, painter, option, widget); + adjustForItem(); + QGraphicsShadowEffect::draw(painter); } diff --git a/examples/graphicsview/lighting/shadoweffect.h b/examples/graphicsview/lighting/shadoweffect.h index 09b63e3..d4aa440 100644 --- a/examples/graphicsview/lighting/shadoweffect.h +++ b/examples/graphicsview/lighting/shadoweffect.h @@ -48,18 +48,17 @@ class ShadowEffect: public QGraphicsShadowEffect { public: - ShadowEffect(QGraphicsItem *source); + ShadowEffect(QGraphicsItem *item, QGraphicsItem *source); - QRectF boundingRectFor(const QGraphicsItem *item); + QRectF boundingRect() const; - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); + void draw(QPainter *painter); private: - void adjustForItem(const QGraphicsItem *item); + void adjustForItem(); private: + QGraphicsItem *item; QGraphicsItem *m_lightSource; }; diff --git a/src/gui/graphicsview/qgraphicseffect.cpp b/src/gui/graphicsview/qgraphicseffect.cpp index c173d1b..8d2c416 100644 --- a/src/gui/graphicsview/qgraphicseffect.cpp +++ b/src/gui/graphicsview/qgraphicseffect.cpp @@ -104,83 +104,90 @@ QGraphicsEffect::QGraphicsEffect() : QObject(*new QGraphicsEffectPrivate, 0) +{} + +/*! + \internal +*/ +QGraphicsEffect::QGraphicsEffect(QGraphicsEffectPrivate &dd) + : QObject(dd, 0) +{} + +QGraphicsEffect::~QGraphicsEffect() { Q_D(QGraphicsEffect); - d->parentItem = 0; + d->setGraphicsEffectSource(0); } -QGraphicsEffect::~QGraphicsEffect() +QRectF QGraphicsEffect::boundingRect() const { + return sourceBoundingRect(); } -QRectF QGraphicsEffect::boundingRectFor(const QGraphicsItem *item) +QRectF QGraphicsEffect::sourceBoundingRect() const { - // 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(); + Q_D(const QGraphicsEffect); + if (d->source) + return d->source->boundingRect(); + return QRectF(); } -/*! \internal -*/ -QGraphicsEffect::QGraphicsEffect(QGraphicsEffectPrivate &dd) - : QObject(dd, 0) +void QGraphicsEffect::drawSource(QPainter *painter) { Q_D(QGraphicsEffect); - d->parentItem = 0; + if (d->source) + d->source->draw(painter); } -// 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) +bool QGraphicsEffect::drawSourceIntoPixmap(QPixmap *pixmap, const QTransform &itemToPixmapTransform) { - if (!item->scene()) - return 0; - return item->scene()->drawItemOnPixmap(painter, item, option, widget, flags); + Q_D(QGraphicsEffect); + if (d->source) + return d->source->drawIntoPixmap(pixmap, itemToPixmapTransform); + return false; } QGraphicsGrayscaleEffect::QGraphicsGrayscaleEffect() : QGraphicsEffect(*new QGraphicsGrayscaleEffectPrivate) -{ -} +{} QGraphicsGrayscaleEffect::~QGraphicsGrayscaleEffect() -{ -} +{} -void QGraphicsGrayscaleEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void QGraphicsGrayscaleEffect::draw(QPainter *painter) { Q_D(QGraphicsGrayscaleEffect); + if (!d->source) + return; // Find the item's bounds in device coordinates. - QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect()); + QTransform itemToPixmapTransform(painter->worldTransform()); + QRectF deviceBounds = itemToPixmapTransform.mapRect(d->source->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) + if (deviceRect.x() != 0 || deviceRect.y() != 0) + itemToPixmapTransform *= QTransform::fromTranslate(-deviceRect.x(), -deviceRect.y()); + + QPixmap pixmap(deviceRect.size()); + if (!d->source->drawIntoPixmap(&pixmap, itemToPixmapTransform)) return; // Draw the pixmap with the filter using an untransformed painter. QTransform restoreTransform = painter->worldTransform(); painter->setWorldTransform(QTransform()); - d->filter->draw(painter, deviceRect.topLeft(), *pixmap, pixmap->rect()); + d->filter->draw(painter, deviceRect.topLeft(), pixmap, pixmap.rect()); painter->setWorldTransform(restoreTransform); + } QGraphicsColorizeEffect::QGraphicsColorizeEffect() : QGraphicsEffect(*new QGraphicsColorizeEffectPrivate) -{ -} +{} QGraphicsColorizeEffect::~QGraphicsColorizeEffect() -{ -} +{} QColor QGraphicsColorizeEffect::color() const { @@ -194,25 +201,30 @@ void QGraphicsColorizeEffect::setColor(const QColor &c) d->filter->setColor(c); } -void QGraphicsColorizeEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void QGraphicsColorizeEffect::draw(QPainter *painter) { Q_D(QGraphicsColorizeEffect); + if (!d->source) + return; // Find the item's bounds in device coordinates. - QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect()); + QTransform itemToPixmapTransform(painter->worldTransform()); + QRectF deviceBounds = itemToPixmapTransform.mapRect(d->source->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) + if (deviceRect.x() != 0 || deviceRect.y() != 0) + itemToPixmapTransform *= QTransform::fromTranslate(-deviceRect.x(), -deviceRect.y()); + + QPixmap pixmap(deviceRect.size()); + if (!d->source->drawIntoPixmap(&pixmap, itemToPixmapTransform)) return; // Draw the pixmap with the filter using an untransformed painter. QTransform restoreTransform = painter->worldTransform(); painter->setWorldTransform(QTransform()); - d->filter->draw(painter, deviceRect.topLeft(), *pixmap, pixmap->rect()); + d->filter->draw(painter, deviceRect.topLeft(), pixmap, pixmap.rect()); painter->setWorldTransform(restoreTransform); } @@ -237,23 +249,28 @@ void QGraphicsPixelizeEffect::setPixelSize(int size) d->pixelSize = size; } -void QGraphicsPixelizeEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void QGraphicsPixelizeEffect::draw(QPainter *painter) { Q_D(QGraphicsPixelizeEffect); + if (!d->source) + return; // Find the item's bounds in device coordinates. - QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect()); + QTransform itemToPixmapTransform(painter->worldTransform()); + QRectF deviceBounds = itemToPixmapTransform.mapRect(d->source->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) + if (deviceRect.x() != 0 || deviceRect.y() != 0) + itemToPixmapTransform *= QTransform::fromTranslate(-deviceRect.x(), -deviceRect.y()); + + QPixmap pixmap(deviceRect.size()); + if (!d->source->drawIntoPixmap(&pixmap)) return; // pixelize routine - QImage img = pixmap->toImage().convertToFormat(QImage::Format_ARGB32); + QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32); if (d->pixelSize > 0) { int width = img.width(); int height = img.height(); @@ -365,31 +382,42 @@ void QGraphicsBlurEffect::setBlurRadius(int radius) d->filter->setBlurRadius(radius); } -QRectF QGraphicsBlurEffect::boundingRectFor(const QGraphicsItem *item) +QRectF QGraphicsBlurEffect::boundingRect() const { Q_D(const QGraphicsBlurEffect); - return d->filter->boundingRectFor(item->boundingRect()); + if (d->source) + return d->filter->boundingRectFor(d->source->boundingRect()); + return QRectF(); } -void QGraphicsBlurEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void QGraphicsBlurEffect::draw(QPainter *painter) { Q_D(QGraphicsBlurEffect); + if (!d->source) + return; // Find the item's bounds in device coordinates. - QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect()); + QTransform itemToPixmapTransform(painter->worldTransform()); + QRectF deviceBounds = itemToPixmapTransform.mapRect(d->source->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) + // ### Potentially big rect; must be clipped to the viewport rect. + const qreal delta = d->blurRadius * 3; + const QRect effectRect = deviceBounds.adjusted(-delta, -delta, delta, delta) + .toRect().adjusted(-1, -1, 1, 1); + if (effectRect.x() != 0 || effectRect.y() != 0) + itemToPixmapTransform *= QTransform::fromTranslate(-effectRect.x(), -effectRect.y()); + + QPixmap pixmap(effectRect.size()); + if (!d->source->drawIntoPixmap(&pixmap, itemToPixmapTransform)) return; // Draw the pixmap with the filter using an untransformed painter. QTransform restoreTransform = painter->worldTransform(); painter->setWorldTransform(QTransform()); - d->filter->draw(painter, deviceRect.topLeft(), *pixmap, pixmap->rect()); + d->filter->draw(painter, deviceRect.topLeft(), pixmap, pixmap.rect()); painter->setWorldTransform(restoreTransform); } @@ -426,13 +454,13 @@ void QGraphicsBloomEffect::setOpacity(qreal alpha) d->opacity = alpha; } -QRectF QGraphicsBloomEffect::boundingRectFor(const QGraphicsItem *item) +QRectF QGraphicsBloomEffect::boundingRect() const { - Q_D(QGraphicsBloomEffect); + Q_D(const QGraphicsBloomEffect); + if (!d->source) + return QRectF(); qreal delta = d->blurRadius * 3; - QRectF blurRect = item->boundingRect(); - blurRect.adjust(-delta, -delta, delta, delta); - return blurRect; + return d->source->boundingRect().adjusted(-delta, -delta, delta, delta); } // Change brightness (positive integer) of each pixel @@ -466,24 +494,29 @@ static QImage composited(const QImage& img1, const QImage& img2, qreal opacity, return result; } -void QGraphicsBloomEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void QGraphicsBloomEffect::draw(QPainter *painter) { Q_D(QGraphicsBloomEffect); + if (!d->source) + return; // Find the item's bounds in device coordinates. - QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect()); + QTransform itemToPixmapTransform(painter->worldTransform()); + QRectF deviceBounds = itemToPixmapTransform.mapRect(d->source->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) + if (deviceRect.x() != 0 || deviceRect.y() != 0) + itemToPixmapTransform *= QTransform::fromTranslate(-deviceRect.x(), -deviceRect.y()); + + QPixmap pixmap(deviceRect.size()); + if (!d->source->drawIntoPixmap(&pixmap, itemToPixmapTransform)) return; // bloom routine int radius = d->blurRadius; - QImage img = pixmap->toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); + 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); @@ -540,31 +573,35 @@ void QGraphicsFrameEffect::setFrameOpacity(qreal opacity) d->alpha = opacity; } -QRectF QGraphicsFrameEffect::boundingRectFor(const QGraphicsItem *item) +QRectF QGraphicsFrameEffect::boundingRect() const { - Q_D(QGraphicsFrameEffect); - QRectF frameRect = item->boundingRect(); - frameRect.adjust(-d->width, -d->width, d->width, d->width); - return frameRect; + Q_D(const QGraphicsFrameEffect); + if (!d->source) + return QRectF(); + return d->source->boundingRect().adjusted(-d->width, -d->width, d->width, d->width); } -void QGraphicsFrameEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void QGraphicsFrameEffect::draw(QPainter *painter) { Q_D(QGraphicsFrameEffect); + if (!d->source) + return; // 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()) + QTransform itemToPixmapTransform(painter->worldTransform()); + QRectF deviceBounds = itemToPixmapTransform.mapRect(d->source->boundingRect()); + if (deviceBounds.isEmpty()) return; - QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0); - if (!pixmap) - return; + QRect frameRect = deviceBounds.adjusted(-d->width, -d->width, d->width, d->width) + .toRect().adjusted(-1, -1, 1, 1); + + if (frameRect.x() != 0 || frameRect.y() != 0) + itemToPixmapTransform *= QTransform::fromTranslate(-frameRect.x(), -frameRect.y()); - QRectF frameRect = deviceBounds; - frameRect.adjust(-d->width, -d->width, d->width, d->width); + QPixmap pixmap(frameRect.size()); + if (!d->source->drawIntoPixmap(&pixmap, itemToPixmapTransform)) + return; painter->save(); painter->setWorldTransform(QTransform()); @@ -576,7 +613,7 @@ void QGraphicsFrameEffect::drawItem(QGraphicsItem *item, QPainter *painter, painter->drawRoundedRect(frameRect, 20, 20, Qt::RelativeSize); painter->restore(); - painter->drawPixmap(frameRect.topLeft(), *pixmap); + painter->drawPixmap(frameRect.topLeft(), pixmap); painter->restore(); } @@ -626,25 +663,30 @@ void QGraphicsShadowEffect::setOpacity(qreal opacity) d->alpha = opacity; } -QRectF QGraphicsShadowEffect::boundingRectFor(const QGraphicsItem *item) +QRectF QGraphicsShadowEffect::boundingRect() const { - Q_D(QGraphicsShadowEffect); - QRectF shadowRect = item->boundingRect(); + Q_D(const QGraphicsShadowEffect); + if (!d->source) + return QRectF(); + const QRectF srcBrect = d->source->boundingRect(); + QRectF shadowRect = srcBrect; shadowRect.adjust(d->offset.x(), d->offset.y(), d->offset.x(), d->offset.y()); QRectF blurRect = shadowRect; qreal delta = d->radius * 3; blurRect.adjust(-delta, -delta, delta, delta); - QRectF totalRect = blurRect.united(item->boundingRect()); + QRectF totalRect = blurRect.united(srcBrect); return totalRect; } -void QGraphicsShadowEffect::drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget) +void QGraphicsShadowEffect::draw(QPainter *painter) { Q_D(QGraphicsShadowEffect); + if (!d->source) + return; // Find the item's bounds in device coordinates. - QRectF deviceBounds = painter->worldTransform().mapRect(item->boundingRect()); + QTransform itemToPixmapTransform(painter->worldTransform()); + QRectF deviceBounds = itemToPixmapTransform.mapRect(d->source->boundingRect()); QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1); if (deviceRect.isEmpty()) return; @@ -654,13 +696,16 @@ void QGraphicsShadowEffect::drawItem(QGraphicsItem *item, QPainter *painter, QRectF blurRect = shadowRect; qreal delta = d->radius * 3; blurRect.adjust(-delta, -delta, delta, delta); - QRectF totalRect = blurRect.united(deviceRect); + QRect totalRect = blurRect.united(deviceRect).toRect().adjusted(-1, -1, 1, 1); - QPixmap *pixmap = QGraphicsEffect::drawItemOnPixmap(painter, item, option, widget, 0); - if (!pixmap) + if (totalRect.x() != 0 || totalRect.y() != 0) + itemToPixmapTransform *= QTransform::fromTranslate(-totalRect.x(), -totalRect.y()); + + QPixmap pixmap(totalRect.size()); + if (!d->source->drawIntoPixmap(&pixmap, itemToPixmapTransform)) return; - QImage img = pixmap->toImage(); + QImage img = pixmap.toImage(); QImage shadowImage(img.size(), QImage::Format_ARGB32); shadowImage.fill(qRgba(0, 0, 0, d->alpha * 255)); shadowImage.setAlphaChannel(img.alphaChannel()); @@ -682,10 +727,9 @@ void QGraphicsShadowEffect::drawItem(QGraphicsItem *item, QPainter *painter, qreal itemx = qMin(blurRect.x(), deviceBounds.x()); qreal itemy = qMin(blurRect.y(), deviceBounds.y()); - painter->drawPixmap(itemx, itemy, *pixmap); + painter->drawPixmap(itemx, itemy, pixmap); painter->setWorldTransform(restoreTransform); } - #endif diff --git a/src/gui/graphicsview/qgraphicseffect.h b/src/gui/graphicsview/qgraphicseffect.h index 9f6e342..74a8430 100644 --- a/src/gui/graphicsview/qgraphicseffect.h +++ b/src/gui/graphicsview/qgraphicseffect.h @@ -46,6 +46,7 @@ #include #include #include +#include #include QT_FORWARD_DECLARE_CLASS(QGraphicsItem); @@ -71,18 +72,17 @@ 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; + virtual QRectF boundingRect() const; protected: QGraphicsEffect(QGraphicsEffectPrivate &d); - QPixmap* drawItemOnPixmap(QPainter *painter, QGraphicsItem *item, - const QStyleOptionGraphicsItem *option, QWidget *widget, int flags); + virtual void draw(QPainter *painter) = 0; + void drawSource(QPainter *painter); + bool drawSourceIntoPixmap(QPixmap *pixmap, const QTransform &xform = QTransform()); + QRectF sourceBoundingRect() const; private: + friend class QGraphicsScenePrivate; friend class QGraphicsItem; Q_DECLARE_PRIVATE(QGraphicsEffect) Q_DISABLE_COPY(QGraphicsEffect) @@ -96,9 +96,8 @@ public: QGraphicsGrayscaleEffect(); ~QGraphicsGrayscaleEffect(); - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); +protected: + void draw(QPainter *painter); private: Q_DECLARE_PRIVATE(QGraphicsGrayscaleEffect) @@ -115,9 +114,8 @@ public: QColor color() const; void setColor(const QColor &c); - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); +protected: + void draw(QPainter *painter); private: Q_DECLARE_PRIVATE(QGraphicsColorizeEffect) @@ -134,9 +132,8 @@ public: int pixelSize() const; void setPixelSize(int pixelSize); - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); +protected: + void draw(QPainter *painter); private: Q_DECLARE_PRIVATE(QGraphicsPixelizeEffect) @@ -153,11 +150,10 @@ public: int blurRadius() const; void setBlurRadius(int blurRadius); - QRectF boundingRectFor(const QGraphicsItem *item); + QRectF boundingRect() const; - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); +protected: + void draw(QPainter *painter); private: Q_DECLARE_PRIVATE(QGraphicsBlurEffect) @@ -177,11 +173,10 @@ public: qreal opacity() const; void setOpacity(qreal opacity); - QRectF boundingRectFor(const QGraphicsItem *item); + QRectF boundingRect() const; - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); +protected: + void draw(QPainter *painter); private: Q_DECLARE_PRIVATE(QGraphicsBloomEffect) @@ -204,11 +199,11 @@ public: qreal frameOpacity() const; void setFrameOpacity(qreal opacity); - QRectF boundingRectFor(const QGraphicsItem *item); + QRectF boundingRect() const; + +protected: + void draw(QPainter *painter); - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); private: Q_DECLARE_PRIVATE(QGraphicsFrameEffect) Q_DISABLE_COPY(QGraphicsFrameEffect) @@ -232,11 +227,10 @@ public: qreal opacity() const; void setOpacity(qreal opacity); - QRectF boundingRectFor(const QGraphicsItem *item); + QRectF boundingRect() const; - void drawItem(QGraphicsItem *item, QPainter *painter, - const QStyleOptionGraphicsItem *option = 0, - QWidget *widget = 0); +protected: + void draw(QPainter *painter); private: Q_DECLARE_PRIVATE(QGraphicsShadowEffect) diff --git a/src/gui/graphicsview/qgraphicseffect_p.h b/src/gui/graphicsview/qgraphicseffect_p.h index 999e013..9204789 100644 --- a/src/gui/graphicsview/qgraphicseffect_p.h +++ b/src/gui/graphicsview/qgraphicseffect_p.h @@ -61,12 +61,31 @@ QT_BEGIN_NAMESPACE +class QGraphicsEffectSource +{ +public: + QGraphicsEffectSource() {} + virtual ~QGraphicsEffectSource() {} + virtual void detach() = 0; + virtual QRectF boundingRect() = 0; + virtual void draw(QPainter *p) = 0; + virtual bool drawIntoPixmap(QPixmap *, const QTransform & = QTransform()) = 0; +}; + class QGraphicsEffectPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QGraphicsEffect) public: - QGraphicsEffectPrivate(): parentItem(0) {} - QGraphicsItem *parentItem; + QGraphicsEffectPrivate() : source(0) {} + QGraphicsEffectSource *source; + inline void setGraphicsEffectSource(QGraphicsEffectSource *newSource) + { + if (source) { + source->detach(); + delete source; + } + source = newSource; + } }; class QGraphicsGrayscaleEffectPrivate : public QGraphicsEffectPrivate @@ -106,8 +125,10 @@ class QGraphicsBlurEffectPrivate : public QGraphicsEffectPrivate { Q_DECLARE_PUBLIC(QGraphicsBlurEffect) public: - QGraphicsBlurEffectPrivate() : blurRadius(4) {} + QGraphicsBlurEffectPrivate() : filter(new QPixmapBlurFilter), blurRadius(4) {} + ~QGraphicsBlurEffectPrivate() { delete filter; } + QPixmapBlurFilter *filter; int blurRadius; }; diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 3a350e0..1d271c9 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -552,7 +552,6 @@ #ifndef QT_NO_GRAPHICSVIEW -#include "qgraphicseffect_p.h" #include "qgraphicsscene.h" #include "qgraphicsscene_p.h" #include "qgraphicssceneevent.h" @@ -1180,8 +1179,7 @@ QGraphicsItem::~QGraphicsItem() else d_ptr->setParentItemHelper(0); - QGraphicsEffect *e = graphicsEffect(); - delete e; + delete d_ptr->graphicsEffect; delete d_ptr->transformData; delete d_ptr; @@ -2198,11 +2196,7 @@ void QGraphicsItem::setOpacity(qreal opacity) */ QGraphicsEffect *QGraphicsItem::graphicsEffect() const { - QGraphicsEffect *fx = 0; - if (d_ptr->hasEffect) - fx = d_ptr->extra(QGraphicsItemPrivate::ExtraEffect).value(); - - return fx; + return d_ptr->graphicsEffect; } /*! @@ -2220,30 +2214,29 @@ QGraphicsEffect *QGraphicsItem::graphicsEffect() const */ void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect) { - if (effect) { - if (QGraphicsEffect *currentEffect = this->graphicsEffect()) { - if (currentEffect != effect) { - qWarning("QGraphicsItem::setEffect: Attempting to set QGraphicsEffect " - "%p on %p, which already has an effect", effect, this); - } - return; - } + if (d_ptr->graphicsEffect == effect) + return; - if (effect->d_func()->parentItem) - effect->d_func()->parentItem->setGraphicsEffect(0); - effect->d_func()->parentItem = this; - d_ptr->hasEffect = true; - d_ptr->setExtra(QGraphicsItemPrivate::ExtraEffect, QVariant::fromValue(effect)); + if (d_ptr->graphicsEffect && effect) { + 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. } else { - d_ptr->hasEffect = false; - d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraEffect); - void *ptr = d_ptr->extra(QGraphicsItemPrivate::ExtraEffectPixmap).value(); - QPixmap *pixmap = reinterpret_cast(ptr); - delete pixmap; - d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraEffectPixmap); + // Set new effect. + effect->d_func()->setGraphicsEffectSource(new QGraphicsItemEffectSource(this)); + d_ptr->graphicsEffect = effect; } - update(); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this); } /*! @@ -2257,10 +2250,8 @@ void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect) */ QRectF QGraphicsItem::effectiveBoundingRect() const { - QGraphicsEffect *fx = graphicsEffect(); - if (fx) - return fx->boundingRectFor(this); - + if (d_ptr->graphicsEffect) + return d_ptr->graphicsEffect->boundingRect(); return boundingRect(); } @@ -2294,42 +2285,6 @@ QRectF QGraphicsItem::sceneEffectiveBoundingRect() const } /*! - \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(); - QPixmap *pixmap = reinterpret_cast(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(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 0beb07c..0c0b341 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -440,8 +440,6 @@ 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 09f296a..dc875f7 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 @@ -103,8 +104,6 @@ public: ExtraCacheData, ExtraMaxDeviceCoordCacheSize, ExtraBoundingRegionGranularity, - ExtraEffect, - ExtraEffectPixmap, ExtraGestures }; @@ -122,6 +121,7 @@ public: scene(0), parent(0), transformData(0), + graphicsEffect(0), index(-1), siblingIndex(-1), depth(0), @@ -151,7 +151,6 @@ public: allChildrenDirty(0), fullUpdatePending(0), flags(0), - hasEffect(0), dirtyChildrenBoundingRect(1), paintedViewBoundingRectsNeedRepaint(0), dirtySceneTransform(1), @@ -409,6 +408,7 @@ public: QList children; struct TransformData; TransformData *transformData; + QGraphicsEffect *graphicsEffect; QTransform sceneTransform; int index; int siblingIndex; @@ -443,7 +443,6 @@ public: // New 32 bits quint32 fullUpdatePending : 1; quint32 flags : 12; - quint32 hasEffect : 1; quint32 dirtyChildrenBoundingRect : 1; quint32 paintedViewBoundingRectsNeedRepaint : 1; quint32 dirtySceneTransform : 1; @@ -511,6 +510,44 @@ struct QGraphicsItemPrivate::TransformData { } }; +class QGraphicsItemEffectSource : public QGraphicsEffectSource +{ +public: + QGraphicsItemEffectSource(QGraphicsItem *i) + : QGraphicsEffectSource(), item(i), option(0), widget(0) + {} + + inline void detach() + { item->setGraphicsEffect(0); } + + inline QRectF boundingRect() + { return item->boundingRect(); } + + inline void draw(QPainter *painter) + { item->paint(painter, option, widget); } + + inline bool drawIntoPixmap(QPixmap *pixmap, const QTransform &itemToPixmapTransform) + { + pixmap->fill(Qt::transparent); + QPainter pixmapPainter(pixmap); + if (!itemToPixmapTransform.isIdentity()) + pixmapPainter.setWorldTransform(itemToPixmapTransform); + item->paint(&pixmapPainter, option, widget); + return true; + } + + inline void setPaintInfo(const QStyleOptionGraphicsItem *o, QWidget *w) + { option = o; widget = w; } + + void resetPaintInfo() + { option = 0; widget = 0; } + +private: + QGraphicsItem *item; + const QStyleOptionGraphicsItem *option; + QWidget *widget; +}; + /*! \internal */ diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index c56f34c..f4fd4ce 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -3928,8 +3928,8 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte #endif // Render using effect, works now only for no cache mode - if (noCache && itemd->hasEffect && item->graphicsEffect()) { - item->graphicsEffect()->drawItem(item, painter, option, widget); + if (noCache && itemd->graphicsEffect) { + itemd->graphicsEffect->draw(painter); return; } @@ -4228,62 +4228,6 @@ 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; -} - void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const viewTransform, QRegion *exposedRegion, QWidget *widget) { @@ -4412,11 +4356,21 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * painter->setClipPath(item->shape(), Qt::IntersectClip); painter->setOpacity(opacity); - if (!item->d_ptr->cacheMode && !item->d_ptr->isWidget && !item->d_ptr->hasEffect) + QGraphicsItemEffectSource *source = item->d_ptr->graphicsEffect + ? static_cast + (item->d_ptr->graphicsEffect->d_func()->source) + : 0; + if (source) + source->setPaintInfo(&styleOptionTmp, widget); + + if (!item->d_ptr->cacheMode && !item->d_ptr->isWidget && !source) item->paint(painter, &styleOptionTmp, widget); else drawItemHelper(item, painter, &styleOptionTmp, widget, painterStateProtection); + if (source) + source->resetPaintInfo(); + if (savePainter) painter->restore(); } diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 68bd3e1..1606053 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -281,11 +281,6 @@ 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); -- cgit v0.12