1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtWidgets/qgraphicseffect.h>
31#include <QtWidgets/qgraphicsview.h>
32#include <QtWidgets/qgraphicsscene.h>
33#include <QtWidgets/qgraphicsitem.h>
34#include <QtWidgets/qstyleoption.h>
35
36#include <private/qgraphicseffect_p.h>
37
38class CustomItem : public QGraphicsRectItem
39{
40public:
41 CustomItem(qreal x, qreal y, qreal width, qreal height)
42 : QGraphicsRectItem(x, y, width, height), numRepaints(0),
43 m_painter(0)
44 {}
45
46 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
47 {
48 m_painter = painter;
49 ++numRepaints;
50 QGraphicsRectItem::paint(painter, option, widget);
51 }
52
53 void reset()
54 {
55 numRepaints = 0;
56 m_painter = 0;
57 }
58
59 int numRepaints;
60 QPainter *m_painter;
61};
62
63class CustomEffect : public QGraphicsEffect
64{
65public:
66 CustomEffect()
67 : QGraphicsEffect(), numRepaints(0), m_margin(10), m_sourceChanged(false),
68 m_sourceBoundingRectChanged(false), doNothingInDraw(false),
69 storeDeviceDependentStuff(false),
70 m_painter(0), m_source(0)
71 {}
72
73 QRectF boundingRectFor(const QRectF &rect) const
74 { return rect.adjusted(xp1: -m_margin, yp1: -m_margin, xp2: m_margin, yp2: m_margin); }
75
76 void reset()
77 {
78 numRepaints = 0;
79 m_sourceChanged = false;
80 m_sourceBoundingRectChanged = false;
81 m_painter = 0;
82 m_source = 0;
83 deviceCoordinatesPixmap = QPixmap();
84 deviceRect = QRect();
85 sourceDeviceBoundingRect = QRectF();
86 }
87
88 void setMargin(int margin)
89 {
90 m_margin = margin;
91 updateBoundingRect();
92 }
93
94 int margin() const
95 { return m_margin; }
96
97 void draw(QPainter *painter)
98 {
99 ++numRepaints;
100 if (storeDeviceDependentStuff) {
101 deviceCoordinatesPixmap = source()->pixmap(system: Qt::DeviceCoordinates);
102 deviceRect = QRect(0, 0, painter->device()->width(), painter->device()->height());
103 sourceDeviceBoundingRect = source()->boundingRect(coordinateSystem: Qt::DeviceCoordinates);
104 }
105 if (doNothingInDraw)
106 return;
107 m_source = source();
108 m_painter = painter;
109 source()->draw(painter);
110 }
111
112 void sourceChanged(ChangeFlags)
113 { m_sourceChanged = true; }
114
115 void sourceBoundingRectChanged()
116 { m_sourceBoundingRectChanged = true; }
117
118 int numRepaints;
119 int m_margin;
120 bool m_sourceChanged;
121 bool m_sourceBoundingRectChanged;
122 bool doNothingInDraw;
123 bool storeDeviceDependentStuff;
124 QPainter *m_painter;
125 QGraphicsEffectSource *m_source;
126 QPixmap deviceCoordinatesPixmap;
127 QRect deviceRect;
128 QRectF sourceDeviceBoundingRect;
129};
130
131class tst_QGraphicsEffectSource : public QObject
132{
133 Q_OBJECT
134public slots:
135 void initTestCase();
136 void cleanupTestCase();
137 void init();
138
139private slots:
140 void graphicsItem();
141 void styleOption();
142 void isPixmap();
143 void draw();
144 void update();
145 void boundingRect();
146 void clippedBoundingRect();
147 void deviceRect();
148 void pixmap();
149
150 void pixmapPadding_data();
151 void pixmapPadding();
152
153private:
154 QGraphicsView *view;
155 QGraphicsScene *scene;
156 CustomItem *item;
157 CustomEffect *effect;
158};
159
160void tst_QGraphicsEffectSource::initTestCase()
161{
162 scene = new QGraphicsScene;
163 item = new CustomItem(0, 0, 100, 100);
164 effect = new CustomEffect;
165 item->setGraphicsEffect(effect);
166 scene->addItem(item);
167 view = new QGraphicsView(scene);
168 view->show();
169 QVERIFY(QTest::qWaitForWindowExposed(view));
170}
171
172void tst_QGraphicsEffectSource::cleanupTestCase()
173{
174 delete view;
175}
176
177void tst_QGraphicsEffectSource::init()
178{
179 QVERIFY(effect);
180 QVERIFY(item);
181 QVERIFY(effect->source());
182 effect->reset();
183 effect->storeDeviceDependentStuff = false;
184 effect->doNothingInDraw = false;
185 item->reset();
186 QCoreApplication::processEvents(); // Process all queued paint events
187}
188
189void tst_QGraphicsEffectSource::graphicsItem()
190{
191 QVERIFY(effect->source());
192 QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item);
193}
194
195void tst_QGraphicsEffectSource::styleOption()
196{
197 // We don't have style options unless the source is drawing.
198 QVERIFY(effect->source());
199 QVERIFY(!effect->source()->styleOption());
200
201 // Trigger an update and check that the style option in QGraphicsEffect::draw
202 // was the same as the one in QGraphicsItem::paint.
203 QCOMPARE(item->numRepaints, 0);
204 QCOMPARE(effect->numRepaints, 0);
205 item->update();
206 QTRY_COMPARE(item->numRepaints, 1);
207 QTRY_COMPARE(effect->numRepaints, 1);
208}
209
210void tst_QGraphicsEffectSource::isPixmap()
211{
212 // Current source is a CustomItem (which is not a pixmap item).
213 QVERIFY(!effect->source()->isPixmap());
214
215 // Make sure isPixmap() returns true for QGraphicsPixmapItem.
216 QGraphicsPixmapItem *pixmapItem = new QGraphicsPixmapItem;
217 CustomEffect *anotherEffect = new CustomEffect;
218 pixmapItem->setGraphicsEffect(anotherEffect);
219 QVERIFY(anotherEffect->source());
220 QCOMPARE(anotherEffect->source()->graphicsItem(), static_cast<const QGraphicsItem *>(pixmapItem));
221 QVERIFY(anotherEffect->source()->isPixmap());
222 delete pixmapItem;
223}
224
225void tst_QGraphicsEffectSource::draw()
226{
227 // The source can only draw as a result of QGraphicsEffect::draw.
228 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsEffectSource::draw: Can only begin as a result of QGraphicsEffect::draw");
229 QPixmap dummyPixmap(10, 10);
230 QPainter dummyPainter(&dummyPixmap);
231 effect->source()->draw(painter: &dummyPainter);
232}
233
234void tst_QGraphicsEffectSource::update()
235{
236 QCOMPARE(item->numRepaints, 0);
237 QCOMPARE(effect->numRepaints, 0);
238
239 effect->source()->update();
240
241 QTRY_COMPARE(item->numRepaints, 1);
242 QTRY_COMPARE(effect->numRepaints, 1);
243}
244
245void tst_QGraphicsEffectSource::boundingRect()
246{
247 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context");
248 QCOMPARE(effect->source()->boundingRect(Qt::DeviceCoordinates), QRectF());
249
250 QRectF itemBoundingRect = item->boundingRect();
251 if (!item->childItems().isEmpty())
252 itemBoundingRect |= item->childrenBoundingRect();
253
254 // We can at least check that the device bounding rect was correct in QGraphicsEffect::draw.
255 effect->storeDeviceDependentStuff = true;
256 effect->source()->update();
257 const QTransform deviceTransform = item->deviceTransform(viewportTransform: view->viewportTransform());
258 QTRY_COMPARE(effect->sourceDeviceBoundingRect, deviceTransform.mapRect(itemBoundingRect));
259
260 // Bounding rect in logical coordinates is of course fine.
261 QTRY_COMPARE(effect->source()->boundingRect(Qt::LogicalCoordinates), itemBoundingRect);
262 // Make sure default value is Qt::LogicalCoordinates.
263 QTRY_COMPARE(effect->source()->boundingRect(), itemBoundingRect);
264}
265
266void tst_QGraphicsEffectSource::clippedBoundingRect()
267{
268 QRectF itemBoundingRect = item->boundingRect();
269 item->setFlag(flag: QGraphicsItem::ItemClipsChildrenToShape);
270
271 QGraphicsRectItem *child = new QGraphicsRectItem(-1000, -1000, 2000, 2000);
272 child->setBrush(Qt::red);
273 child->setParentItem(item);
274
275 effect->storeDeviceDependentStuff = true;
276 effect->source()->update();
277 QTRY_COMPARE(effect->source()->boundingRect(Qt::LogicalCoordinates), itemBoundingRect);
278}
279
280void tst_QGraphicsEffectSource::deviceRect()
281{
282 effect->storeDeviceDependentStuff = true;
283 effect->source()->update();
284 QTRY_COMPARE(effect->deviceRect, view->viewport()->rect());
285}
286
287void tst_QGraphicsEffectSource::pixmap()
288{
289 QTest::ignoreMessage(type: QtWarningMsg, message: "QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context");
290 QCOMPARE(effect->source()->pixmap(Qt::DeviceCoordinates), QPixmap());
291
292 // We can at least verify a valid pixmap from QGraphicsEffect::draw.
293 effect->storeDeviceDependentStuff = true;
294 effect->source()->update();
295 QTRY_VERIFY(!effect->deviceCoordinatesPixmap.isNull());
296
297 // Pixmaps in logical coordinates we can do fine.
298 QPixmap pixmap1 = effect->source()->pixmap(system: Qt::LogicalCoordinates);
299 QVERIFY(!pixmap1.isNull());
300
301 // Make sure default value is Qt::LogicalCoordinates.
302 QPixmap pixmap2 = effect->source()->pixmap();
303 QCOMPARE(pixmap1, pixmap2);
304}
305
306class PaddingEffect : public QGraphicsEffect
307{
308public:
309 PaddingEffect(QObject *parent) : QGraphicsEffect(parent)
310 {
311 }
312
313 QRectF boundingRectFor(const QRectF &src) const {
314 return src.adjusted(xp1: -10, yp1: -10, xp2: 10, yp2: 10);
315 }
316
317 void draw(QPainter *) {
318 pix = source()->pixmap(system: coordinateMode, offset: &offset, mode: padMode);
319 }
320
321 QPixmap pix;
322 QPoint offset;
323 QGraphicsEffect::PixmapPadMode padMode;
324 Qt::CoordinateSystem coordinateMode;
325};
326
327void tst_QGraphicsEffectSource::pixmapPadding_data()
328{
329 QTest::addColumn<int>(name: "coordinateMode");
330 QTest::addColumn<int>(name: "padMode");
331 QTest::addColumn<QSize>(name: "size");
332 QTest::addColumn<QPoint>(name: "offset");
333 QTest::addColumn<uint>(name: "ulPixel");
334
335 QTest::newRow(dataTag: "log,nopad") << int(Qt::LogicalCoordinates)
336 << int(QGraphicsEffect::NoPad)
337 << QSize(10, 10) << QPoint(0, 0)
338 << 0xffff0000u;
339
340 QTest::newRow(dataTag: "log,transparent") << int(Qt::LogicalCoordinates)
341 << int(QGraphicsEffect::PadToTransparentBorder)
342 << QSize(14, 14) << QPoint(-2, -2)
343 << 0x00000000u;
344
345 QTest::newRow(dataTag: "log,effectrect") << int(Qt::LogicalCoordinates)
346 << int(QGraphicsEffect::PadToEffectiveBoundingRect)
347 << QSize(20, 20) << QPoint(-5, -5)
348 << 0x00000000u;
349
350 QTest::newRow(dataTag: "dev,nopad") << int(Qt::DeviceCoordinates)
351 << int(QGraphicsEffect::NoPad)
352 << QSize(20, 20) << QPoint(40, 40)
353 << 0xffff0000u;
354
355 QTest::newRow(dataTag: "dev,transparent") << int(Qt::DeviceCoordinates)
356 << int(QGraphicsEffect::PadToTransparentBorder)
357 << QSize(24, 24) << QPoint(38, 38)
358 << 0x00000000u;
359
360 QTest::newRow(dataTag: "dev,effectrect") << int(Qt::DeviceCoordinates)
361 << int(QGraphicsEffect::PadToEffectiveBoundingRect)
362 << QSize(40, 40) << QPoint(30, 30)
363 << 0x00000000u;
364
365}
366
367void tst_QGraphicsEffectSource::pixmapPadding()
368{
369 QPixmap dummyTarget(100, 100);
370 QPainter dummyPainter(&dummyTarget);
371 dummyPainter.translate(dx: 40, dy: 40);
372 dummyPainter.scale(sx: 2, sy: 2);
373
374 QPixmap pm(10, 10);
375 pm.fill(fillColor: Qt::red);
376
377 QGraphicsScene *scene = new QGraphicsScene();
378 PaddingEffect *effect = new PaddingEffect(scene);
379 QGraphicsPixmapItem *pmItem = new QGraphicsPixmapItem(pm);
380 scene->addItem(item: pmItem);
381 pmItem->setGraphicsEffect(effect);
382
383 QFETCH(int, coordinateMode);
384 QFETCH(int, padMode);
385 QFETCH(QPoint, offset);
386 QFETCH(QSize, size);
387 QFETCH(uint, ulPixel);
388
389 effect->padMode = (QGraphicsEffect::PixmapPadMode) padMode;
390 effect->coordinateMode = (Qt::CoordinateSystem) coordinateMode;
391
392 scene->render(painter: &dummyPainter, target: scene->itemsBoundingRect(), source: scene->itemsBoundingRect());
393
394 QCOMPARE(effect->pix.size(), size);
395 QCOMPARE(effect->offset, offset);
396 QCOMPARE(effect->pix.toImage().pixel(0, 0), ulPixel);
397
398 // ### Fix corruption in scene destruction, then enable...
399 // delete scene;
400}
401
402QTEST_MAIN(tst_QGraphicsEffectSource)
403#include "tst_qgraphicseffectsource.moc"
404
405

source code of qtbase/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp