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 QtWidgets module of the Qt Toolkit.
7 **
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 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 Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 /*!
41     \class QGraphicsEffect
42     \brief The QGraphicsEffect class is the base class for all graphics
43            effects.
44     \since 4.6
45     \ingroup multimedia
46     \ingroup graphicsview-api
47     \inmodule QtWidgets
48 
49     Effects alter the appearance of elements by hooking into the rendering
50     pipeline and operating between the source (e.g., a QGraphicsPixmapItem)
51     and the destination device (e.g., QGraphicsView's viewport). Effects can be
52     disabled by calling setEnabled(false). If effects are disabled, the source
53     is rendered directly.
54 
55     To add a visual effect to a QGraphicsItem, for example, you can use one of
56     the standard effects, or alternately, create your own effect by creating a
57     subclass of QGraphicsEffect. The effect can then be installed on the item
58     using QGraphicsItem::setGraphicsEffect().
59 
60     Qt provides the following standard effects:
61 
62     \list
63     \li QGraphicsBlurEffect - blurs the item by a given radius
64     \li QGraphicsDropShadowEffect - renders a dropshadow behind the item
65     \li QGraphicsColorizeEffect - renders the item in shades of any given color
66     \li QGraphicsOpacityEffect - renders the item with an opacity
67     \endlist
68 
69     \table
70     \row
71     \li{2,1} \image graphicseffect-plain.png
72     \row
73     \li \image graphicseffect-blur.png
74     \li \image graphicseffect-colorize.png
75     \row
76     \li \image graphicseffect-opacity.png
77     \li \image graphicseffect-drop-shadow.png
78     \endtable
79 
80     \image graphicseffect-widget.png
81 
82     For more information on how to use each effect, refer to the specific
83     effect's documentation.
84 
85     To create your own custom effect, create a subclass of QGraphicsEffect (or
86     any other existing effects) and reimplement the virtual function draw().
87     This function is called whenever the effect needs to redraw. The draw()
88     function takes the painter with which to draw as an argument. For more
89     information, refer to the documenation for draw(). In the draw() function
90     you can call sourcePixmap() to get a pixmap of the graphics effect source
91     which you can then process.
92 
93     If your effect changes, use update() to request for a redraw. If your
94     custom effect changes the bounding rectangle of the source, e.g., a radial
95     glow effect may need to apply an extra margin, you can reimplement the
96     virtual boundingRectFor() function, and call updateBoundingRect()
97     to notify the framework whenever this rectangle changes. The virtual
98     sourceChanged() function is called to notify the effects that
99     the source has changed in some way - e.g., if the source is a
100     QGraphicsRectItem and its rectangle parameters have changed.
101 
102     \sa QGraphicsItem::setGraphicsEffect(), QWidget::setGraphicsEffect()
103 */
104 
105 #include "qgraphicseffect_p.h"
106 #include "private/qgraphicsitem_p.h"
107 
108 #include <QtWidgets/qgraphicsitem.h>
109 
110 #include <QtGui/qimage.h>
111 #include <QtGui/qpainter.h>
112 #include <QtGui/qpaintengine.h>
113 #include <QtCore/qrect.h>
114 #include <QtCore/qdebug.h>
115 #include <private/qdrawhelper_p.h>
116 
117 QT_BEGIN_NAMESPACE
118 
~QGraphicsEffectPrivate()119 QGraphicsEffectPrivate::~QGraphicsEffectPrivate()
120 {
121 }
122 
123 /*!
124     \internal
125     \class QGraphicsEffectSource
126     \brief The QGraphicsEffectSource class represents the source on which a
127            QGraphicsEffect is installed on.
128 
129     When a QGraphicsEffect is installed on a QGraphicsItem, for example, this
130     class will act as a wrapper around QGraphicsItem. Then, calling update() is
131     effectively the same as calling QGraphicsItem::update().
132 
133     QGraphicsEffectSource also provides a pixmap() function which creates a
134     pixmap with the source painted into it.
135 
136     \sa QGraphicsItem::setGraphicsEffect(), QWidget::setGraphicsEffect()
137 */
138 
139 /*!
140     \internal
141 */
QGraphicsEffectSource(QGraphicsEffectSourcePrivate & dd,QObject * parent)142 QGraphicsEffectSource::QGraphicsEffectSource(QGraphicsEffectSourcePrivate &dd, QObject *parent)
143     : QObject(dd, parent)
144 {}
145 
146 /*!
147     Destroys the effect source.
148 */
~QGraphicsEffectSource()149 QGraphicsEffectSource::~QGraphicsEffectSource()
150 {}
151 
152 /*!
153     Returns the bounding rectangle of the source mapped to the given \a system.
154 
155     \sa draw()
156 */
boundingRect(Qt::CoordinateSystem system) const157 QRectF QGraphicsEffectSource::boundingRect(Qt::CoordinateSystem system) const
158 {
159     return d_func()->boundingRect(system);
160 }
161 
162 /*!
163     Returns the bounding rectangle of the source mapped to the given \a system.
164 
165     Calling this function with Qt::DeviceCoordinates outside of
166     QGraphicsEffect::draw() will give undefined results, as there is no device
167     context available.
168 
169     \sa draw()
170 */
sourceBoundingRect(Qt::CoordinateSystem system) const171 QRectF QGraphicsEffect::sourceBoundingRect(Qt::CoordinateSystem system) const
172 {
173     Q_D(const QGraphicsEffect);
174     if (d->source)
175         return d->source->boundingRect(system);
176     return QRectF();
177 }
178 
179 /*!
180     Returns a pointer to the item if this source is a QGraphicsItem; otherwise
181     returns \nullptr.
182 
183     \sa widget()
184 */
graphicsItem() const185 const QGraphicsItem *QGraphicsEffectSource::graphicsItem() const
186 {
187     return d_func()->graphicsItem();
188 }
189 
190 /*!
191     Returns a pointer to the widget if this source is a QWidget; otherwise
192     returns \nullptr.
193 
194     \sa graphicsItem()
195 */
widget() const196 const QWidget *QGraphicsEffectSource::widget() const
197 {
198     return d_func()->widget();
199 }
200 
201 /*!
202     Returns a pointer to the style options (used when drawing the source) if
203     available; otherwise returns \nullptr.
204 
205     \sa graphicsItem(), widget()
206 */
styleOption() const207 const QStyleOption *QGraphicsEffectSource::styleOption() const
208 {
209     return d_func()->styleOption();
210 }
211 
212 /*!
213     Draws the source using the given \a painter.
214 
215     This function should only be called from QGraphicsEffect::draw().
216 
217     \sa QGraphicsEffect::draw()
218 */
draw(QPainter * painter)219 void QGraphicsEffectSource::draw(QPainter *painter)
220 {
221     Q_D(const QGraphicsEffectSource);
222 
223     QPixmap pm;
224     if (QPixmapCache::find(d->m_cacheKey, &pm)) {
225         QTransform restoreTransform;
226         if (d->m_cachedSystem == Qt::DeviceCoordinates) {
227             restoreTransform = painter->worldTransform();
228             painter->setWorldTransform(QTransform());
229         }
230 
231         painter->drawPixmap(d->m_cachedOffset, pm);
232 
233         if (d->m_cachedSystem == Qt::DeviceCoordinates)
234             painter->setWorldTransform(restoreTransform);
235     } else {
236         d_func()->draw(painter);
237     }
238 }
239 
240 /*!
241     Draws the source directly using the given \a painter.
242 
243     This function should only be called from QGraphicsEffect::draw().
244 
245     For example:
246 
247     \snippet code/src_gui_effects_qgraphicseffect.cpp 0
248 
249     \sa QGraphicsEffect::draw()
250 */
drawSource(QPainter * painter)251 void QGraphicsEffect::drawSource(QPainter *painter)
252 {
253     Q_D(const QGraphicsEffect);
254     if (d->source)
255         d->source->draw(painter);
256 }
257 
258 /*!
259     Schedules a redraw of the source. Call this function whenever the source
260     needs to be redrawn.
261 
262     \sa QGraphicsEffect::updateBoundingRect(), QWidget::update(),
263         QGraphicsItem::update(),
264 */
update()265 void QGraphicsEffectSource::update()
266 {
267     d_func()->update();
268 }
269 
270 /*!
271     Returns \c true if the source effectively is a pixmap, e.g., a
272     QGraphicsPixmapItem.
273 
274     This function is useful for optimization purposes. For instance, there's no
275     point in drawing the source in device coordinates to avoid pixmap scaling
276     if this function returns \c true - the source pixmap will be scaled anyways.
277 */
isPixmap() const278 bool QGraphicsEffectSource::isPixmap() const
279 {
280     return d_func()->isPixmap();
281 }
282 
283 /*!
284     Returns \c true if the source effectively is a pixmap, e.g., a
285     QGraphicsPixmapItem.
286 
287     This function is useful for optimization purposes. For instance, there's no
288     point in drawing the source in device coordinates to avoid pixmap scaling
289     if this function returns \c true - the source pixmap will be scaled anyways.
290 */
sourceIsPixmap() const291 bool QGraphicsEffect::sourceIsPixmap() const
292 {
293     return source() ? source()->isPixmap() : false;
294 }
295 
296 /*!
297     Returns a pixmap with the source painted into it.
298 
299     The \a system specifies which coordinate system to be used for the source.
300     The optional \a offset parameter returns the offset where the pixmap should
301     be painted at using the current painter.
302 
303     The \a mode determines how much of the effect the pixmap will contain.
304     By default, the pixmap will contain the whole effect.
305 
306     The returned pixmap is bound to the current painter's device rectangle when
307     \a system is Qt::DeviceCoordinates.
308 
309     \sa QGraphicsEffect::draw(), boundingRect()
310 */
pixmap(Qt::CoordinateSystem system,QPoint * offset,QGraphicsEffect::PixmapPadMode mode) const311 QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offset, QGraphicsEffect::PixmapPadMode mode) const
312 {
313     Q_D(const QGraphicsEffectSource);
314 
315     // Shortcut, no cache for childless pixmap items...
316     const QGraphicsItem *item = graphicsItem();
317     if (system == Qt::LogicalCoordinates && mode == QGraphicsEffect::NoPad && item && isPixmap()) {
318         const QGraphicsPixmapItem *pixmapItem = static_cast<const QGraphicsPixmapItem *>(item);
319         if (offset)
320             *offset = pixmapItem->offset().toPoint();
321         return pixmapItem->pixmap();
322     }
323 
324     if (Q_UNLIKELY(system == Qt::DeviceCoordinates && item &&
325                    !static_cast<const QGraphicsItemEffectSourcePrivate *>(d_func())->info)) {
326         qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context");
327         return QPixmap();
328     }
329 
330     QPixmap pm;
331     if (item && d->m_cachedSystem == system && d->m_cachedMode == mode)
332         QPixmapCache::find(d->m_cacheKey, &pm);
333 
334     if (pm.isNull()) {
335         pm = d->pixmap(system, &d->m_cachedOffset, mode);
336         d->m_cachedSystem = system;
337         d->m_cachedMode = mode;
338 
339         d->invalidateCache();
340         d->m_cacheKey = QPixmapCache::insert(pm);
341     }
342 
343     if (offset)
344         *offset = d->m_cachedOffset;
345 
346     return pm;
347 }
348 
349 /*!
350     Returns a pixmap with the source painted into it.
351 
352     The \a system specifies which coordinate system to be used for the source.
353     The optional \a offset parameter returns the offset where the pixmap should
354     be painted at using the current painter. For control on how the pixmap is
355     padded use the \a mode parameter.
356 
357     The returned pixmap is clipped to the current painter's device rectangle when
358     \a system is Qt::DeviceCoordinates.
359 
360     Calling this function with Qt::DeviceCoordinates outside of
361     QGraphicsEffect::draw() will give undefined results, as there is no device
362     context available.
363 
364     \sa draw(), boundingRect()
365 */
sourcePixmap(Qt::CoordinateSystem system,QPoint * offset,QGraphicsEffect::PixmapPadMode mode) const366 QPixmap QGraphicsEffect::sourcePixmap(Qt::CoordinateSystem system, QPoint *offset, QGraphicsEffect::PixmapPadMode mode) const
367 {
368     Q_D(const QGraphicsEffect);
369     if (d->source)
370         return d->source->pixmap(system, offset, mode);
371     return QPixmap();
372 }
373 
~QGraphicsEffectSourcePrivate()374 QGraphicsEffectSourcePrivate::~QGraphicsEffectSourcePrivate()
375 {
376     invalidateCache();
377 }
378 
setCachedOffset(const QPoint & offset)379 void QGraphicsEffectSourcePrivate::setCachedOffset(const QPoint &offset)
380 {
381     m_cachedOffset = offset;
382 }
383 
invalidateCache(InvalidateReason reason) const384 void QGraphicsEffectSourcePrivate::invalidateCache(InvalidateReason reason) const
385 {
386     if (m_cachedMode != QGraphicsEffect::PadToEffectiveBoundingRect
387         && (reason == EffectRectChanged
388             || (reason == TransformChanged && m_cachedSystem == Qt::LogicalCoordinates))) {
389         return;
390     }
391 
392     QPixmapCache::remove(m_cacheKey);
393 }
394 
395 /*!
396     Constructs a new QGraphicsEffect instance having the
397     specified \a parent.
398 */
QGraphicsEffect(QObject * parent)399 QGraphicsEffect::QGraphicsEffect(QObject *parent)
400     : QObject(*new QGraphicsEffectPrivate, parent)
401 {
402 }
403 
404 /*!
405     \internal
406 */
QGraphicsEffect(QGraphicsEffectPrivate & dd,QObject * parent)407 QGraphicsEffect::QGraphicsEffect(QGraphicsEffectPrivate &dd, QObject *parent)
408     : QObject(dd, parent)
409 {
410 }
411 
412 /*!
413     Removes the effect from the source, and destroys the graphics effect.
414 */
~QGraphicsEffect()415 QGraphicsEffect::~QGraphicsEffect()
416 {
417     Q_D(QGraphicsEffect);
418     d->setGraphicsEffectSource(nullptr);
419 }
420 
421 /*!
422     Returns the effective bounding rectangle for this effect, i.e., the
423     bounding rectangle of the source in device coordinates, adjusted by
424     any margins applied by the effect itself.
425 
426     \sa boundingRectFor(), updateBoundingRect()
427 */
boundingRect() const428 QRectF QGraphicsEffect::boundingRect() const
429 {
430     Q_D(const QGraphicsEffect);
431     if (d->source)
432         return boundingRectFor(d->source->boundingRect());
433     return QRectF();
434 }
435 
436 /*!
437     Returns the effective bounding rectangle for this effect, given the
438     provided \a rect in the device coordinates. When writing
439     you own custom effect, you must call updateBoundingRect() whenever any
440     parameters are changed that may cause this this function to return a
441     different value.
442 
443     \sa sourceBoundingRect()
444 */
boundingRectFor(const QRectF & rect) const445 QRectF QGraphicsEffect::boundingRectFor(const QRectF &rect) const
446 {
447     return rect;
448 }
449 
450 /*!
451     \property QGraphicsEffect::enabled
452     \brief whether the effect is enabled or not.
453 
454     If an effect is disabled, the source will be rendered with as normal, with
455     no interference from the effect. If the effect is enabled, the source will
456     be rendered with the effect applied.
457 
458     This property is enabled by default.
459 
460     Using this property, you can disable certain effects on slow platforms, in
461     order to ensure that the user interface is responsive.
462 */
isEnabled() const463 bool QGraphicsEffect::isEnabled() const
464 {
465     Q_D(const QGraphicsEffect);
466     return d->isEnabled;
467 }
468 
setEnabled(bool enable)469 void QGraphicsEffect::setEnabled(bool enable)
470 {
471     Q_D(QGraphicsEffect);
472     if (d->isEnabled == enable)
473         return;
474 
475     d->isEnabled = enable;
476     if (d->source) {
477         d->source->d_func()->effectBoundingRectChanged();
478         d->source->d_func()->invalidateCache();
479     }
480     emit enabledChanged(enable);
481 }
482 
483 /*!
484     \fn void QGraphicsEffect::enabledChanged(bool enabled)
485 
486     This signal is emitted whenever the effect is enabled or disabled.
487     The \a enabled parameter holds the effects's new enabled state.
488 
489     \sa isEnabled()
490 */
491 
492 /*!
493     Schedules a redraw of the effect. Call this function whenever the effect
494     needs to be redrawn. This function does not trigger a redraw of the source.
495 
496     \sa updateBoundingRect()
497 */
update()498 void QGraphicsEffect::update()
499 {
500     Q_D(QGraphicsEffect);
501     if (d->source)
502         d->source->update();
503 }
504 
505 /*!
506     \internal
507 
508     Returns a pointer to the source, which provides extra context information
509     that can be useful for the effect.
510 
511     \sa draw()
512 */
source() const513 QGraphicsEffectSource *QGraphicsEffect::source() const
514 {
515     Q_D(const QGraphicsEffect);
516     return d->source;
517 }
518 
519 /*!
520     This function notifies the effect framework when the effect's bounding
521     rectangle has changed. As a custom effect author, you must call this
522     function whenever you change any parameters that will cause the virtual
523     boundingRectFor() function to return a different value.
524 
525     This function will call update() if this is necessary.
526 
527     \sa boundingRectFor(), boundingRect(), sourceBoundingRect()
528 */
updateBoundingRect()529 void QGraphicsEffect::updateBoundingRect()
530 {
531     Q_D(QGraphicsEffect);
532     if (d->source) {
533         d->source->d_func()->effectBoundingRectChanged();
534         d->source->d_func()->invalidateCache(QGraphicsEffectSourcePrivate::EffectRectChanged);
535     }
536 }
537 
538 /*!
539     \fn virtual void QGraphicsEffect::draw(QPainter *painter) = 0
540 
541     This pure virtual function draws the effect and is called whenever the
542     source needs to be drawn.
543 
544     Reimplement this function in a QGraphicsEffect subclass to provide the
545     effect's drawing implementation, using \a painter.
546 
547     For example:
548 
549     \snippet code/src_gui_effects_qgraphicseffect.cpp 1
550 
551     This function should not be called explicitly by the user, since it is
552     meant for reimplementation purposes only.
553 */
554 
555 /*!
556     \enum QGraphicsEffect::ChangeFlag
557 
558     This enum describes what has changed in QGraphicsEffectSource.
559 
560     \value SourceAttached The effect is installed on a source.
561     \value SourceDetached The effect is uninstalled on a source.
562     \value SourceBoundingRectChanged The bounding rect of the source has
563            changed.
564     \value SourceInvalidated The visual appearance of the source has changed.
565 */
566 
567 /*!
568     \enum QGraphicsEffect::PixmapPadMode
569 
570     This enum describes how the pixmap returned from sourcePixmap should be
571     padded.
572 
573     \value NoPad The pixmap should not receive any additional
574            padding.
575     \value PadToTransparentBorder The pixmap should be padded
576            to ensure it has a completely transparent border.
577     \value PadToEffectiveBoundingRect The pixmap should be padded to
578            match the effective bounding rectangle of the effect.
579 */
580 
581 /*!
582     This virtual function is called by QGraphicsEffect to notify the effect
583     that the source has changed. If the effect applies any cache, then this
584     cache must be purged in order to reflect the new appearance of the source.
585 
586     The \a flags describes what has changed.
587 */
sourceChanged(ChangeFlags flags)588 void QGraphicsEffect::sourceChanged(ChangeFlags flags)
589 {
590     Q_UNUSED(flags);
591 }
592 
593 /*!
594     \class QGraphicsColorizeEffect
595     \brief The QGraphicsColorizeEffect class provides a colorize effect.
596     \since 4.6
597     \inmodule QtWidgets
598 
599     A colorize effect renders the source with a tint of its color(). The color
600     can be modified using the setColor() function.
601 
602     By default, the color is light blue (QColor(0, 0, 192)).
603 
604     \image graphicseffect-colorize.png
605 
606     \sa QGraphicsDropShadowEffect, QGraphicsBlurEffect, QGraphicsOpacityEffect
607 */
608 
609 /*!
610     Constructs a new QGraphicsColorizeEffect instance.
611     The \a parent parameter is passed to QGraphicsEffect's constructor.
612 */
QGraphicsColorizeEffect(QObject * parent)613 QGraphicsColorizeEffect::QGraphicsColorizeEffect(QObject *parent)
614     : QGraphicsEffect(*new QGraphicsColorizeEffectPrivate, parent)
615 {
616 }
617 
618 /*!
619     Destroys the effect.
620 */
~QGraphicsColorizeEffect()621 QGraphicsColorizeEffect::~QGraphicsColorizeEffect()
622 {
623 }
624 
625 /*!
626     \property QGraphicsColorizeEffect::color
627     \brief the color of the effect.
628 
629     By default, the color is light blue (QColor(0, 0, 192)).
630 */
color() const631 QColor QGraphicsColorizeEffect::color() const
632 {
633     Q_D(const QGraphicsColorizeEffect);
634     return d->filter->color();
635 }
636 
setColor(const QColor & color)637 void QGraphicsColorizeEffect::setColor(const QColor &color)
638 {
639     Q_D(QGraphicsColorizeEffect);
640     if (d->filter->color() == color)
641         return;
642 
643     d->filter->setColor(color);
644     update();
645     emit colorChanged(color);
646 }
647 
648 /*!
649     \property QGraphicsColorizeEffect::strength
650     \brief the strength of the effect.
651 
652     By default, the strength is 1.0.
653     A strength 0.0 equals to no effect, while 1.0 means full colorization.
654 */
strength() const655 qreal QGraphicsColorizeEffect::strength() const
656 {
657     Q_D(const QGraphicsColorizeEffect);
658     return d->filter->strength();
659 }
660 
setStrength(qreal strength)661 void QGraphicsColorizeEffect::setStrength(qreal strength)
662 {
663     Q_D(QGraphicsColorizeEffect);
664     if (qFuzzyCompare(d->filter->strength(), strength))
665         return;
666 
667     d->filter->setStrength(strength);
668     d->opaque = !qFuzzyIsNull(strength);
669     update();
670     emit strengthChanged(strength);
671 }
672 
673 /*! \fn void QGraphicsColorizeEffect::strengthChanged(qreal strength)
674   This signal is emitted whenever setStrength() changes the colorize
675   strength property. \a strength contains the new strength value of
676   the colorize effect.
677  */
678 
679 /*!
680     \fn void QGraphicsColorizeEffect::colorChanged(const QColor &color)
681 
682     This signal is emitted whenever the effect's color changes.
683     The \a color parameter holds the effect's new color.
684 */
685 
686 /*!
687     \reimp
688 */
draw(QPainter * painter)689 void QGraphicsColorizeEffect::draw(QPainter *painter)
690 {
691     Q_D(QGraphicsColorizeEffect);
692 
693     if (!d->opaque) {
694         drawSource(painter);
695         return;
696     }
697 
698     QPoint offset;
699     if (sourceIsPixmap()) {
700         // No point in drawing in device coordinates (pixmap will be scaled anyways).
701         const QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, NoPad);
702         if (!pixmap.isNull())
703             d->filter->draw(painter, offset, pixmap);
704 
705         return;
706     }
707 
708     // Draw pixmap in deviceCoordinates to avoid pixmap scaling.
709     const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset);
710     if (pixmap.isNull())
711         return;
712 
713     QTransform restoreTransform = painter->worldTransform();
714     painter->setWorldTransform(QTransform());
715     d->filter->draw(painter, offset, pixmap);
716     painter->setWorldTransform(restoreTransform);
717 }
718 
719 /*!
720     \class QGraphicsBlurEffect
721     \brief The QGraphicsBlurEffect class provides a blur effect.
722     \since 4.6
723     \inmodule QtWidgets
724 
725     A blur effect blurs the source. This effect is useful for reducing details,
726     such as when the source loses focus and you want to draw attention to other
727     elements. The level of detail can be modified using the setBlurRadius()
728     function. Use setBlurHints() to choose the blur hints.
729 
730     By default, the blur radius is 5 pixels. The blur radius is specified in
731     device coordinates.
732 
733     \image graphicseffect-blur.png
734 
735     \sa QGraphicsDropShadowEffect, QGraphicsColorizeEffect, QGraphicsOpacityEffect
736 */
737 
738 /*!
739     \enum QGraphicsBlurEffect::BlurHint
740     \since 4.6
741 
742     This enum describes the possible hints that can be used to control how
743     blur effects are applied. The hints might not have an effect in all the
744     paint engines.
745 
746     \value PerformanceHint Indicates that rendering performance is the most important factor,
747     at the potential cost of lower quality.
748 
749     \value QualityHint Indicates that rendering quality is the most important factor,
750     at the potential cost of lower performance.
751 
752     \value AnimationHint Indicates that the blur radius is going to be animated, hinting
753     that the implementation can keep a cache of blurred verisons of the source.
754     Do not use this hint if the source is going to be dynamically changing.
755 
756     \sa blurHints(), setBlurHints()
757 */
758 
759 
760 /*!
761     Constructs a new QGraphicsBlurEffect instance.
762     The \a parent parameter is passed to QGraphicsEffect's constructor.
763 */
QGraphicsBlurEffect(QObject * parent)764 QGraphicsBlurEffect::QGraphicsBlurEffect(QObject *parent)
765     : QGraphicsEffect(*new QGraphicsBlurEffectPrivate, parent)
766 {
767     Q_D(QGraphicsBlurEffect);
768     d->filter->setBlurHints(QGraphicsBlurEffect::PerformanceHint);
769 }
770 
771 /*!
772     Destroys the effect.
773 */
~QGraphicsBlurEffect()774 QGraphicsBlurEffect::~QGraphicsBlurEffect()
775 {
776 }
777 
778 /*!
779     \property QGraphicsBlurEffect::blurRadius
780     \brief the blur radius of the effect.
781 
782     Using a smaller radius results in a sharper appearance, whereas a bigger
783     radius results in a more blurred appearance.
784 
785     By default, the blur radius is 5 pixels.
786 
787     The radius is given in device coordinates, meaning it is
788     unaffected by scale.
789 */
blurRadius() const790 qreal QGraphicsBlurEffect::blurRadius() const
791 {
792     Q_D(const QGraphicsBlurEffect);
793     return d->filter->radius();
794 }
795 
setBlurRadius(qreal radius)796 void QGraphicsBlurEffect::setBlurRadius(qreal radius)
797 {
798     Q_D(QGraphicsBlurEffect);
799     if (qFuzzyCompare(d->filter->radius(), radius))
800         return;
801 
802     d->filter->setRadius(radius);
803     updateBoundingRect();
804     emit blurRadiusChanged(radius);
805 }
806 
807 /*!
808     \fn void QGraphicsBlurEffect::blurRadiusChanged(qreal radius)
809 
810     This signal is emitted whenever the effect's blur radius changes.
811     The \a radius parameter holds the effect's new blur radius.
812 */
813 
814 /*!
815     \property QGraphicsBlurEffect::blurHints
816     \brief the blur hint of the effect.
817 
818     Use the PerformanceHint hint to say that you want a faster blur,
819     the QualityHint hint to say that you prefer a higher quality blur,
820     or the AnimationHint when you want to animate the blur radius.
821 
822     By default, the blur hint is PerformanceHint.
823 */
blurHints() const824 QGraphicsBlurEffect::BlurHints QGraphicsBlurEffect::blurHints() const
825 {
826     Q_D(const QGraphicsBlurEffect);
827     return d->filter->blurHints();
828 }
829 
setBlurHints(QGraphicsBlurEffect::BlurHints hints)830 void QGraphicsBlurEffect::setBlurHints(QGraphicsBlurEffect::BlurHints hints)
831 {
832     Q_D(QGraphicsBlurEffect);
833     if (d->filter->blurHints() == hints)
834         return;
835 
836     d->filter->setBlurHints(hints);
837     emit blurHintsChanged(hints);
838 }
839 
840 /*!
841     \fn void QGraphicsBlurEffect::blurHintsChanged(QGraphicsBlurEffect::BlurHints hints)
842 
843     This signal is emitted whenever the effect's blur hints changes.
844     The \a hints parameter holds the effect's new blur hints.
845 */
846 
847 /*!
848     \reimp
849 */
boundingRectFor(const QRectF & rect) const850 QRectF QGraphicsBlurEffect::boundingRectFor(const QRectF &rect) const
851 {
852     Q_D(const QGraphicsBlurEffect);
853     return d->filter->boundingRectFor(rect);
854 }
855 
856 /*!
857     \reimp
858 */
draw(QPainter * painter)859 void QGraphicsBlurEffect::draw(QPainter *painter)
860 {
861     Q_D(QGraphicsBlurEffect);
862     if (d->filter->radius() < 1) {
863         drawSource(painter);
864         return;
865     }
866 
867     PixmapPadMode mode = PadToEffectiveBoundingRect;
868 
869     QPoint offset;
870     QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, mode);
871     if (pixmap.isNull())
872         return;
873 
874     d->filter->draw(painter, offset, pixmap);
875 }
876 
877 /*!
878     \class QGraphicsDropShadowEffect
879     \brief The QGraphicsDropShadowEffect class provides a drop shadow effect.
880     \since 4.6
881     \inmodule QtWidgets
882 
883     A drop shadow effect renders the source with a drop shadow. The color of
884     the drop shadow can be modified using the setColor() function. The drop
885     shadow offset can be modified using the setOffset() function and the blur
886     radius of the drop shadow can be changed with the setBlurRadius()
887     function.
888 
889     By default, the drop shadow is a semi-transparent dark gray
890     (QColor(63, 63, 63, 180)) shadow, blurred with a radius of 1 at an offset
891     of 8 pixels towards the lower right. The drop shadow offset is specified
892     in device coordinates.
893 
894     \image graphicseffect-drop-shadow.png
895 
896     \sa QGraphicsBlurEffect, QGraphicsColorizeEffect, QGraphicsOpacityEffect
897 */
898 
899 /*!
900     Constructs a new QGraphicsDropShadowEffect instance.
901     The \a parent parameter is passed to QGraphicsEffect's constructor.
902 */
QGraphicsDropShadowEffect(QObject * parent)903 QGraphicsDropShadowEffect::QGraphicsDropShadowEffect(QObject *parent)
904     : QGraphicsEffect(*new QGraphicsDropShadowEffectPrivate, parent)
905 {
906 }
907 
908 /*!
909     Destroys the effect.
910 */
~QGraphicsDropShadowEffect()911 QGraphicsDropShadowEffect::~QGraphicsDropShadowEffect()
912 {
913 }
914 
915 /*!
916     \property QGraphicsDropShadowEffect::offset
917     \brief the shadow offset in pixels.
918 
919     By default, the offset is 8 pixels towards the lower right.
920 
921     The offset is given in device coordinates, which means it is
922     unaffected by scale.
923 
924     \sa xOffset(), yOffset(), blurRadius(), color()
925 */
offset() const926 QPointF QGraphicsDropShadowEffect::offset() const
927 {
928     Q_D(const QGraphicsDropShadowEffect);
929     return d->filter->offset();
930 }
931 
setOffset(const QPointF & offset)932 void QGraphicsDropShadowEffect::setOffset(const QPointF &offset)
933 {
934     Q_D(QGraphicsDropShadowEffect);
935     if (d->filter->offset() == offset)
936         return;
937 
938     d->filter->setOffset(offset);
939     updateBoundingRect();
940     emit offsetChanged(offset);
941 }
942 
943 /*!
944     \property QGraphicsDropShadowEffect::xOffset
945     \brief the horizontal shadow offset in pixels.
946 
947     By default, the horizontal shadow offset is 8 pixels.
948 
949 
950 
951     \sa yOffset(), offset()
952 */
953 
954 /*!
955     \property QGraphicsDropShadowEffect::yOffset
956     \brief the vertical shadow offset in pixels.
957 
958     By default, the vertical shadow offset is 8 pixels.
959 
960     \sa xOffset(), offset()
961 */
962 
963 /*!
964     \fn void QGraphicsDropShadowEffect::offsetChanged(const QPointF &offset)
965 
966     This signal is emitted whenever the effect's shadow offset changes.
967     The \a offset parameter holds the effect's new shadow offset.
968 */
969 
970 /*!
971     \property QGraphicsDropShadowEffect::blurRadius
972     \brief the blur radius in pixels of the drop shadow.
973 
974     Using a smaller radius results in a sharper shadow, whereas using a bigger
975     radius results in a more blurred shadow.
976 
977     By default, the blur radius is 1 pixel.
978 
979     \sa color(), offset()
980 */
blurRadius() const981 qreal QGraphicsDropShadowEffect::blurRadius() const
982 {
983     Q_D(const QGraphicsDropShadowEffect);
984     return d->filter->blurRadius();
985 }
986 
setBlurRadius(qreal blurRadius)987 void QGraphicsDropShadowEffect::setBlurRadius(qreal blurRadius)
988 {
989     Q_D(QGraphicsDropShadowEffect);
990     if (qFuzzyCompare(d->filter->blurRadius(), blurRadius))
991         return;
992 
993     d->filter->setBlurRadius(blurRadius);
994     updateBoundingRect();
995     emit blurRadiusChanged(blurRadius);
996 }
997 
998 /*!
999     \fn void QGraphicsDropShadowEffect::blurRadiusChanged(qreal blurRadius)
1000 
1001     This signal is emitted whenever the effect's blur radius changes.
1002     The \a blurRadius parameter holds the effect's new blur radius.
1003 */
1004 
1005 /*!
1006     \property QGraphicsDropShadowEffect::color
1007     \brief the color of the drop shadow.
1008 
1009     By default, the drop color is a semi-transparent dark gray
1010     (QColor(63, 63, 63, 180)).
1011 
1012     \sa offset(), blurRadius()
1013 */
color() const1014 QColor QGraphicsDropShadowEffect::color() const
1015 {
1016     Q_D(const QGraphicsDropShadowEffect);
1017     return d->filter->color();
1018 }
1019 
setColor(const QColor & color)1020 void QGraphicsDropShadowEffect::setColor(const QColor &color)
1021 {
1022     Q_D(QGraphicsDropShadowEffect);
1023     if (d->filter->color() == color)
1024         return;
1025 
1026     d->filter->setColor(color);
1027     update();
1028     emit colorChanged(color);
1029 }
1030 
1031 /*!
1032     \fn void QGraphicsDropShadowEffect::colorChanged(const QColor &color)
1033 
1034     This signal is emitted whenever the effect's color changes.
1035     The \a color parameter holds the effect's new color.
1036 */
1037 
1038 /*!
1039     \reimp
1040 */
boundingRectFor(const QRectF & rect) const1041 QRectF QGraphicsDropShadowEffect::boundingRectFor(const QRectF &rect) const
1042 {
1043     Q_D(const QGraphicsDropShadowEffect);
1044     return d->filter->boundingRectFor(rect);
1045 }
1046 
1047 /*!
1048     \reimp
1049 */
draw(QPainter * painter)1050 void QGraphicsDropShadowEffect::draw(QPainter *painter)
1051 {
1052     Q_D(QGraphicsDropShadowEffect);
1053     if (d->filter->blurRadius() <= 0 && d->filter->offset().isNull()) {
1054         drawSource(painter);
1055         return;
1056     }
1057 
1058     PixmapPadMode mode = PadToEffectiveBoundingRect;
1059 
1060     // Draw pixmap in device coordinates to avoid pixmap scaling.
1061     QPoint offset;
1062     const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, mode);
1063     if (pixmap.isNull())
1064         return;
1065 
1066     QTransform restoreTransform = painter->worldTransform();
1067     painter->setWorldTransform(QTransform());
1068     d->filter->draw(painter, offset, pixmap);
1069     painter->setWorldTransform(restoreTransform);
1070 }
1071 
1072 /*!
1073     \class QGraphicsOpacityEffect
1074     \brief The QGraphicsOpacityEffect class provides an opacity effect.
1075     \since 4.6
1076     \inmodule QtWidgets
1077 
1078     An opacity effect renders the source with an opacity. This effect is useful
1079     for making the source semi-transparent, similar to a fade-in/fade-out
1080     sequence. The opacity can be modified using the setOpacity() function.
1081 
1082     By default, the opacity is 0.7.
1083 
1084     \image graphicseffect-opacity.png
1085 
1086     \sa QGraphicsDropShadowEffect, QGraphicsBlurEffect, QGraphicsColorizeEffect
1087 */
1088 
1089 /*!
1090     Constructs a new QGraphicsOpacityEffect instance.
1091     The \a parent parameter is passed to QGraphicsEffect's constructor.
1092 */
QGraphicsOpacityEffect(QObject * parent)1093 QGraphicsOpacityEffect::QGraphicsOpacityEffect(QObject *parent)
1094     : QGraphicsEffect(*new QGraphicsOpacityEffectPrivate, parent)
1095 {
1096 }
1097 
1098 /*!
1099     Destroys the effect.
1100 */
~QGraphicsOpacityEffect()1101 QGraphicsOpacityEffect::~QGraphicsOpacityEffect()
1102 {
1103 }
1104 
1105 /*!
1106     \property QGraphicsOpacityEffect::opacity
1107     \brief the opacity of the effect.
1108 
1109     The value should be in the range of 0.0 to 1.0, where 0.0 is
1110     fully transparent and 1.0 is fully opaque.
1111 
1112     By default, the opacity is 0.7.
1113 
1114     \sa setOpacityMask()
1115 */
opacity() const1116 qreal QGraphicsOpacityEffect::opacity() const
1117 {
1118     Q_D(const QGraphicsOpacityEffect);
1119     return d->opacity;
1120 }
1121 
setOpacity(qreal opacity)1122 void QGraphicsOpacityEffect::setOpacity(qreal opacity)
1123 {
1124     Q_D(QGraphicsOpacityEffect);
1125     opacity = qBound(qreal(0.0), opacity, qreal(1.0));
1126 
1127     if (qFuzzyCompare(d->opacity, opacity))
1128         return;
1129 
1130     d->opacity = opacity;
1131     if ((d->isFullyTransparent = qFuzzyIsNull(d->opacity)))
1132         d->isFullyOpaque = 0;
1133     else
1134         d->isFullyOpaque = qFuzzyIsNull(d->opacity - 1);
1135     update();
1136     emit opacityChanged(opacity);
1137 }
1138 
1139 /*!
1140     \fn void QGraphicsOpacityEffect::opacityChanged(qreal opacity)
1141 
1142     This signal is emitted whenever the effect's opacity changes.
1143     The \a opacity parameter holds the effect's new opacity.
1144 */
1145 
1146 /*!
1147     \property QGraphicsOpacityEffect::opacityMask
1148     \brief the opacity mask of the effect.
1149 
1150     An opacity mask allows you apply opacity to portions of an element.
1151 
1152     For example:
1153 
1154     \snippet code/src_gui_effects_qgraphicseffect.cpp 2
1155 
1156     There is no opacity mask by default.
1157 
1158     \sa setOpacity()
1159 */
opacityMask() const1160 QBrush QGraphicsOpacityEffect::opacityMask() const
1161 {
1162     Q_D(const QGraphicsOpacityEffect);
1163     return d->opacityMask;
1164 }
1165 
setOpacityMask(const QBrush & mask)1166 void QGraphicsOpacityEffect::setOpacityMask(const QBrush &mask)
1167 {
1168     Q_D(QGraphicsOpacityEffect);
1169     if (d->opacityMask == mask)
1170         return;
1171 
1172     d->opacityMask = mask;
1173     d->hasOpacityMask = (mask.style() != Qt::NoBrush);
1174     update();
1175 
1176     emit opacityMaskChanged(mask);
1177 }
1178 
1179 /*!
1180     \fn void QGraphicsOpacityEffect::opacityMaskChanged(const QBrush &mask)
1181 
1182     This signal is emitted whenever the effect's opacity mask changes.
1183     The \a mask parameter holds the effect's new opacity mask.
1184 */
1185 
1186 /*!
1187     \reimp
1188 */
draw(QPainter * painter)1189 void QGraphicsOpacityEffect::draw(QPainter *painter)
1190 {
1191     Q_D(QGraphicsOpacityEffect);
1192 
1193     // Transparent; nothing to draw.
1194     if (d->isFullyTransparent)
1195         return;
1196 
1197     // Opaque; draw directly without going through a pixmap.
1198     if (d->isFullyOpaque && !d->hasOpacityMask) {
1199         drawSource(painter);
1200         return;
1201     }
1202 
1203     QPoint offset;
1204     Qt::CoordinateSystem system = sourceIsPixmap() ? Qt::LogicalCoordinates : Qt::DeviceCoordinates;
1205     QPixmap pixmap = sourcePixmap(system, &offset, QGraphicsEffect::NoPad);
1206     if (pixmap.isNull())
1207         return;
1208 
1209     painter->save();
1210     painter->setOpacity(d->opacity);
1211 
1212     if (d->hasOpacityMask) {
1213         QPainter pixmapPainter(&pixmap);
1214         pixmapPainter.setRenderHints(painter->renderHints());
1215         pixmapPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
1216         if (system == Qt::DeviceCoordinates) {
1217             QTransform worldTransform = painter->worldTransform();
1218             worldTransform *= QTransform::fromTranslate(-offset.x(), -offset.y());
1219             pixmapPainter.setWorldTransform(worldTransform);
1220             pixmapPainter.fillRect(sourceBoundingRect(), d->opacityMask);
1221         } else {
1222             pixmapPainter.translate(-offset);
1223             pixmapPainter.fillRect(pixmap.rect(), d->opacityMask);
1224         }
1225     }
1226 
1227     if (system == Qt::DeviceCoordinates)
1228         painter->setWorldTransform(QTransform());
1229 
1230     painter->drawPixmap(offset, pixmap);
1231     painter->restore();
1232 }
1233 
1234 
1235 QT_END_NAMESPACE
1236 
1237 #include "moc_qgraphicseffect.cpp"
1238 #include "moc_qgraphicseffect_p.cpp"
1239