From c39fac87d62ef15867dc5571d03530d7e7240aa7 Mon Sep 17 00:00:00 2001
From: Gunnar Sletta <gunnar@trolltech.com>
Date: Mon, 26 Oct 2009 10:39:38 +0100
Subject: Options on how to get a pixmap from an effect source

Usable for future optimizations.

Reviewed-by: Samuel
---
 src/gui/effects/qgraphicseffect.cpp                | 22 +++--
 src/gui/effects/qgraphicseffect.h                  | 10 ++-
 src/gui/effects/qgraphicseffect_p.h                |  3 +-
 src/gui/graphicsview/qgraphicsitem.cpp             | 31 +++++--
 src/gui/graphicsview/qgraphicsitem_p.h             |  9 +-
 src/gui/kernel/qwidget.cpp                         | 18 +++-
 src/gui/kernel/qwidget_p.h                         |  3 +-
 .../tst_qgraphicseffectsource.cpp                  | 99 ++++++++++++++++++++++
 8 files changed, 177 insertions(+), 18 deletions(-)

diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp
index f20480b..647fd1b 100644
--- a/src/gui/effects/qgraphicseffect.cpp
+++ b/src/gui/effects/qgraphicseffect.cpp
@@ -97,6 +97,7 @@
 */
 
 #include "qgraphicseffect_p.h"
+#include <QtGui/qgraphicsitem.h>
 
 #include <QtGui/qimage.h>
 #include <QtGui/qpainter.h>
@@ -248,16 +249,22 @@ bool QGraphicsEffectSource::isPixmap() const
 
     \sa QGraphicsEffect::draw(), boundingRect(), deviceRect()
 */
-QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offset) const
+QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offset, PixmapPadMode mode) const
 {
     Q_D(const QGraphicsEffectSource);
 
+    // Shortcut, no cache for childless pixmap items...
+    const QGraphicsItem *item = graphicsItem();
+    if (system == Qt::LogicalCoordinates && mode == NoExpandPadMode && item && isPixmap()) {
+        return ((QGraphicsPixmapItem *) item)->pixmap();
+    }
+
     QPixmap pm;
     if (d->m_cachedSystem == system)
         QPixmapCache::find(d->m_cacheKey, &pm);
 
     if (pm.isNull()) {
-        pm = d->pixmap(system, &d->m_cachedOffset);
+        pm = d->pixmap(system, &d->m_cachedOffset, mode);
         d->m_cachedSystem = system;
 
         d->invalidateCache();
@@ -565,7 +572,8 @@ void QGraphicsColorizeEffect::draw(QPainter *painter, QGraphicsEffectSource *sou
     QPoint offset;
     if (source->isPixmap()) {
         // No point in drawing in device coordinates (pixmap will be scaled anyways).
-        const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+        const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset,
+                                              QGraphicsEffectSource::NoExpandPadMode);
         d->filter->draw(painter, offset, pixmap);
         return;
     }
@@ -776,6 +784,8 @@ void QGraphicsDropShadowEffect::setOffset(const QPointF &offset)
 
     By default, the horizontal shadow offset is 8 pixels.
 
+
+
     \sa yOffset(), offset()
 */
 
@@ -1029,7 +1039,8 @@ void QGraphicsOpacityEffect::draw(QPainter *painter, QGraphicsEffectSource *sour
     if (source->isPixmap()) {
         // No point in drawing in device coordinates (pixmap will be scaled anyways).
         if (!d->hasOpacityMask) {
-            const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset);
+            const QPixmap pixmap = source->pixmap(Qt::LogicalCoordinates, &offset,
+                                                  QGraphicsEffectSource::NoExpandPadMode);
             painter->drawPixmap(offset, pixmap);
         } else {
             QRect srcBrect = source->boundingRect().toAlignedRect();
@@ -1050,7 +1061,8 @@ void QGraphicsOpacityEffect::draw(QPainter *painter, QGraphicsEffectSource *sour
     } else {
         // Draw pixmap in device coordinates to avoid pixmap scaling;
         if (!d->hasOpacityMask) {
-            const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+            const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset,
+                                                  QGraphicsEffectSource::NoExpandPadMode);
             painter->setWorldTransform(QTransform());
             painter->drawPixmap(offset, pixmap);
         } else {
diff --git a/src/gui/effects/qgraphicseffect.h b/src/gui/effects/qgraphicseffect.h
index 019e808..abf03b3 100644
--- a/src/gui/effects/qgraphicseffect.h
+++ b/src/gui/effects/qgraphicseffect.h
@@ -64,6 +64,12 @@ class Q_GUI_EXPORT QGraphicsEffectSource : public QObject
 {
     Q_OBJECT
 public:
+    enum PixmapPadMode {
+        NoExpandPadMode,
+        ExpandToTransparentBorderPadMode,
+        ExpandToEffectRectPadMode
+    };
+
     ~QGraphicsEffectSource();
     const QGraphicsItem *graphicsItem() const;
     const QWidget *widget() const;
@@ -75,7 +81,9 @@ public:
 
     QRectF boundingRect(Qt::CoordinateSystem coordinateSystem = Qt::LogicalCoordinates) const;
     QRect deviceRect() const;
-    QPixmap pixmap(Qt::CoordinateSystem system = Qt::LogicalCoordinates, QPoint *offset = 0) const;
+    QPixmap pixmap(Qt::CoordinateSystem system = Qt::LogicalCoordinates,
+                   QPoint *offset = 0,
+                   PixmapPadMode mode = ExpandToEffectRectPadMode) const;
 
 protected:
     QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent = 0);
diff --git a/src/gui/effects/qgraphicseffect_p.h b/src/gui/effects/qgraphicseffect_p.h
index 24d8696..dff84a1 100644
--- a/src/gui/effects/qgraphicseffect_p.h
+++ b/src/gui/effects/qgraphicseffect_p.h
@@ -77,7 +77,8 @@ public:
     virtual void draw(QPainter *p) = 0;
     virtual void update() = 0;
     virtual bool isPixmap() const = 0;
-    virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0) const = 0;
+    virtual QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset = 0,
+                           QGraphicsEffectSource::PixmapPadMode mode = QGraphicsEffectSource::ExpandToTransparentBorderPadMode) const = 0;
     virtual void effectBoundingRectChanged() = 0;
     void invalidateCache() const { QPixmapCache::remove(m_cacheKey); }
 
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index f892bb4..97357a7 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -1657,7 +1657,7 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags)
         d_ptr->scene->d_func()->index->itemChange(this, ItemFlagsChange, quint32(flags));
 
     // Flags that alter the geometry of the item (or its children).
-    const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations);
+    const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations | ItemIsSelectable);
     bool fullUpdate = (quint32(flags) & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask);
     if (fullUpdate)
         d_ptr->paintedViewBoundingRectsNeedRepaint = 1;
