1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
43 #include <QtTest/QtTestWidgets>
44 #include <QtWidgets/qdesktopwidget.h>
45 #include <QtWidgets/qgraphicseffect.h>
46 #include <QtWidgets/qgraphicsview.h>
47 #include <QtWidgets/qgraphicsscene.h>
48 #include <QtWidgets/qgraphicsitem.h>
49 #include <QtWidgets/qgraphicswidget.h>
50 #include <QtWidgets/qstyleoption.h>
52 #include <private/qgraphicseffect_p.h>
54 class tst_QGraphicsEffect : public QObject
63 void boundingRectFor();
70 void drawPixmapItem();
71 void deviceCoordinateTranslateCaching();
72 void inheritOpacity();
73 void dropShadowClipping();
74 void childrenVisibilityShouldInvalidateCache();
75 void prepareGeometryChangeInvalidateCache();
76 void itemHasNoContents();
79 void tst_QGraphicsEffect::initTestCase()
82 class CustomItem : public QGraphicsRectItem
85 CustomItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent = 0)
86 : QGraphicsRectItem(x, y, width, height, parent), numRepaints(0),
87 m_painter(0), m_styleOption(0)
90 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
93 m_styleOption = option;
95 QGraphicsRectItem::paint(painter, option, widget);
107 const QStyleOption *m_styleOption;
110 class CustomEffect : public QGraphicsEffect
114 : QGraphicsEffect(), numRepaints(0), m_margin(10),
115 doNothingInDraw(false), m_painter(0), m_styleOption(0), m_source(0), m_opacity(1.0)
118 QRectF boundingRectFor(const QRectF &rect) const
119 { return rect.adjusted(-m_margin, -m_margin, m_margin, m_margin); }
124 m_sourceChangedFlags = QGraphicsEffect::ChangeFlags();
131 void setMargin(int margin)
134 updateBoundingRect();
140 void draw(QPainter *painter)
147 m_styleOption = source()->styleOption();
148 m_opacity = painter->opacity();
152 void sourceChanged(QGraphicsEffect::ChangeFlags flags)
153 { m_sourceChangedFlags |= flags; }
157 QGraphicsEffect::ChangeFlags m_sourceChangedFlags;
158 bool doNothingInDraw;
160 const QStyleOption *m_styleOption;
161 QGraphicsEffectSource *m_source;
165 void tst_QGraphicsEffect::setEnabled()
168 QVERIFY(effect.isEnabled());
170 effect.setEnabled(false);
171 QVERIFY(!effect.isEnabled());
174 void tst_QGraphicsEffect::source()
176 QPointer<CustomEffect> effect = new CustomEffect;
177 QVERIFY(!effect->source());
178 QVERIFY(!effect->m_sourceChangedFlags);
180 // Install effect on QGraphicsItem.
181 QGraphicsItem *item = new QGraphicsRectItem(0, 0, 10, 10);
182 item->setGraphicsEffect(effect);
183 QVERIFY(effect->source());
184 QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item);
185 QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceAttached);
188 // Make sure disabling/enabling the effect doesn't change the source.
189 effect->setEnabled(false);
190 QVERIFY(effect->source());
191 QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item);
192 QVERIFY(!effect->m_sourceChangedFlags);
195 effect->setEnabled(true);
196 QVERIFY(effect->source());
197 QCOMPARE(effect->source()->graphicsItem(), (const QGraphicsItem*)item);
198 QVERIFY(!effect->m_sourceChangedFlags);
201 // Uninstall effect on QGraphicsItem.
203 item->setGraphicsEffect(0);
205 effect = new CustomEffect;
207 // The item takes ownership and should delete the effect when destroyed.
208 item->setGraphicsEffect(effect);
209 QPointer<QGraphicsEffectSource> source = effect->source();
211 QCOMPARE(source->graphicsItem(), (const QGraphicsItem*)item);
217 void tst_QGraphicsEffect::boundingRectFor()
220 int margin = effect.margin();
221 const QRectF source(0, 0, 100, 100);
222 QCOMPARE(effect.boundingRectFor(source), source.adjusted(-margin, -margin, margin, margin));
224 effect.setMargin(margin = 20);
225 QCOMPARE(effect.boundingRectFor(source), source.adjusted(-margin, -margin, margin, margin));
228 void tst_QGraphicsEffect::boundingRect()
230 // No source; empty bounding rect.
231 CustomEffect *effect = new CustomEffect;
232 QCOMPARE(effect->boundingRect(), QRectF());
234 // Install effect on QGraphicsItem.
235 QRectF itemRect(0, 0, 100, 100);
236 QGraphicsRectItem *item = new QGraphicsRectItem;
237 item->setRect(itemRect);
238 item->setGraphicsEffect(effect);
239 int margin = effect->margin();
240 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
241 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
243 // Make sure disabling/enabling the effect doesn't change the bounding rect.
244 effect->setEnabled(false);
245 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
246 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
247 effect->setEnabled(true);
248 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
249 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
251 // Change effect margins.
252 effect->setMargin(margin = 20);
253 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
254 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
256 // Uninstall effect on QGraphicsItem.
257 QPointer<CustomEffect> ptr = effect;
258 item->setGraphicsEffect(0);
264 void tst_QGraphicsEffect::boundingRect2()
266 CustomEffect *effect = new CustomEffect;
267 QGraphicsRectItem *root = new QGraphicsRectItem;
268 root->setGraphicsEffect(effect);
270 QGraphicsRectItem *child = new QGraphicsRectItem;
271 QRectF childRect(0, 0, 100, 100);
272 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
273 child->setRect(childRect);
274 child->setParentItem(root);
276 QGraphicsRectItem *grandChild = new QGraphicsRectItem;
277 QRectF grandChildRect(0, 0, 200, 200);
278 grandChild->setRect(grandChildRect);
279 grandChild->setParentItem(child);
281 // Make sure the effect's bounding rect is clipped to the child's bounding rect.
282 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect));
284 // Disable ItemClipsChildrenToShape; effect's bounding rect is no longer clipped.
285 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
286 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
288 // Add root item to a scene, do the same tests as above. Results should be the same.
289 QGraphicsScene scene;
292 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
293 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect));
295 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
296 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
298 // Now add the scene to a view, results should be the same.
299 QGraphicsView view(&scene);
301 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
302 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect));
304 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
305 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
307 CustomEffect *childEffect = new CustomEffect;
308 child->setGraphicsEffect(childEffect);
309 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childEffect->boundingRectFor(childRect | grandChildRect)));
311 child->setGraphicsEffect(0);
312 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
315 void tst_QGraphicsEffect::draw()
317 QGraphicsScene scene;
318 CustomItem *item = new CustomItem(0, 0, 100, 100);
321 QGraphicsView view(&scene);
323 QVERIFY(QTest::qWaitForWindowActive(&view));
324 QTRY_VERIFY(item->numRepaints > 0);
327 // Make sure installing the effect triggers a repaint.
328 CustomEffect *effect = new CustomEffect;
329 item->setGraphicsEffect(effect);
330 QTRY_COMPARE(effect->numRepaints, 1);
331 QTRY_COMPARE(item->numRepaints, 1);
333 // Make sure QPainter* and QStyleOptionGraphicsItem* stays persistent
334 // during QGraphicsEffect::draw/QGraphicsItem::paint.
335 QVERIFY(effect->m_painter);
336 QCOMPARE(effect->m_painter, item->m_painter);
337 QCOMPARE(effect->m_styleOption, item->m_styleOption);
338 // Make sure QGraphicsEffect::source is persistent.
339 QCOMPARE(effect->m_source, effect->source());
343 // Make sure updating the source triggers a repaint.
345 QTRY_COMPARE(effect->numRepaints, 1);
346 QTRY_COMPARE(item->numRepaints, 1);
347 QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceInvalidated);
351 // Make sure changing the effect's bounding rect triggers a repaint.
352 effect->setMargin(20);
353 QTRY_COMPARE(effect->numRepaints, 1);
354 QTRY_COMPARE(item->numRepaints, 1);
358 // Make sure change the item's bounding rect triggers a repaint.
359 item->setRect(0, 0, 50, 50);
360 QTRY_COMPARE(effect->numRepaints, 1);
361 QTRY_COMPARE(item->numRepaints, 1);
362 QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceBoundingRectChanged);
366 // Make sure the effect is the one to issue a repaint of the item.
367 effect->doNothingInDraw = true;
369 QTRY_COMPARE(effect->numRepaints, 1);
370 QCOMPARE(item->numRepaints, 0);
371 effect->doNothingInDraw = false;
375 // Make sure we update the source when disabling/enabling the effect.
376 effect->setEnabled(false);
378 QCOMPARE(effect->numRepaints, 0);
379 QCOMPARE(item->numRepaints, 1);
383 effect->setEnabled(true);
384 QTRY_COMPARE(effect->numRepaints, 1);
385 QTRY_COMPARE(item->numRepaints, 1);
389 // Effect is already enabled; nothing should happen.
390 effect->setEnabled(true);
392 QCOMPARE(effect->numRepaints, 0);
393 QCOMPARE(item->numRepaints, 0);
395 // Make sure uninstalling an effect triggers a repaint.
396 QPointer<CustomEffect> ptr = effect;
397 item->setGraphicsEffect(0);
399 QTRY_COMPARE(item->numRepaints, 1);
402 void tst_QGraphicsEffect::opacity()
404 // Make sure the painter's opacity is correct in QGraphicsEffect::draw.
405 QGraphicsScene scene;
406 CustomItem *item = new CustomItem(0, 0, 100, 100);
407 item->setOpacity(0.5);
408 CustomEffect *effect = new CustomEffect;
409 item->setGraphicsEffect(effect);
412 QGraphicsView view(&scene);
414 QVERIFY(QTest::qWaitForWindowExposed(&view));
415 QTRY_VERIFY(effect->numRepaints > 0);
416 QCOMPARE(effect->m_opacity, qreal(0.5));
419 void tst_QGraphicsEffect::grayscale()
421 if (qApp->desktop()->depth() < 24)
422 QSKIP("Test only works on 32 bit displays");
424 QGraphicsScene scene(0, 0, 100, 100);
426 QGraphicsRectItem *item = scene.addRect(0, 0, 50, 50);
427 item->setPen(Qt::NoPen);
428 item->setBrush(QColor(122, 193, 66)); // Qt light green
430 QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect;
431 effect->setColor(Qt::black);
432 item->setGraphicsEffect(effect);
435 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
438 painter.begin(&image);
439 painter.setRenderHint(QPainter::Antialiasing);
440 scene.render(&painter);
443 QCOMPARE(image.pixel(10, 10), qRgb(148, 148, 148));
445 effect->setStrength(0.5);
448 painter.begin(&image);
449 painter.setRenderHint(QPainter::Antialiasing);
450 scene.render(&painter);
453 QCOMPARE(image.pixel(10, 10), qRgb(135, 171, 107));
455 effect->setStrength(0.0);
458 painter.begin(&image);
459 painter.setRenderHint(QPainter::Antialiasing);
460 scene.render(&painter);
463 QCOMPARE(image.pixel(10, 10), qRgb(122, 193, 66));
466 void tst_QGraphicsEffect::colorize()
468 if (qApp->desktop()->depth() < 24)
469 QSKIP("Test only works on 32 bit displays");
471 QGraphicsScene scene(0, 0, 100, 100);
473 QGraphicsRectItem *item = scene.addRect(0, 0, 50, 50);
474 item->setPen(Qt::NoPen);
475 item->setBrush(QColor(122, 193, 66)); // Qt light green
477 QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect;
478 effect->setColor(QColor(102, 153, 51)); // Qt dark green
479 item->setGraphicsEffect(effect);
482 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
485 painter.begin(&image);
486 painter.setRenderHint(QPainter::Antialiasing);
487 scene.render(&painter);
490 QCOMPARE(image.pixel(10, 10), qRgb(191, 212, 169));
492 effect->setStrength(0.5);
495 painter.begin(&image);
496 painter.setRenderHint(QPainter::Antialiasing);
497 scene.render(&painter);
500 QCOMPARE(image.pixel(10, 10), qRgb(156, 203, 117));
502 effect->setStrength(0.0);
505 painter.begin(&image);
506 painter.setRenderHint(QPainter::Antialiasing);
507 scene.render(&painter);
510 QCOMPARE(image.pixel(10, 10), qRgb(122, 193, 66));
513 class PixmapItemEffect : public QGraphicsEffect
516 PixmapItemEffect(const QPixmap &source)
522 QRectF boundingRectFor(const QRectF &rect) const
525 void draw(QPainter *painter)
527 QVERIFY(sourcePixmap(Qt::LogicalCoordinates).handle() == pixmap.handle());
528 QVERIFY((painter->worldTransform().type() <= QTransform::TxTranslate) == (sourcePixmap(Qt::DeviceCoordinates).handle() == pixmap.handle()));
536 void tst_QGraphicsEffect::drawPixmapItem()
538 QImage image(32, 32, QImage::Format_RGB32);
540 p.fillRect(0, 0, 32, 16, Qt::blue);
541 p.fillRect(0, 16, 32, 16, Qt::red);
544 QGraphicsScene scene;
545 QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
548 PixmapItemEffect *effect = new PixmapItemEffect(item->pixmap());
549 item->setGraphicsEffect(effect);
551 QGraphicsView view(&scene);
553 QVERIFY(QTest::qWaitForWindowExposed(&view));
554 QTRY_VERIFY(effect->repaints >= 1);
558 QTRY_VERIFY(effect->repaints >= 2);
561 class DeviceEffect : public QGraphicsEffect
564 QRectF boundingRectFor(const QRectF &rect) const
567 void draw(QPainter *painter)
570 QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, QGraphicsEffect::NoPad);
576 painter->setWorldTransform(QTransform());
577 painter->drawPixmap(offset, pixmap);
582 void tst_QGraphicsEffect::deviceCoordinateTranslateCaching()
584 QGraphicsScene scene;
585 CustomItem *item = new CustomItem(0, 0, 10, 10);
587 scene.setSceneRect(0, 0, 50, 0);
589 item->setGraphicsEffect(new DeviceEffect);
590 item->setPen(Qt::NoPen);
591 item->setBrush(Qt::red);
593 QGraphicsView view(&scene);
595 QVERIFY(QTest::qWaitForWindowExposed(&view));
597 QTRY_VERIFY(item->numRepaints >= 1);
598 int numRepaints = item->numRepaints;
600 item->translate(10, 0);
602 QTRY_VERIFY(item->numRepaints == numRepaints);
605 void tst_QGraphicsEffect::inheritOpacity()
607 QGraphicsScene scene;
608 QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 10, 10);
609 CustomItem *item = new CustomItem(0, 0, 10, 10, rectItem);
611 scene.addItem(rectItem);
613 item->setGraphicsEffect(new DeviceEffect);
614 item->setPen(Qt::NoPen);
615 item->setBrush(Qt::red);
617 rectItem->setOpacity(0.5);
619 QGraphicsView view(&scene);
621 QVERIFY(QTest::qWaitForWindowExposed(&view));
623 QTRY_VERIFY(item->numRepaints >= 1);
625 int numRepaints = item->numRepaints;
627 rectItem->setOpacity(1);
629 // item should have been rerendered due to opacity changing
630 QTRY_VERIFY(item->numRepaints > numRepaints);
633 void tst_QGraphicsEffect::dropShadowClipping()
635 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
636 img.fill(0xffffffff);
638 QGraphicsScene scene;
639 QGraphicsRectItem *item = new QGraphicsRectItem(-5, -500, 10, 1000);
640 item->setGraphicsEffect(new QGraphicsDropShadowEffect);
641 item->setPen(Qt::NoPen);
642 item->setBrush(Qt::red);
647 scene.render(&p, img.rect(), QRect(-64, -64, 128, 128));
650 for (int y = 1; y < img.height(); ++y)
651 for (int x = 0; x < img.width(); ++x)
652 QCOMPARE(img.pixel(x, y), img.pixel(x, y-1));
655 class MyGraphicsItem : public QGraphicsWidget
658 MyGraphicsItem(QGraphicsItem *parent = 0) :
659 QGraphicsWidget(parent), nbPaint(0)
661 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
664 QGraphicsWidget::paint(painter, option, widget);
669 void tst_QGraphicsEffect::childrenVisibilityShouldInvalidateCache()
671 QGraphicsScene scene;
672 MyGraphicsItem parent;
673 parent.resize(200, 200);
674 QGraphicsWidget child(&parent);
675 child.resize(200, 200);
676 child.setVisible(false);
677 scene.addItem(&parent);
678 QGraphicsView view(&scene);
680 QApplication::setActiveWindow(&view);
681 QVERIFY(QTest::qWaitForWindowActive(&view));
682 QTRY_VERIFY(parent.nbPaint >= 1);
683 //we set an effect on the parent
684 parent.setGraphicsEffect(new QGraphicsDropShadowEffect(&parent));
686 QApplication::processEvents();
687 //new effect applied->repaint
688 QVERIFY(parent.nbPaint >= 2);
689 child.setVisible(true);
691 QApplication::processEvents();
692 //a new child appears we need to redraw the effect.
693 QVERIFY(parent.nbPaint >= 3);
696 void tst_QGraphicsEffect::prepareGeometryChangeInvalidateCache()
698 MyGraphicsItem *item = new MyGraphicsItem;
699 item->resize(200, 200);
701 QGraphicsScene scene;
704 QGraphicsView view(&scene);
706 qApp->setActiveWindow(&view);
707 QVERIFY(QTest::qWaitForWindowActive(&view));
708 QTRY_VERIFY(item->nbPaint >= 1);
711 item->setGraphicsEffect(new QGraphicsDropShadowEffect);
712 QTRY_COMPARE(item->nbPaint, 1);
715 item->resize(300, 300);
716 QTRY_COMPARE(item->nbPaint, 1);
719 item->setPos(item->pos() + QPointF(10, 10));
721 QCOMPARE(item->nbPaint, 0);
724 void tst_QGraphicsEffect::itemHasNoContents()
726 QGraphicsRectItem *parent = new QGraphicsRectItem;
727 parent->setFlag(QGraphicsItem::ItemHasNoContents);
729 MyGraphicsItem *child = new MyGraphicsItem;
730 child->setParentItem(parent);
731 child->resize(200, 200);
733 QGraphicsScene scene;
734 scene.addItem(parent);
736 QGraphicsView view(&scene);
738 qApp->setActiveWindow(&view);
739 QVERIFY(QTest::qWaitForWindowActive(&view));
740 QTRY_VERIFY(child->nbPaint >= 1);
742 CustomEffect *effect = new CustomEffect;
743 parent->setGraphicsEffect(effect);
744 QTRY_COMPARE(effect->numRepaints, 1);
746 for (int i = 0; i < 3; ++i) {
749 QTRY_COMPARE(effect->numRepaints, 1);
753 QTEST_MAIN(tst_QGraphicsEffect)
754 #include "tst_qgraphicseffect.moc"