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->setPen(QPen(Qt::black, 0));
238 item->setRect(itemRect);
239 item->setGraphicsEffect(effect);
240 int margin = effect->margin();
241 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
242 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
244 // Make sure disabling/enabling the effect doesn't change the bounding rect.
245 effect->setEnabled(false);
246 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
247 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
248 effect->setEnabled(true);
249 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
250 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
252 // Change effect margins.
253 effect->setMargin(margin = 20);
254 QCOMPARE(effect->boundingRect(), itemRect.adjusted(-margin, -margin, margin, margin));
255 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(itemRect));
257 // Uninstall effect on QGraphicsItem.
258 QPointer<CustomEffect> ptr = effect;
259 item->setGraphicsEffect(0);
265 void tst_QGraphicsEffect::boundingRect2()
267 CustomEffect *effect = new CustomEffect;
268 QGraphicsRectItem *root = new QGraphicsRectItem;
269 root->setPen(QPen(Qt::black, 0));
270 root->setGraphicsEffect(effect);
272 QGraphicsRectItem *child = new QGraphicsRectItem;
273 QRectF childRect(0, 0, 100, 100);
274 child->setPen(QPen(Qt::black, 0));
275 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
276 child->setRect(childRect);
277 child->setParentItem(root);
279 QGraphicsRectItem *grandChild = new QGraphicsRectItem;
280 QRectF grandChildRect(0, 0, 200, 200);
281 grandChild->setPen(QPen(Qt::black, 0));
282 grandChild->setRect(grandChildRect);
283 grandChild->setParentItem(child);
285 // Make sure the effect's bounding rect is clipped to the child's bounding rect.
286 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect));
288 // Disable ItemClipsChildrenToShape; effect's bounding rect is no longer clipped.
289 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
290 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
292 // Add root item to a scene, do the same tests as above. Results should be the same.
293 QGraphicsScene scene;
296 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
297 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect));
299 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
300 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
302 // Now add the scene to a view, results should be the same.
303 QGraphicsView view(&scene);
305 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
306 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect));
308 child->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
309 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
311 CustomEffect *childEffect = new CustomEffect;
312 child->setGraphicsEffect(childEffect);
313 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childEffect->boundingRectFor(childRect | grandChildRect)));
315 child->setGraphicsEffect(0);
316 QCOMPARE(effect->boundingRect(), effect->boundingRectFor(childRect | grandChildRect));
319 void tst_QGraphicsEffect::draw()
321 QGraphicsScene scene;
322 CustomItem *item = new CustomItem(0, 0, 100, 100);
325 QGraphicsView view(&scene);
327 QVERIFY(QTest::qWaitForWindowActive(&view));
328 QTRY_VERIFY(item->numRepaints > 0);
331 // Make sure installing the effect triggers a repaint.
332 CustomEffect *effect = new CustomEffect;
333 item->setGraphicsEffect(effect);
334 QTRY_COMPARE(effect->numRepaints, 1);
335 QTRY_COMPARE(item->numRepaints, 1);
337 // Make sure QPainter* and QStyleOptionGraphicsItem* stays persistent
338 // during QGraphicsEffect::draw/QGraphicsItem::paint.
339 QVERIFY(effect->m_painter);
340 QCOMPARE(effect->m_painter, item->m_painter);
341 QCOMPARE(effect->m_styleOption, item->m_styleOption);
342 // Make sure QGraphicsEffect::source is persistent.
343 QCOMPARE(effect->m_source, effect->source());
347 // Make sure updating the source triggers a repaint.
349 QTRY_COMPARE(effect->numRepaints, 1);
350 QTRY_COMPARE(item->numRepaints, 1);
351 QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceInvalidated);
355 // Make sure changing the effect's bounding rect triggers a repaint.
356 effect->setMargin(20);
357 QTRY_COMPARE(effect->numRepaints, 1);
358 QTRY_COMPARE(item->numRepaints, 1);
362 // Make sure change the item's bounding rect triggers a repaint.
363 item->setRect(0, 0, 50, 50);
364 QTRY_COMPARE(effect->numRepaints, 1);
365 QTRY_COMPARE(item->numRepaints, 1);
366 QVERIFY(effect->m_sourceChangedFlags & QGraphicsEffect::SourceBoundingRectChanged);
370 // Make sure the effect is the one to issue a repaint of the item.
371 effect->doNothingInDraw = true;
373 QTRY_COMPARE(effect->numRepaints, 1);
374 QCOMPARE(item->numRepaints, 0);
375 effect->doNothingInDraw = false;
379 // Make sure we update the source when disabling/enabling the effect.
380 effect->setEnabled(false);
382 QCOMPARE(effect->numRepaints, 0);
383 QCOMPARE(item->numRepaints, 1);
387 effect->setEnabled(true);
388 QTRY_COMPARE(effect->numRepaints, 1);
389 QTRY_COMPARE(item->numRepaints, 1);
393 // Effect is already enabled; nothing should happen.
394 effect->setEnabled(true);
396 QCOMPARE(effect->numRepaints, 0);
397 QCOMPARE(item->numRepaints, 0);
399 // Make sure uninstalling an effect triggers a repaint.
400 QPointer<CustomEffect> ptr = effect;
401 item->setGraphicsEffect(0);
403 QTRY_COMPARE(item->numRepaints, 1);
406 void tst_QGraphicsEffect::opacity()
408 // Make sure the painter's opacity is correct in QGraphicsEffect::draw.
409 QGraphicsScene scene;
410 CustomItem *item = new CustomItem(0, 0, 100, 100);
411 item->setOpacity(0.5);
412 CustomEffect *effect = new CustomEffect;
413 item->setGraphicsEffect(effect);
416 QGraphicsView view(&scene);
418 QVERIFY(QTest::qWaitForWindowExposed(&view));
419 QTRY_VERIFY(effect->numRepaints > 0);
420 QCOMPARE(effect->m_opacity, qreal(0.5));
423 void tst_QGraphicsEffect::grayscale()
425 if (qApp->desktop()->depth() < 24)
426 QSKIP("Test only works on 32 bit displays");
428 QGraphicsScene scene(0, 0, 100, 100);
430 QGraphicsRectItem *item = scene.addRect(0, 0, 50, 50);
431 item->setPen(Qt::NoPen);
432 item->setBrush(QColor(122, 193, 66)); // Qt light green
434 QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect;
435 effect->setColor(Qt::black);
436 item->setGraphicsEffect(effect);
439 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
442 painter.begin(&image);
443 painter.setRenderHint(QPainter::Antialiasing);
444 scene.render(&painter);
447 QCOMPARE(image.pixel(10, 10), qRgb(148, 148, 148));
449 effect->setStrength(0.5);
452 painter.begin(&image);
453 painter.setRenderHint(QPainter::Antialiasing);
454 scene.render(&painter);
457 QCOMPARE(image.pixel(10, 10), qRgb(135, 171, 107));
459 effect->setStrength(0.0);
462 painter.begin(&image);
463 painter.setRenderHint(QPainter::Antialiasing);
464 scene.render(&painter);
467 QCOMPARE(image.pixel(10, 10), qRgb(122, 193, 66));
470 void tst_QGraphicsEffect::colorize()
472 if (qApp->desktop()->depth() < 24)
473 QSKIP("Test only works on 32 bit displays");
475 QGraphicsScene scene(0, 0, 100, 100);
477 QGraphicsRectItem *item = scene.addRect(0, 0, 50, 50);
478 item->setPen(Qt::NoPen);
479 item->setBrush(QColor(122, 193, 66)); // Qt light green
481 QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect;
482 effect->setColor(QColor(102, 153, 51)); // Qt dark green
483 item->setGraphicsEffect(effect);
486 QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
489 painter.begin(&image);
490 painter.setRenderHint(QPainter::Antialiasing);
491 scene.render(&painter);
494 QCOMPARE(image.pixel(10, 10), qRgb(191, 212, 169));
496 effect->setStrength(0.5);
499 painter.begin(&image);
500 painter.setRenderHint(QPainter::Antialiasing);
501 scene.render(&painter);
504 QCOMPARE(image.pixel(10, 10), qRgb(156, 203, 117));
506 effect->setStrength(0.0);
509 painter.begin(&image);
510 painter.setRenderHint(QPainter::Antialiasing);
511 scene.render(&painter);
514 QCOMPARE(image.pixel(10, 10), qRgb(122, 193, 66));
517 class PixmapItemEffect : public QGraphicsEffect
520 PixmapItemEffect(const QPixmap &source)
526 QRectF boundingRectFor(const QRectF &rect) const
529 void draw(QPainter *painter)
531 QVERIFY(sourcePixmap(Qt::LogicalCoordinates).handle() == pixmap.handle());
532 QVERIFY((painter->worldTransform().type() <= QTransform::TxTranslate) == (sourcePixmap(Qt::DeviceCoordinates).handle() == pixmap.handle()));
540 void tst_QGraphicsEffect::drawPixmapItem()
542 QImage image(32, 32, QImage::Format_RGB32);
544 p.fillRect(0, 0, 32, 16, Qt::blue);
545 p.fillRect(0, 16, 32, 16, Qt::red);
548 QGraphicsScene scene;
549 QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
552 PixmapItemEffect *effect = new PixmapItemEffect(item->pixmap());
553 item->setGraphicsEffect(effect);
555 QGraphicsView view(&scene);
557 QVERIFY(QTest::qWaitForWindowExposed(&view));
558 QTRY_VERIFY(effect->repaints >= 1);
562 QTRY_VERIFY(effect->repaints >= 2);
565 class DeviceEffect : public QGraphicsEffect
568 QRectF boundingRectFor(const QRectF &rect) const
571 void draw(QPainter *painter)
574 QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, QGraphicsEffect::NoPad);
580 painter->setWorldTransform(QTransform());
581 painter->drawPixmap(offset, pixmap);
586 void tst_QGraphicsEffect::deviceCoordinateTranslateCaching()
588 QGraphicsScene scene;
589 CustomItem *item = new CustomItem(0, 0, 10, 10);
591 scene.setSceneRect(0, 0, 50, 0);
593 item->setGraphicsEffect(new DeviceEffect);
594 item->setPen(Qt::NoPen);
595 item->setBrush(Qt::red);
597 QGraphicsView view(&scene);
599 QVERIFY(QTest::qWaitForWindowExposed(&view));
601 QTRY_VERIFY(item->numRepaints >= 1);
602 int numRepaints = item->numRepaints;
604 item->translate(10, 0);
606 QTRY_VERIFY(item->numRepaints == numRepaints);
609 void tst_QGraphicsEffect::inheritOpacity()
611 QGraphicsScene scene;
612 QGraphicsRectItem *rectItem = new QGraphicsRectItem(0, 0, 10, 10);
613 CustomItem *item = new CustomItem(0, 0, 10, 10, rectItem);
615 scene.addItem(rectItem);
617 item->setGraphicsEffect(new DeviceEffect);
618 item->setPen(Qt::NoPen);
619 item->setBrush(Qt::red);
621 rectItem->setOpacity(0.5);
623 QGraphicsView view(&scene);
625 QVERIFY(QTest::qWaitForWindowExposed(&view));
627 QTRY_VERIFY(item->numRepaints >= 1);
629 int numRepaints = item->numRepaints;
631 rectItem->setOpacity(1);
633 // item should have been rerendered due to opacity changing
634 QTRY_VERIFY(item->numRepaints > numRepaints);
637 void tst_QGraphicsEffect::dropShadowClipping()
639 QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
640 img.fill(0xffffffff);
642 QGraphicsScene scene;
643 QGraphicsRectItem *item = new QGraphicsRectItem(-5, -500, 10, 1000);
644 item->setGraphicsEffect(new QGraphicsDropShadowEffect);
645 item->setPen(Qt::NoPen);
646 item->setBrush(Qt::red);
651 scene.render(&p, img.rect(), QRect(-64, -64, 128, 128));
654 for (int y = 1; y < img.height(); ++y)
655 for (int x = 0; x < img.width(); ++x)
656 QCOMPARE(img.pixel(x, y), img.pixel(x, y-1));
659 class MyGraphicsItem : public QGraphicsWidget
662 MyGraphicsItem(QGraphicsItem *parent = 0) :
663 QGraphicsWidget(parent), nbPaint(0)
665 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
668 QGraphicsWidget::paint(painter, option, widget);
673 void tst_QGraphicsEffect::childrenVisibilityShouldInvalidateCache()
675 QGraphicsScene scene;
676 MyGraphicsItem parent;
677 parent.resize(200, 200);
678 QGraphicsWidget child(&parent);
679 child.resize(200, 200);
680 child.setVisible(false);
681 scene.addItem(&parent);
682 QGraphicsView view(&scene);
684 QApplication::setActiveWindow(&view);
685 QVERIFY(QTest::qWaitForWindowActive(&view));
686 QTRY_VERIFY(parent.nbPaint >= 1);
687 //we set an effect on the parent
688 parent.setGraphicsEffect(new QGraphicsDropShadowEffect(&parent));
690 QApplication::processEvents();
691 //new effect applied->repaint
692 QVERIFY(parent.nbPaint >= 2);
693 child.setVisible(true);
695 QApplication::processEvents();
696 //a new child appears we need to redraw the effect.
697 QVERIFY(parent.nbPaint >= 3);
700 void tst_QGraphicsEffect::prepareGeometryChangeInvalidateCache()
702 MyGraphicsItem *item = new MyGraphicsItem;
703 item->resize(200, 200);
705 QGraphicsScene scene;
708 QGraphicsView view(&scene);
710 qApp->setActiveWindow(&view);
711 QVERIFY(QTest::qWaitForWindowActive(&view));
712 QTRY_VERIFY(item->nbPaint >= 1);
715 item->setGraphicsEffect(new QGraphicsDropShadowEffect);
716 QTRY_COMPARE(item->nbPaint, 1);
719 item->resize(300, 300);
720 QTRY_COMPARE(item->nbPaint, 1);
723 item->setPos(item->pos() + QPointF(10, 10));
725 QCOMPARE(item->nbPaint, 0);
728 void tst_QGraphicsEffect::itemHasNoContents()
730 QGraphicsRectItem *parent = new QGraphicsRectItem;
731 parent->setFlag(QGraphicsItem::ItemHasNoContents);
733 MyGraphicsItem *child = new MyGraphicsItem;
734 child->setParentItem(parent);
735 child->resize(200, 200);
737 QGraphicsScene scene;
738 scene.addItem(parent);
740 QGraphicsView view(&scene);
742 qApp->setActiveWindow(&view);
743 QVERIFY(QTest::qWaitForWindowActive(&view));
744 QTRY_VERIFY(child->nbPaint >= 1);
746 CustomEffect *effect = new CustomEffect;
747 parent->setGraphicsEffect(effect);
748 QTRY_COMPARE(effect->numRepaints, 1);
750 for (int i = 0; i < 3; ++i) {
753 QTRY_COMPARE(effect->numRepaints, 1);
757 QTEST_MAIN(tst_QGraphicsEffect)
758 #include "tst_qgraphicseffect.moc"