@@ -9138,10 +9138,14 @@ void QGraphicsPixmapItem::setOffset(const QPointF &offset)
 QRectF QGraphicsPixmapItem::boundingRect() const
 {
     Q_D(const QGraphicsPixmapItem);
-    qreal pw = 1.0;
     if (d->pixmap.isNull())
         return QRectF();
-    return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2);
+    if (d->flags & ItemIsSelectable) {
+        qreal pw = 1.0;
+        return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2);
+    } else {
+        return QRectF(d->offset, d->pixmap.size());
+    }
 }
 
 /*!
@@ -10660,7 +10664,8 @@ void QGraphicsItemEffectSourcePrivate::draw(QPainter *painter)
     }
 }
 
-QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const
+QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset,
+                                                 QGraphicsEffectSource::PixmapPadMode mode) const
 {
     const bool deviceCoordinates = (system == Qt::DeviceCoordinates);
     if (!info && deviceCoordinates) {
@@ -10674,7 +10679,16 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP
     QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func();
 
     const QRectF sourceRect = boundingRect(system);
-    QRect effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+    QRect effectRect;
+
+    if (mode == QGraphicsEffectSource::ExpandToEffectRectPadMode) {
+        effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+    } else if (mode == QGraphicsEffectSource::ExpandToTransparentBorderPadMode) {
+        effectRect = sourceRect.adjusted(-1, -1, 1, 1).toAlignedRect();
+    } else {
+        effectRect = sourceRect.toAlignedRect();
+    }
+
     if (offset)
         *offset = effectRect.topLeft();
 
@@ -10702,10 +10716,15 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP
             effectRect.setBottom(deviceHeight -1);
 
     }
-
     if (effectRect.isEmpty())
         return QPixmap();
 
+    if (system == Qt::LogicalCoordinates
+        && effectRect.size() == sourceRect.size()
+        && isPixmap()) {
+        return static_cast<QGraphicsPixmapItem *>(item)->pixmap();
+    }
+
     QPixmap pixmap(effectRect.size());
     pixmap.fill(Qt::transparent);
     QPainter pixmapPainter(&pixmap);
diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h
index 7c3c4f0..183e95b 100644
--- a/src/gui/graphicsview/qgraphicsitem_p.h
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -61,6 +61,7 @@
 #include <private/qgraphicstransform_p.h>
 
 #include <private/qgraphicseffect_p.h>
+#include <qgraphicseffect.h>
 
 #include <QtCore/qpoint.h>
 
@@ -603,7 +604,9 @@ public:
 
     inline bool isPixmap() const
     {
-        return (item->type() == QGraphicsPixmapItem::Type);
+        return item->type() == QGraphicsPixmapItem::Type
+               && !(item->flags() & QGraphicsItem::ItemIsSelectable)
+               && item->d_ptr->children.size() == 0;
             //|| (item->d_ptr->isObject && qobject_cast<QFxImage *>(q_func()));
     }
 
@@ -621,7 +624,9 @@ public:
 
     QRectF boundingRect(Qt::CoordinateSystem system) const;
     void draw(QPainter *);
-    QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const;
+    QPixmap pixmap(Qt::CoordinateSystem system,
+                   QPoint *offset,
+                   QGraphicsEffectSource::PixmapPadMode mode) const;
 
     QGraphicsItem *item;
     QGraphicsItemPaintInfo *info;
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp
index 5fa9a92..27e73e0 100644
--- a/src/gui/kernel/qwidget.cpp
+++ b/src/gui/kernel/qwidget.cpp
@@ -5437,7 +5437,8 @@ void QWidgetEffectSourcePrivate::draw(QPainter *painter)
                    context->sharedPainter, context->backingStore);
 }
 
-QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const
+QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset,
+                                           QGraphicsEffectSource::PixmapPadMode mode) const
 {
     const bool deviceCoordinates = (system == Qt::DeviceCoordinates);
     if (!context && deviceCoordinates) {
@@ -5455,7 +5456,20 @@ QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *
         pixmapOffset = painterTransform.map(pixmapOffset);
     }
 
-    QRect effectRect = m_widget->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+
+    QRect effectRect;
+
+    if (mode == QGraphicsEffectSource::ExpandToEffectRectPadMode) {
+        effectRect = m_widget->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+
+    } else if (mode == QGraphicsEffectSource::ExpandToTransparentBorderPadMode) {
+        effectRect = sourceRect.adjusted(-1, -1, 1, 1).toAlignedRect();
+
+    } else {
+        effectRect = sourceRect.toAlignedRect();
+
+    }
+
     if (offset)
         *offset = effectRect.topLeft();
 
diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h
index 159a3f2..616a972 100644
--- a/src/gui/kernel/qwidget_p.h
+++ b/src/gui/kernel/qwidget_p.h
@@ -819,7 +819,8 @@ public:
 
     QRectF boundingRect(Qt::CoordinateSystem system) const;
     void draw(QPainter *p);
-    QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const;
+    QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset,
+                   QGraphicsEffectSource::PixmapPadMode mode) const;
 
     QWidget *m_widget;
     QWidgetPaintContext *context;
diff --git a/tests/auto/qgraphicseffectsource/tst_qgraphicseffectsource.cpp b/tests/auto/qgraphicseffectsource/tst_qgraphicseffectsource.cpp
index 855950b..0635989 100644
--- a/tests/auto/qgraphicseffectsource/tst_qgraphicseffectsource.cpp
+++ b/tests/auto/qgraphicseffectsource/tst_qgraphicseffectsource.cpp
@@ -166,6 +166,9 @@ private slots:
     void deviceRect();
     void pixmap();
 
+    void pixmapPadding_data();
+    void pixmapPadding();
+
 private:
     QGraphicsView *view;
     QGraphicsScene *scene;
@@ -318,6 +321,102 @@ void tst_QGraphicsEffectSource::pixmap()
     QCOMPARE(pixmap1, pixmap2);
 }
 
+class PaddingEffect : public QGraphicsEffect
+{
+public:
+    PaddingEffect(QObject *parent) : QGraphicsEffect(parent)
+    {
+    }
+
+    QRectF boundingRectFor(const QRectF &src) const {
+        return src.adjusted(-10, -10, 10, 10);
+    }
+
+    void draw(QPainter *p, QGraphicsEffectSource *source) {
+        pix = source->pixmap(coordinateMode, &offset, padMode);
+    }
+
+    QPixmap pix;
+    QPoint offset;
+    QGraphicsEffectSource::PixmapPadMode padMode;
+    Qt::CoordinateSystem coordinateMode;
+};
+
+void tst_QGraphicsEffectSource::pixmapPadding_data()
+{
+    QTest::addColumn<int>("coordinateMode");
+    QTest::addColumn<int>("padMode");
+    QTest::addColumn<QSize>("size");
+    QTest::addColumn<QPoint>("offset");
+    QTest::addColumn<uint>("ulPixel");
+
+    QTest::newRow("log,nopad") << int(Qt::LogicalCoordinates)
+                               << int(QGraphicsEffectSource::NoPadMode)
+                               << QSize(10, 10) << QPoint(0, 0)
+                               << 0xffff0000u;
+
+    QTest::newRow("log,transparent") << int(Qt::LogicalCoordinates)
+                                     << int(QGraphicsEffectSource::ExpandToTransparentBorderPadMode)
+                                     << QSize(12, 12) << QPoint(-1, -1)
+                                     << 0x00000000u;
+
+    QTest::newRow("log,effectrect") << int(Qt::LogicalCoordinates)
+                                    << int(QGraphicsEffectSource::ExpandToEffectRectPadMode)
+                                    << QSize(30, 30) << QPoint(-10, -10)
+                                    << 0x00000000u;
+
+    QTest::newRow("dev,nopad") << int(Qt::DeviceCoordinates)
+                               << int(QGraphicsEffectSource::NoPadMode)
+                               << QSize(20, 20) << QPoint(40, 40)
+                               << 0xffff0000u;
+
+    QTest::newRow("dev,transparent") << int(Qt::DeviceCoordinates)
+                                     << int(QGraphicsEffectSource::ExpandToTransparentBorderPadMode)
+                                     << QSize(22, 22) << QPoint(39, 39)
+                                     << 0x00000000u;
+
+    QTest::newRow("dev,effectrect") << int(Qt::DeviceCoordinates)
+                                    << int(QGraphicsEffectSource::ExpandToEffectRectPadMode)
+                                    << QSize(40, 40) << QPoint(30, 30)
+                                    << 0x00000000u;
+
+}
+
+void tst_QGraphicsEffectSource::pixmapPadding()
+{
+    QPixmap dummyTarget(100, 100);
+    QPainter dummyPainter(&dummyTarget);
+    dummyPainter.translate(40, 40);
+    dummyPainter.scale(2, 2);
+
+    QPixmap pm(10, 10);
+    pm.fill(Qt::red);
+
+    QGraphicsScene *scene = new QGraphicsScene();
+    PaddingEffect *effect = new PaddingEffect(scene);
+    QGraphicsPixmapItem *pmItem = new QGraphicsPixmapItem(pm);
+    scene->addItem(pmItem);
+    pmItem->setGraphicsEffect(effect);
+
+    QFETCH(int, coordinateMode);
+    QFETCH(int, padMode);
+    QFETCH(QPoint, offset);
+    QFETCH(QSize, size);
+    QFETCH(uint, ulPixel);
+
+    effect->padMode = (QGraphicsEffectSource::PixmapPadMode) padMode;
+    effect->coordinateMode = (Qt::CoordinateSystem) coordinateMode;
+
+    scene->render(&dummyPainter, scene->itemsBoundingRect(), scene->itemsBoundingRect());
+
+    QCOMPARE(effect->pix.size(), size);
+    QCOMPARE(effect->offset, offset);
+    QCOMPARE(effect->pix.toImage().pixel(0, 0), ulPixel);
+
+    // ### Fix corruption in scene destruction, then enable...
+    // delete scene;
+}
+
 QTEST_MAIN(tst_QGraphicsEffectSource)
 #include "tst_qgraphicseffectsource.moc"
 
-- 
cgit v0.12