1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtypes.h"
5#include <private/qquickmultieffect_p_p.h>
6#include <private/qquickshadereffect_p.h>
7#include <private/qquickshadereffectsource_p.h>
8
9QT_BEGIN_NAMESPACE
10
11Q_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
12
13/*!
14 \keyword Qt Quick Effects
15 \qmlmodule QtQuick.Effects
16 \title Qt Quick Effects QML Types
17 \ingroup qmlmodules
18 \brief Provides QML types for applying one or more simple graphical effects to Qt Quick items.
19
20 To use the types in this module, import the module with the following line:
21
22 \qml
23 import QtQuick.Effects
24 \endqml
25*/
26
27/*!
28 \qmltype MultiEffect
29 \instantiates QQuickMultiEffect
30 \inqmlmodule QtQuick.Effects
31 \inherits Item
32 \ingroup qtquick-effects
33 \brief Applies post-processing effect to an item.
34
35 The MultiEffect type, the successor to the deprecated Qt Graphical Effects
36 from Qt 5, applies a post-processing effect to the \l source item. Compared
37 to the Qt Graphical Effects module, MultiEffect combines multiple
38 effects (blur, shadow, colorization etc.) into a single item and shader
39 which makes it better for multiple effects. There are several shader
40 variations and the most optimal one gets selected based on the features
41 used.
42
43 MultiEffect is designed specifically for most common effects and can be easily animated.
44 If the MultiEffect doesn't contain the effect you need, consider implementing a custom
45 effect using \l {Qt Quick Effect Maker}. For more information about shader effects,
46 see the \l ShaderEffect reference documentation.
47
48 \section1 Example Usage
49
50 The following simple example shows how to apply a saturation effect on an item:
51
52 \table 70%
53 \row
54 \li \image multieffect-example1.png
55 \li \qml
56 import QtQuick
57 import QtQuick.Effects
58
59 ...
60 Image {
61 id: sourceItem
62 source: "qt_logo_green_rgb.png"
63 visible: false
64 }
65 MultiEffect {
66 source: sourceItem
67 anchors.fill: sourceItem
68 saturation: -1.0
69 }
70 \endqml
71 \endtable
72
73 Here is a bit more complex example, applying multiple effects at the same time:
74
75 \table 70%
76 \row
77 \li \image multieffect-example2.png
78 \li \qml
79 import QtQuick
80 import QtQuick.Effects
81
82 ...
83 MultiEffect {
84 source: sourceItem
85 anchors.fill: sourceItem
86 brightness: 0.4
87 saturation: 0.2
88 blurEnabled: true
89 blurMax: 64
90 blur: 1.0
91 }
92 \endqml
93 \endtable
94
95 Below is an example of how to use the mask, colorization and brightness effects together to
96 fade away an element. This kind of hiding/showing of elements can be, for example, bind
97 to a slider value or animations like NumberAnimation. Note how the \c visible property is
98 false when the item is totally faded away, to avoid unnecessary rendering of the effect.
99
100 \table 70%
101 \row
102 \li \image multieffect-example3.png
103 \li \qml
104 import QtQuick
105 import QtQuick.Effects
106 import QtQuick.Controls.Material
107
108 ...
109 MultiEffect {
110 property real effectAmount: effectSlider.value
111 source: sourceItem
112 anchors.fill: sourceItem
113 brightness: effectAmount
114 colorizationColor: "#ff20d0"
115 colorization: effectAmount
116 maskEnabled: true
117 maskSource: Image {
118 source: "mask.png"
119 }
120 maskSpreadAtMin: 0.2
121 maskThresholdMin: effectAmount
122 visible: effectAmount < 1.0
123 }
124 Slider {
125 id: effectSlider
126 anchors.bottom: parent.bottom
127 anchors.horizontalCenter: parent.horizontalCenter
128 }
129 \endqml
130 \endtable
131
132 \section1 Performance
133 There are a few things to consider for optimal performance:
134 \list
135 \li To get the most optimal shader, enable only the effects which you actually use
136 (see \l blurEnabled, \l shadowEnabled, \l maskEnabled). Simple color effects (\l brightness,
137 \l contrast, \l saturation, \l colorization) are always enabled, so using them doesn't
138 add extra overhead.
139 \li See the \b {Performance notes} of the properties which may change the shader or the effect
140 item size and don't modify these during animations.
141 \li When the MultiEffect isn't used, remember to set its \c visible property to be false to
142 avoid rendering the effects in the background.
143 \li Blur and shadow are the heaviest effects. With these, prefer increasing \l blurMultiplier
144 over \l blurMax and avoid using \l source items which animate, so blurring doesn't need
145 to be regenerated in every frame.
146 \li Apply effects to optimally sized QML elements as more pixels means more work for
147 the GPU. When applying the blur effect to the whole background, remember to set
148 \l autoPaddingEnabled false or the effect grows "outside" the window / screen.
149 \endlist
150*/
151
152/*!
153 \qmlsignal QtQuick.Effects::MultiEffect::shaderChanged()
154
155 This signal is emitted when the used shader changes.
156 \sa fragmentShader, vertexShader
157*/
158
159QQuickMultiEffect::QQuickMultiEffect(QQuickItem *parent)
160 : QQuickItem(*new QQuickMultiEffectPrivate, parent)
161{
162 setFlag(flag: ItemHasContents);
163}
164
165QQuickMultiEffect::~QQuickMultiEffect()
166{
167}
168
169/*!
170 \qmlproperty Item QtQuick.Effects::MultiEffect::source
171
172 This property holds the item to be used as a source for the effect.
173 If needed, MultiEffect will internally generate a \l ShaderEffectSource
174 as the texture source.
175
176 \note It is not supported to let the effect include itself, for instance
177 by setting source to the effect's parent.
178
179 \note If the source item has \l {QtQuick::Item::layer.enabled} {layer.enabled} set to true,
180 it will be used directly. This is good for the performance and often desired, when the source
181 is hidden. But if the source remains visible and the effect adds padding (autoPaddingEnabled,
182 paddingRect), that padding can affect the appearance of the source item.
183
184 \sa hasProxySource
185*/
186QQuickItem *QQuickMultiEffect::source() const
187{
188 Q_D(const QQuickMultiEffect);
189 return d->source();
190}
191
192void QQuickMultiEffect::setSource(QQuickItem *item)
193{
194 Q_D(QQuickMultiEffect);
195 d->setSource(item);
196}
197
198/*!
199 \qmlproperty bool QtQuick.Effects::MultiEffect::autoPaddingEnabled
200
201 When blur or shadow effects are enabled and this is set to true (default),
202 the item size is padded automatically based on blurMax and blurMultiplier.
203 Note that \l paddingRect is always added to the size.
204
205 \image multieffect-example4.png
206
207 \sa paddingRect
208
209 \include notes.qdocinc performance item size
210
211 \include notes.qdocinc performance item resize
212*/
213bool QQuickMultiEffect::autoPaddingEnabled() const
214{
215 Q_D(const QQuickMultiEffect);
216 return d->autoPaddingEnabled();
217}
218
219void QQuickMultiEffect::setAutoPaddingEnabled(bool enabled)
220{
221 Q_D(QQuickMultiEffect);
222 d->setAutoPaddingEnabled(enabled);
223}
224
225/*!
226 \qmlproperty rect QtQuick.Effects::MultiEffect::paddingRect
227
228 Set this to increase item size manually so that blur and/or shadows will fit.
229 If autoPaddingEnabled is true and paddingRect is not set, the item is padded
230 to fit maximally blurred item based on blurMax and blurMultiplier. When
231 enabling the shadow, you typically need to take \l shadowHorizontalOffset and
232 \l shadowVerticalOffset into account and adjust this paddingRect accordingly.
233
234 Below is an example of adjusting paddingRect with autoPaddingEnabled set to
235 false so that the shadow fits inside the MultiEffect item.
236
237 \image multieffect-example5.png
238
239 \sa autoPaddingEnabled
240
241 \include notes.qdocinc performance item size
242
243 \include notes.qdocinc performance item resize
244*/
245QRectF QQuickMultiEffect::paddingRect() const
246{
247 Q_D(const QQuickMultiEffect);
248 return d->paddingRect();
249}
250
251void QQuickMultiEffect::setPaddingRect(const QRectF &rect)
252{
253 Q_D(QQuickMultiEffect);
254 d->setPaddingRect(rect);
255}
256
257/*!
258 \qmlproperty real QtQuick.Effects::MultiEffect::brightness
259
260 This property defines how much the source brightness is increased or
261 decreased.
262
263 The value ranges from -1.0 to 1.0. By default, the property is set to \c
264 0.0 (no change).
265*/
266qreal QQuickMultiEffect::brightness() const
267{
268 Q_D(const QQuickMultiEffect);
269 return d->brightness();
270}
271
272void QQuickMultiEffect::setBrightness(qreal brightness)
273{
274 Q_D(QQuickMultiEffect);
275 d->setBrightness(brightness);
276}
277
278/*!
279 \qmlproperty real QtQuick.Effects::MultiEffect::contrast
280
281 This property defines how much the source contrast is increased or
282 decreased.
283
284 The value ranges from -1.0 to 1.0. By default, the property is set to \c
285 0.0 (no change).
286*/
287qreal QQuickMultiEffect::contrast() const
288{
289 Q_D(const QQuickMultiEffect);
290 return d->contrast();
291}
292
293void QQuickMultiEffect::setContrast(qreal contrast)
294{
295 Q_D(QQuickMultiEffect);
296 d->setContrast(contrast);
297}
298
299/*!
300 \qmlproperty real QtQuick.Effects::MultiEffect::saturation
301
302 This property defines how much the source saturation is increased or
303 decreased.
304
305 The value ranges from -1.0 (totally desaturated) to inf. By default,
306 the property is set to \c 0.0 (no change).
307*/
308qreal QQuickMultiEffect::saturation() const
309{
310 Q_D(const QQuickMultiEffect);
311 return d->saturation();
312}
313
314void QQuickMultiEffect::setSaturation(qreal saturation)
315{
316 Q_D(QQuickMultiEffect);
317 d->setSaturation(saturation);
318}
319
320/*!
321 \qmlproperty real QtQuick.Effects::MultiEffect::colorization
322
323 This property defines how much the source is colorized with the
324 colorizationColor.
325
326 The value ranges from 0.0 (not colorized) to 1.0 (fully colorized).
327 By default, the property is set to \c 0.0 (no change).
328*/
329qreal QQuickMultiEffect::colorization() const
330{
331 Q_D(const QQuickMultiEffect);
332 return d->colorization();
333}
334
335void QQuickMultiEffect::setColorization(qreal colorization)
336{
337 Q_D(QQuickMultiEffect);
338 d->setColorization(colorization);
339}
340
341/*!
342 \qmlproperty color QtQuick.Effects::MultiEffect::colorizationColor
343
344 This property defines the RGBA color value which is used to
345 colorize the source.
346
347 By default, the property is set to \c {Qt.rgba(1.0, 0.0, 0.0, 1.0)} (red).
348
349 \sa colorization
350*/
351QColor QQuickMultiEffect::colorizationColor() const
352{
353 Q_D(const QQuickMultiEffect);
354 return d->colorizationColor();
355}
356
357void QQuickMultiEffect::setColorizationColor(const QColor &color)
358{
359 Q_D(QQuickMultiEffect);
360 d->setColorizationColor(color);
361}
362
363/*!
364 \qmlproperty bool QtQuick.Effects::MultiEffect::blurEnabled
365
366 Enables the blur effect.
367
368 \include notes.qdocinc performance shader regen
369*/
370bool QQuickMultiEffect::blurEnabled() const
371{
372 Q_D(const QQuickMultiEffect);
373 return d->blurEnabled();
374}
375
376void QQuickMultiEffect::setBlurEnabled(bool enabled)
377{
378 Q_D(QQuickMultiEffect);
379 d->setBlurEnabled(enabled);
380}
381
382/*!
383 \qmlproperty real QtQuick.Effects::MultiEffect::blur
384
385 This property defines how much blur (radius) is applied to the source.
386
387 The value ranges from 0.0 (no blur) to 1.0 (full blur). By default,
388 the property is set to \c 0.0 (no change). The amount of full blur
389 is affected by blurMax and blurMultiplier.
390
391 \b {Performance note:} If you don't need to go close to 1.0 at any point
392 of blur animations, consider reducing blurMax or blurMultiplier for
393 optimal performance.
394*/
395qreal QQuickMultiEffect::blur() const
396{
397 Q_D(const QQuickMultiEffect);
398 return d->blur();
399}
400
401void QQuickMultiEffect::setBlur(qreal blur)
402{
403 Q_D(QQuickMultiEffect);
404 d->setBlur(blur);
405}
406
407/*!
408 \qmlproperty int QtQuick.Effects::MultiEffect::blurMax
409
410 This property defines the maximum pixel radius that blur with value
411 1.0 will reach.
412
413 Meaningful range of this value is from 2 (subtle blur) to 64 (high
414 blur). By default, the property is set to \c 32. For the most optimal
415 performance, select as small value as you need.
416
417 \note This affects to both blur and shadow effects.
418
419 \include notes.qdocinc performance shader regen
420
421 \include notes.qdocinc performance item resize
422*/
423int QQuickMultiEffect::blurMax() const
424{
425 Q_D(const QQuickMultiEffect);
426 return d->blurMax();
427}
428
429void QQuickMultiEffect::setBlurMax(int blurMax)
430{
431 Q_D(QQuickMultiEffect);
432 d->setBlurMax(blurMax);
433}
434
435/*!
436 \qmlproperty real QtQuick.Effects::MultiEffect::blurMultiplier
437
438 This property defines a multiplier for extending the blur radius.
439
440 The value ranges from 0.0 (not multiplied) to inf. By default,
441 the property is set to \c 0.0. Incresing the multiplier extends the
442 blur radius, but decreases the blur quality. This is more performant
443 option for a bigger blur radius than blurMax as it doesn't increase
444 the amount of texture lookups.
445
446 \note This affects to both blur and shadow effects.
447
448 \include notes.qdocinc performance item resize
449*/
450qreal QQuickMultiEffect::blurMultiplier() const
451{
452 Q_D(const QQuickMultiEffect);
453 return d->blurMultiplier();
454}
455
456void QQuickMultiEffect::setBlurMultiplier(qreal blurMultiplier)
457{
458 Q_D(QQuickMultiEffect);
459 d->setBlurMultiplier(blurMultiplier);
460}
461
462/*!
463 \qmlproperty bool QtQuick.Effects::MultiEffect::shadowEnabled
464
465 Enables the shadow effect.
466
467 \include notes.qdocinc performance shader regen
468*/
469bool QQuickMultiEffect::shadowEnabled() const
470{
471 Q_D(const QQuickMultiEffect);
472 return d->shadowEnabled();
473}
474
475void QQuickMultiEffect::setShadowEnabled(bool enabled)
476{
477 Q_D(QQuickMultiEffect);
478 d->setShadowEnabled(enabled);
479}
480
481/*!
482 \qmlproperty real QtQuick.Effects::MultiEffect::shadowOpacity
483
484 This property defines the opacity of the drop shadow. This value
485 is multiplied with the \c shadowColor alpha value.
486
487 The value ranges from 0.0 (fully transparent) to 1.0 (fully opaque).
488 By default, the property is set to \c 1.0.
489*/
490qreal QQuickMultiEffect::shadowOpacity() const
491{
492 Q_D(const QQuickMultiEffect);
493 return d->shadowOpacity();
494}
495
496void QQuickMultiEffect::setShadowOpacity(qreal shadowOpacity)
497{
498 Q_D(QQuickMultiEffect);
499 d->setShadowOpacity(shadowOpacity);
500}
501
502/*!
503 \qmlproperty real QtQuick.Effects::MultiEffect::shadowBlur
504
505 This property defines how much blur (radius) is applied to the shadow.
506
507 The value ranges from 0.0 (no blur) to 1.0 (full blur). By default,
508 the property is set to \c 1.0. The amount of full blur
509 is affected by blurMax and blurMultiplier.
510
511 \b {Performance note:} The most optimal way to reduce shadow blurring is
512 to make blurMax smaller (if it isn't needed for item blur). Just remember
513 to not adjust blurMax during animations.
514*/
515qreal QQuickMultiEffect::shadowBlur() const
516{
517 Q_D(const QQuickMultiEffect);
518 return d->shadowBlur();
519}
520
521void QQuickMultiEffect::setShadowBlur(qreal shadowBlur)
522{
523 Q_D(QQuickMultiEffect);
524 d->setShadowBlur(shadowBlur);
525}
526
527/*!
528 \qmlproperty real QtQuick.Effects::MultiEffect::shadowHorizontalOffset
529
530 This property defines the horizontal offset of the shadow from the
531 item center.
532
533 The value ranges from -inf to inf. By default, the property is set
534 to \c 0.0.
535
536 \note When moving shadow position away from center and adding
537 shadowBlur, you possibly also need to increase the paddingRect
538 accordingly if you want the shadow to not be clipped.
539*/
540qreal QQuickMultiEffect::shadowHorizontalOffset() const
541{
542 Q_D(const QQuickMultiEffect);
543 return d->shadowHorizontalOffset();
544}
545
546void QQuickMultiEffect::setShadowHorizontalOffset(qreal offset)
547{
548 Q_D(QQuickMultiEffect);
549 d->setShadowHorizontalOffset(offset);
550}
551
552/*!
553 \qmlproperty real QtQuick.Effects::MultiEffect::shadowVerticalOffset
554
555 This property defines the vertical offset of the shadow from the
556 item center.
557
558 The value ranges from -inf to inf. By default, the property is set
559 to \c 0.0.
560
561 \note When moving shadow position away from center and adding
562 shadowBlur, you possibly also need to increase the paddingRect
563 accordingly if you want the shadow to not be clipped.
564*/
565qreal QQuickMultiEffect::shadowVerticalOffset() const
566{
567 Q_D(const QQuickMultiEffect);
568 return d->shadowVerticalOffset();
569}
570
571void QQuickMultiEffect::setShadowVerticalOffset(qreal offset)
572{
573 Q_D(QQuickMultiEffect);
574 d->setShadowVerticalOffset(offset);
575}
576
577/*!
578 \qmlproperty color QtQuick.Effects::MultiEffect::shadowColor
579
580 This property defines the RGBA color value which is used for
581 the shadow. It is useful for example when a shadow is used for
582 simulating a glow effect.
583
584 By default, the property is set to \c {Qt.rgba(0.0, 0.0, 0.0, 1.0)}
585 (black).
586*/
587QColor QQuickMultiEffect::shadowColor() const
588{
589 Q_D(const QQuickMultiEffect);
590 return d->shadowColor();
591}
592
593void QQuickMultiEffect::setShadowColor(const QColor &color)
594{
595 Q_D(QQuickMultiEffect);
596 d->setShadowColor(color);
597}
598
599/*!
600 \qmlproperty real QtQuick.Effects::MultiEffect::shadowScale
601
602 This property defines the scale of the shadow. Scaling is applied from
603 the center of the item.
604
605 The value ranges from 0 to inf. By default, the property is set to
606 \c 1.0.
607
608 \note When increasing the shadowScale, you possibly also need to
609 increase the paddingRect accordingly to avoid the shadow from being
610 clipped.
611*/
612qreal QQuickMultiEffect::shadowScale() const
613{
614 Q_D(const QQuickMultiEffect);
615 return d->shadowScale();
616}
617
618void QQuickMultiEffect::setShadowScale(qreal shadowScale)
619{
620 Q_D(QQuickMultiEffect);
621 d->setShadowScale(shadowScale);
622}
623
624/*!
625 \qmlproperty bool QtQuick.Effects::MultiEffect::maskEnabled
626
627 Enables the mask effect.
628
629 \include notes.qdocinc performance shader regen
630*/
631bool QQuickMultiEffect::maskEnabled() const
632{
633 Q_D(const QQuickMultiEffect);
634 return d->maskEnabled();
635}
636
637void QQuickMultiEffect::setMaskEnabled(bool enabled)
638{
639 Q_D(QQuickMultiEffect);
640 d->setMaskEnabled(enabled);
641}
642
643/*!
644 \qmlproperty Item QtQuick.Effects::MultiEffect::maskSource
645
646 Source item for the mask effect. Should point to ShaderEffectSource,
647 item with \l {QtQuick::Item::layer.enabled} {layer.enabled} set to \c true,
648 or to an item that can be directly used as a texture source (e.g.
649 \l [QML] Image). The alpha channel of the source item is used for masking.
650*/
651QQuickItem *QQuickMultiEffect::maskSource() const
652{
653 Q_D(const QQuickMultiEffect);
654 return d->maskSource();
655}
656
657void QQuickMultiEffect::setMaskSource(QQuickItem *item)
658{
659 Q_D(QQuickMultiEffect);
660 d->setMaskSource(item);
661}
662
663/*!
664 \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMin
665
666 This property defines a lower threshold value for the mask pixels.
667 The mask pixels that have an alpha value below this property are used
668 to completely mask away the corresponding pixels from the source item.
669 The mask pixels that have a higher alpha value are used to alphablend
670 the source item to the display.
671
672 The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By
673 default, the property is set to \c 0.0.
674*/
675qreal QQuickMultiEffect::maskThresholdMin() const
676{
677 Q_D(const QQuickMultiEffect);
678 return d->maskThresholdMin();
679}
680
681void QQuickMultiEffect::setMaskThresholdMin(qreal threshold)
682{
683 Q_D(QQuickMultiEffect);
684 d->setMaskThresholdMin(threshold);
685}
686
687/*!
688 \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMin
689
690 This property defines the smoothness of the mask edges near the
691 maskThresholdMin. Setting higher spread values softens the transition
692 from the transparent mask pixels towards opaque mask pixels by adding
693 interpolated values between them.
694
695 The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge).
696 By default, the property is set to \c 0.0.
697*/
698qreal QQuickMultiEffect::maskSpreadAtMin() const
699{
700 Q_D(const QQuickMultiEffect);
701 return d->maskSpreadAtMin();
702}
703
704void QQuickMultiEffect::setMaskSpreadAtMin(qreal spread)
705{
706 Q_D(QQuickMultiEffect);
707 d->setMaskSpreadAtMin(spread);
708}
709
710/*!
711 \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMax
712
713 This property defines an upper threshold value for the mask pixels.
714 The mask pixels that have an alpha value below this property are used
715 to completely mask away the corresponding pixels from the source item.
716 The mask pixels that have a higher alpha value are used to alphablend
717 the source item to the display.
718
719 The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By
720 default, the property is set to \c 1.0.
721*/
722qreal QQuickMultiEffect::maskThresholdMax() const
723{
724 Q_D(const QQuickMultiEffect);
725 return d->maskThresholdMax();
726}
727
728void QQuickMultiEffect::setMaskThresholdMax(qreal threshold)
729{
730 Q_D(QQuickMultiEffect);
731 d->setMaskThresholdMax(threshold);
732}
733
734/*!
735 \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMax
736
737 This property defines the smoothness of the mask edges near the
738 maskThresholdMax. Using higher spread values softens the transition
739 from the transparent mask pixels towards opaque mask pixels by adding
740 interpolated values between them.
741
742 The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge).
743 By default, the property is set to \c 0.0.
744*/
745qreal QQuickMultiEffect::maskSpreadAtMax() const
746{
747 Q_D(const QQuickMultiEffect);
748 return d->maskSpreadAtMax();
749}
750
751void QQuickMultiEffect::setMaskSpreadAtMax(qreal spread)
752{
753 Q_D(QQuickMultiEffect);
754 d->setMaskSpreadAtMax(spread);
755}
756
757/*!
758 \qmlproperty bool QtQuick.Effects::MultiEffect::maskInverted
759
760 This property switches the mask to the opposite side; instead of
761 masking away the content outside maskThresholdMin and maskThresholdMax,
762 content between them will get masked away.
763
764 By default, the property is set to \c false.
765*/
766bool QQuickMultiEffect::maskInverted() const
767{
768 Q_D(const QQuickMultiEffect);
769 return d->maskInverted();
770}
771
772void QQuickMultiEffect::setMaskInverted(bool inverted)
773{
774 Q_D(QQuickMultiEffect);
775 d->setMaskInverted(inverted);
776}
777
778/*!
779 \qmlproperty rect QtQuick.Effects::MultiEffect::itemRect
780
781 Read-only access to effect item rectangle. This can be used e.g. to see
782 the area item covers.
783
784 \sa paddingRect, autoPaddingEnabled
785*/
786QRectF QQuickMultiEffect::itemRect() const
787{
788 Q_D(const QQuickMultiEffect);
789 return d->itemRect();
790}
791
792/*!
793 \qmlproperty string QtQuick.Effects::MultiEffect::fragmentShader
794 \readonly
795
796 Read-only access to filename of the currently used fragment shader.
797*/
798QString QQuickMultiEffect::fragmentShader() const
799{
800 Q_D(const QQuickMultiEffect);
801 return d->fragmentShader();
802}
803
804/*!
805 \qmlproperty string QtQuick.Effects::MultiEffect::vertexShader
806 \readonly
807
808 Read-only access to filename of the currently used vertex shader.
809*/
810QString QQuickMultiEffect::vertexShader() const
811{
812 Q_D(const QQuickMultiEffect);
813 return d->vertexShader();
814}
815
816/*!
817 \qmlproperty bool QtQuick.Effects::MultiEffect::hasProxySource
818 \readonly
819
820 Returns true when the MultiEffect internally creates \l ShaderEffectSource
821 for the \l source item and false when \l source item is used as-is.
822 For example when source is \l Image element or \l Item with
823 \l {QtQuick::Item::layer.enabled} {layer.enabled} set to \c true,
824 this additional proxy source is not needed.
825*/
826bool QQuickMultiEffect::hasProxySource() const
827{
828 Q_D(const QQuickMultiEffect);
829 return d->hasProxySource();
830}
831
832// *** protected ***
833
834void QQuickMultiEffect::componentComplete()
835{
836 Q_D(QQuickMultiEffect);
837 QQuickItem::componentComplete();
838 d->initialize();
839}
840
841void QQuickMultiEffect::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
842{
843 Q_D(QQuickMultiEffect);
844 QQuickItem::geometryChange(newGeometry, oldGeometry);
845 if (width() > 0 && height() > 0)
846 d->handleGeometryChange(newGeometry, oldGeometry);
847}
848
849void QQuickMultiEffect::itemChange(ItemChange change, const ItemChangeData &value)
850{
851 Q_D(QQuickMultiEffect);
852 d->handleItemChange(change, value);
853 QQuickItem::itemChange(change, value);
854}
855
856// *** private ***
857
858QQuickMultiEffectPrivate::QQuickMultiEffectPrivate()
859{
860}
861
862QQuickMultiEffectPrivate::~QQuickMultiEffectPrivate()
863{
864}
865
866void QQuickMultiEffectPrivate::handleGeometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
867{
868 Q_UNUSED(oldGeometry);
869 Q_UNUSED(newGeometry);
870 initialize();
871 if (!m_shaderEffect)
872 return;
873 updateBlurItemSizes();
874 updateSourcePadding();
875}
876
877void QQuickMultiEffectPrivate::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
878{
879 Q_UNUSED(value);
880 if (change == QQuickItem::ItemSceneChange)
881 initialize();
882}
883
884
885QQuickItem *QQuickMultiEffectPrivate::source() const
886{
887 return m_sourceItem;
888}
889
890void QQuickMultiEffectPrivate::setSource(QQuickItem *item)
891{
892 Q_Q(QQuickMultiEffect);
893 if (item == m_sourceItem)
894 return;
895
896 m_sourceItem = item;
897 if (m_shaderSource)
898 m_shaderSource->setInput(m_sourceItem);
899
900 updateSourcePadding();
901 q->update();
902 Q_EMIT q->sourceChanged();
903}
904
905bool QQuickMultiEffectPrivate::autoPaddingEnabled() const
906{
907 return m_autoPaddingEnabled;
908}
909
910void QQuickMultiEffectPrivate::setAutoPaddingEnabled(bool enabled)
911{
912 Q_Q(QQuickMultiEffect);
913 if (enabled == m_autoPaddingEnabled)
914 return;
915
916 m_autoPaddingEnabled = enabled;
917 updateSourcePadding();
918 q->update();
919 Q_EMIT q->autoPaddingEnabledChanged();
920}
921
922QRectF QQuickMultiEffectPrivate::paddingRect() const
923{
924 return m_paddingRect;
925}
926
927void QQuickMultiEffectPrivate::setPaddingRect(const QRectF &rect)
928{
929 Q_Q(QQuickMultiEffect);
930 if (rect == m_paddingRect)
931 return;
932 m_paddingRect = rect;
933 updateCenterOffset();
934 updateSourcePadding();
935 q->update();
936 emit q->paddingRectChanged();
937}
938
939qreal QQuickMultiEffectPrivate::brightness() const
940{
941 return m_brightness;
942}
943
944void QQuickMultiEffectPrivate::setBrightness(qreal brightness)
945{
946 Q_Q(QQuickMultiEffect);
947 if (brightness == m_brightness)
948 return;
949
950 m_brightness = brightness;
951 if (m_shaderEffect)
952 m_shaderEffect->setProperty(name: "brightness", value: m_brightness);
953
954 q->update();
955 Q_EMIT q->brightnessChanged();
956}
957
958qreal QQuickMultiEffectPrivate::contrast() const
959{
960 return m_contrast;
961}
962
963void QQuickMultiEffectPrivate::setContrast(qreal contrast)
964{
965 Q_Q(QQuickMultiEffect);
966 if (contrast == m_contrast)
967 return;
968
969 m_contrast = contrast;
970 if (m_shaderEffect)
971 m_shaderEffect->setProperty(name: "contrast", value: m_contrast);
972
973 q->update();
974 Q_EMIT q->contrastChanged();
975}
976
977qreal QQuickMultiEffectPrivate::saturation() const
978{
979 return m_saturation;
980}
981
982void QQuickMultiEffectPrivate::setSaturation(qreal saturation)
983{
984 Q_Q(QQuickMultiEffect);
985 if (saturation == m_saturation)
986 return;
987
988 m_saturation = saturation;
989 if (m_shaderEffect)
990 m_shaderEffect->setProperty(name: "saturation", value: m_saturation);
991
992 q->update();
993 Q_EMIT q->saturationChanged();
994}
995
996qreal QQuickMultiEffectPrivate::colorization() const
997{
998 return m_colorization;
999}
1000
1001void QQuickMultiEffectPrivate::setColorization(qreal colorization)
1002{
1003 Q_Q(QQuickMultiEffect);
1004 if (colorization == m_colorization)
1005 return;
1006
1007 m_colorization = colorization;
1008 updateColorizationColor();
1009
1010 q->update();
1011 Q_EMIT q->colorizationChanged();
1012}
1013
1014QColor QQuickMultiEffectPrivate::colorizationColor() const
1015{
1016 return m_colorizationColor;
1017}
1018
1019void QQuickMultiEffectPrivate::setColorizationColor(const QColor &color)
1020{
1021 Q_Q(QQuickMultiEffect);
1022 if (color == m_colorizationColor)
1023 return;
1024
1025 m_colorizationColor = color;
1026 updateColorizationColor();
1027
1028 q->update();
1029 Q_EMIT q->colorizationColorChanged();
1030}
1031
1032bool QQuickMultiEffectPrivate::blurEnabled() const
1033{
1034 return m_blurEnabled;
1035}
1036
1037void QQuickMultiEffectPrivate::setBlurEnabled(bool enabled)
1038{
1039 Q_Q(QQuickMultiEffect);
1040 if (enabled == m_blurEnabled)
1041 return;
1042
1043 m_blurEnabled = enabled;
1044 updateSourcePadding();
1045 updateBlurLevel();
1046 updateEffectShaders();
1047
1048 q->update();
1049 Q_EMIT q->blurEnabledChanged();
1050}
1051
1052qreal QQuickMultiEffectPrivate::blur() const
1053{
1054 return m_blur;
1055}
1056
1057void QQuickMultiEffectPrivate::setBlur(qreal blur)
1058{
1059 Q_Q(QQuickMultiEffect);
1060 if (blur == m_blur)
1061 return;
1062
1063 m_blur = blur;
1064 updateBlurWeights();
1065
1066 q->update();
1067 Q_EMIT q->blurChanged();
1068}
1069
1070int QQuickMultiEffectPrivate::blurMax() const
1071{
1072 return m_blurMax;
1073}
1074
1075void QQuickMultiEffectPrivate::setBlurMax(int blurMax)
1076{
1077 Q_Q(QQuickMultiEffect);
1078 if (blurMax == m_blurMax)
1079 return;
1080
1081 m_blurMax = blurMax;
1082 updateSourcePadding();
1083 updateBlurLevel();
1084 updateBlurItemSizes();
1085 updateBlurWeights();
1086 updateShadowBlurWeights();
1087 updateEffectShaders();
1088
1089 q->update();
1090 Q_EMIT q->blurMaxChanged();
1091}
1092
1093qreal QQuickMultiEffectPrivate::blurMultiplier() const
1094{
1095 return m_blurMultiplier;
1096}
1097
1098void QQuickMultiEffectPrivate::setBlurMultiplier(qreal blurMultiplier)
1099{
1100 Q_Q(QQuickMultiEffect);
1101 if (blurMultiplier == m_blurMultiplier)
1102 return;
1103
1104 m_blurMultiplier = blurMultiplier;
1105 updateSourcePadding();
1106 updateBlurItemSizes(forceUpdate: true);
1107 updateBlurWeights();
1108 updateShadowBlurWeights();
1109
1110 q->update();
1111 Q_EMIT q->blurMultiplierChanged();
1112}
1113
1114bool QQuickMultiEffectPrivate::shadowEnabled() const
1115{
1116 return m_shadowEnabled;
1117}
1118
1119void QQuickMultiEffectPrivate::setShadowEnabled(bool enabled)
1120{
1121 Q_Q(QQuickMultiEffect);
1122 if (enabled == m_shadowEnabled)
1123 return;
1124
1125 m_shadowEnabled = enabled;
1126 updateSourcePadding();
1127 updateBlurLevel();
1128 updateEffectShaders();
1129
1130 q->update();
1131 Q_EMIT q->shadowEnabledChanged();
1132}
1133
1134qreal QQuickMultiEffectPrivate::shadowOpacity() const
1135{
1136 return m_shadowOpacity;
1137}
1138
1139void QQuickMultiEffectPrivate::setShadowOpacity(qreal shadowOpacity)
1140{
1141 Q_Q(QQuickMultiEffect);
1142 if (shadowOpacity == m_shadowOpacity)
1143 return;
1144
1145 m_shadowOpacity = shadowOpacity;
1146 updateShadowColor();
1147
1148 q->update();
1149 Q_EMIT q->shadowOpacityChanged();
1150}
1151
1152qreal QQuickMultiEffectPrivate::shadowBlur() const
1153{
1154 return m_shadowBlur;
1155}
1156
1157void QQuickMultiEffectPrivate::setShadowBlur(qreal shadowBlur)
1158{
1159 Q_Q(QQuickMultiEffect);
1160 if (shadowBlur == m_shadowBlur)
1161 return;
1162
1163 m_shadowBlur = shadowBlur;
1164 updateShadowBlurWeights();
1165
1166 q->update();
1167 Q_EMIT q->shadowBlurChanged();
1168}
1169
1170qreal QQuickMultiEffectPrivate::shadowHorizontalOffset() const
1171{
1172 return m_shadowHorizontalOffset;
1173}
1174
1175void QQuickMultiEffectPrivate::setShadowHorizontalOffset(qreal offset)
1176{
1177 Q_Q(QQuickMultiEffect);
1178 if (offset == m_shadowHorizontalOffset)
1179 return;
1180
1181 m_shadowHorizontalOffset = offset;
1182 updateShadowOffset();
1183
1184 q->update();
1185 Q_EMIT q->shadowHorizontalOffsetChanged();
1186}
1187
1188qreal QQuickMultiEffectPrivate::shadowVerticalOffset() const
1189{
1190 return m_shadowVerticalOffset;
1191}
1192
1193void QQuickMultiEffectPrivate::setShadowVerticalOffset(qreal offset)
1194{
1195 Q_Q(QQuickMultiEffect);
1196 if (offset == m_shadowVerticalOffset)
1197 return;
1198
1199 m_shadowVerticalOffset = offset;
1200 updateShadowOffset();
1201
1202 q->update();
1203 Q_EMIT q->shadowVerticalOffsetChanged();
1204}
1205
1206QColor QQuickMultiEffectPrivate::shadowColor() const
1207{
1208 return m_shadowColor;
1209}
1210
1211void QQuickMultiEffectPrivate::setShadowColor(const QColor &color)
1212{
1213 Q_Q(QQuickMultiEffect);
1214 if (color == m_shadowColor)
1215 return;
1216
1217 m_shadowColor = color;
1218 updateShadowColor();
1219
1220 q->update();
1221 Q_EMIT q->shadowColorChanged();
1222}
1223
1224qreal QQuickMultiEffectPrivate::shadowScale() const
1225{
1226 return m_shadowScale;
1227}
1228
1229void QQuickMultiEffectPrivate::setShadowScale(qreal shadowScale)
1230{
1231 Q_Q(QQuickMultiEffect);
1232 if (shadowScale == m_shadowScale)
1233 return;
1234
1235 m_shadowScale = shadowScale;
1236 updateCenterOffset();
1237 if (m_shaderEffect)
1238 m_shaderEffect->setProperty(name: "shadowScale", value: 1.0 / m_shadowScale);
1239
1240 q->update();
1241 Q_EMIT q->shadowScaleChanged();
1242}
1243
1244bool QQuickMultiEffectPrivate::maskEnabled() const
1245{
1246 return m_maskEnabled;
1247}
1248
1249void QQuickMultiEffectPrivate::setMaskEnabled(bool enabled)
1250{
1251 Q_Q(QQuickMultiEffect);
1252 if (enabled == m_maskEnabled)
1253 return;
1254
1255 m_maskEnabled = enabled;
1256 updateEffectShaders();
1257
1258 q->update();
1259 Q_EMIT q->maskEnabledChanged();
1260}
1261
1262QQuickItem *QQuickMultiEffectPrivate::maskSource() const
1263{
1264 return m_maskSourceItem;
1265}
1266
1267void QQuickMultiEffectPrivate::setMaskSource(QQuickItem *item)
1268{
1269 Q_Q(QQuickMultiEffect);
1270 if (item == m_maskSourceItem)
1271 return;
1272
1273 m_maskSourceItem = item;
1274 if (m_shaderEffect) {
1275 auto maskSourceVariant = QVariant::fromValue<QQuickItem*>(value: m_maskSourceItem);
1276 m_shaderEffect->setProperty(name: "maskSrc", value: maskSourceVariant);
1277 }
1278
1279 q->update();
1280 Q_EMIT q->maskSourceChanged();
1281}
1282
1283qreal QQuickMultiEffectPrivate::maskThresholdMin() const
1284{
1285 return m_maskThresholdMin;
1286}
1287
1288void QQuickMultiEffectPrivate::setMaskThresholdMin(qreal threshold)
1289{
1290 Q_Q(QQuickMultiEffect);
1291 if (threshold == m_maskThresholdMin)
1292 return;
1293
1294 m_maskThresholdMin = threshold;
1295 updateMaskThresholdSpread();
1296
1297 q->update();
1298 Q_EMIT q->maskThresholdMinChanged();
1299}
1300
1301qreal QQuickMultiEffectPrivate::maskSpreadAtMin() const
1302{
1303 return m_maskSpreadAtMin;
1304}
1305
1306void QQuickMultiEffectPrivate::setMaskSpreadAtMin(qreal spread)
1307{
1308 Q_Q(QQuickMultiEffect);
1309 if (spread == m_maskSpreadAtMin)
1310 return;
1311
1312 m_maskSpreadAtMin = spread;
1313 updateMaskThresholdSpread();
1314
1315 q->update();
1316 Q_EMIT q->maskSpreadAtMinChanged();
1317}
1318
1319qreal QQuickMultiEffectPrivate::maskThresholdMax() const
1320{
1321 return m_maskThresholdMax;
1322}
1323
1324void QQuickMultiEffectPrivate::setMaskThresholdMax(qreal threshold)
1325{
1326 Q_Q(QQuickMultiEffect);
1327 if (threshold == m_maskThresholdMax)
1328 return;
1329
1330 m_maskThresholdMax = threshold;
1331 updateMaskThresholdSpread();
1332
1333 q->update();
1334 Q_EMIT q->maskThresholdMaxChanged();
1335}
1336
1337qreal QQuickMultiEffectPrivate::maskSpreadAtMax() const
1338{
1339 return m_maskSpreadAtMax;
1340}
1341
1342void QQuickMultiEffectPrivate::setMaskSpreadAtMax(qreal spread)
1343{
1344 Q_Q(QQuickMultiEffect);
1345 if (spread == m_maskSpreadAtMax)
1346 return;
1347
1348 m_maskSpreadAtMax = spread;
1349 updateMaskThresholdSpread();
1350
1351 q->update();
1352 Q_EMIT q->maskSpreadAtMaxChanged();
1353}
1354
1355bool QQuickMultiEffectPrivate::maskInverted() const
1356{
1357 return m_maskInverted;
1358}
1359
1360void QQuickMultiEffectPrivate::setMaskInverted(bool inverted)
1361{
1362 Q_Q(QQuickMultiEffect);
1363 if (inverted == m_maskInverted)
1364 return;
1365
1366 m_maskInverted = inverted;
1367 if (m_shaderEffect)
1368 m_shaderEffect->setProperty(name: "maskInverted", value: float(m_maskInverted));
1369
1370 q->update();
1371 Q_EMIT q->maskInvertedChanged();
1372}
1373
1374QRectF QQuickMultiEffectPrivate::itemRect() const
1375{
1376 if (!m_shaderEffect || !m_shaderSource)
1377 return QRectF();
1378
1379 QRectF sourceRect = m_shaderSource->sourceRect();
1380 if (sourceRect.width() > 0 && sourceRect.height() > 0)
1381 return sourceRect;
1382 else
1383 return m_shaderEffect->boundingRect();
1384}
1385
1386QString QQuickMultiEffectPrivate::fragmentShader() const
1387{
1388 return m_fragShader;
1389}
1390
1391QString QQuickMultiEffectPrivate::vertexShader() const
1392{
1393 return m_vertShader;
1394}
1395
1396bool QQuickMultiEffectPrivate::hasProxySource() const
1397{
1398 return m_shaderSource && m_shaderSource->isActive();
1399}
1400
1401// This initializes the component. It will be ran once, when
1402// the component is ready and it has a valid size.
1403void QQuickMultiEffectPrivate::initialize()
1404{
1405 Q_Q(QQuickMultiEffect);
1406 if (m_initialized)
1407 return;
1408 if (!q->isComponentComplete())
1409 return;
1410 if (!q->window())
1411 return;
1412 if (q->width() <= 0 || q->height() <= 0)
1413 return;
1414
1415 m_shaderEffect = new QQuickShaderEffect(q);
1416 m_shaderSource = new QGfxSourceProxy(q);
1417 QObject::connect(sender: m_shaderSource, signal: &QGfxSourceProxy::outputChanged, context: q, slot: [this] { proxyOutputChanged(); });
1418 QObject::connect(sender: m_shaderSource, signal: &QGfxSourceProxy::activeChanged, context: q, slot: &QQuickMultiEffect::hasProxySourceChanged);
1419
1420 m_shaderEffect->setParentItem(q);
1421 m_shaderEffect->setSize(q->size());
1422
1423 m_shaderSource->setParentItem(q);
1424 m_shaderSource->setSize(q->size());
1425 m_shaderSource->setInput(m_sourceItem);
1426
1427 updateCenterOffset();
1428 updateMaskThresholdSpread();
1429 updateBlurWeights();
1430 updateShadowBlurWeights();
1431 updateColorizationColor();
1432 updateShadowColor();
1433 updateShadowOffset();
1434
1435 // Create properties
1436 auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: m_shaderSource->output());
1437 m_shaderEffect->setProperty(name: "src", value: sourceVariant);
1438 m_shaderEffect->setProperty(name: "brightness", value: m_brightness);
1439 m_shaderEffect->setProperty(name: "contrast", value: m_contrast);
1440 m_shaderEffect->setProperty(name: "saturation", value: m_saturation);
1441 m_shaderEffect->setProperty(name: "shadowScale", value: 1.0 / m_shadowScale);
1442 auto maskSourceVariant = QVariant::fromValue<QQuickItem*>(value: m_maskSourceItem);
1443 m_shaderEffect->setProperty(name: "maskSrc", value: maskSourceVariant);
1444 m_shaderEffect->setProperty(name: "maskInverted", value: float(m_maskInverted));
1445
1446 updateBlurLevel();
1447 updateBlurItemSizes();
1448 updateSourcePadding();
1449
1450 updateEffectShaders();
1451
1452 m_initialized = true;
1453}
1454
1455void QQuickMultiEffectPrivate::updateMaskThresholdSpread()
1456{
1457 if (!m_shaderEffect)
1458 return;
1459
1460 // Calculate threshold and spread values for mask
1461 // smoothstep, keeping always edge0 < edge1.
1462 const qreal c0 = 0.0001;
1463 const qreal c1 = 1.0 - c0;
1464 const qreal mt1 = m_maskThresholdMin + c0;
1465 const qreal ms1 = m_maskSpreadAtMin + 1.0;
1466 const qreal mt2 = c1 - m_maskThresholdMax;
1467 const qreal ms2 = m_maskSpreadAtMax + 1.0;
1468 const QVector4D maskThresholdSpread = QVector4D(
1469 mt1 * ms1 - (ms1 - c1),
1470 mt1 * ms1,
1471 mt2 * ms2 - (ms2 - c1),
1472 mt2 * ms2);
1473 m_shaderEffect->setProperty(name: "mask", value: maskThresholdSpread);
1474}
1475
1476void QQuickMultiEffectPrivate::updateCenterOffset()
1477{
1478 if (!m_shaderEffect)
1479 return;
1480
1481 const qreal scale = 1.0 / m_shadowScale;
1482 QVector2D centerOffset((1.0 - scale) * (0.5 + 0.5 * (m_paddingRect.x() - m_paddingRect.width()) / m_shaderEffect->width()),
1483 (1.0 - scale) * (0.5 + 0.5 * (m_paddingRect.y() - m_paddingRect.height()) / m_shaderEffect->height()));
1484 m_shaderEffect->setProperty(name: "centerOffset", value: centerOffset);
1485}
1486
1487void QQuickMultiEffectPrivate::updateShadowOffset()
1488{
1489 if (!m_shaderEffect)
1490 return;
1491
1492 QVector2D shadowOffset = QVector2D(m_shadowHorizontalOffset / m_shaderEffect->width(), m_shadowVerticalOffset / m_shaderEffect->height());
1493 m_shaderEffect->setProperty(name: "shadowOffset", value: shadowOffset);
1494}
1495
1496void QQuickMultiEffectPrivate::updateColorizationColor()
1497{
1498 if (!m_shaderEffect)
1499 return;
1500
1501 float alpha = std::clamp(val: float(m_colorizationColor.alphaF() * m_colorization), lo: 0.0f, hi: 1.0f);
1502 QVector4D colorizationColor(m_colorizationColor.redF(),
1503 m_colorizationColor.greenF(),
1504 m_colorizationColor.blueF(),
1505 alpha);
1506 m_shaderEffect->setProperty(name: "colorizationColor", value: colorizationColor);
1507}
1508
1509void QQuickMultiEffectPrivate::updateShadowColor()
1510{
1511 if (!m_shaderEffect)
1512 return;
1513
1514 float alpha = std::clamp(val: float(m_shadowColor.alphaF() * m_shadowOpacity), lo: 0.0f, hi: 1.0f);
1515 QVector4D shadowColor(m_shadowColor.redF(),
1516 m_shadowColor.greenF(),
1517 m_shadowColor.blueF(),
1518 alpha);
1519
1520 m_shaderEffect->setProperty(name: "shadowColor", value: shadowColor);
1521}
1522
1523float QQuickMultiEffectPrivate::calculateLod(float blurAmount)
1524{
1525 return qSqrt(v: blurAmount * float(m_blurMax) / 64.0f) * 1.2f - 0.2f;
1526}
1527
1528float QQuickMultiEffectPrivate::blurWeight(float v)
1529{
1530 return std::max(a: 0.0f, b: std::min(a: 1.0f, b: 1.0f - v * 2.0f));
1531}
1532
1533void QQuickMultiEffectPrivate::getBlurWeights(float blurLod, QVector4D &blurWeight1, QVector2D &blurWeight2)
1534{
1535 float bw1 = blurWeight(v: std::fabs(x: blurLod - 0.1f));
1536 float bw2 = blurWeight(v: std::fabs(x: blurLod - 0.3f));
1537 float bw3 = blurWeight(v: std::fabs(x: blurLod - 0.5f));
1538 float bw4 = blurWeight(v: std::fabs(x: blurLod - 0.7f));
1539 float bw5 = blurWeight(v: std::fabs(x: blurLod - 0.9f));
1540 float bw6 = blurWeight(v: std::fabs(x: blurLod - 1.1f));
1541 float bsum = bw1 + bw2 + bw3 + bw4 + bw5 + bw6;
1542 blurWeight1 = QVector4D(bw1 / bsum, bw2 / bsum, bw3 / bsum, bw4 / bsum);
1543 blurWeight2 = QVector2D(bw5 / bsum, bw6 / bsum);
1544}
1545
1546void QQuickMultiEffectPrivate::updateBlurWeights()
1547{
1548 if (!m_shaderEffect)
1549 return;
1550 float blurLod = calculateLod(blurAmount: m_blur);
1551 getBlurWeights(blurLod, blurWeight1&: m_blurWeight1, blurWeight2&: m_blurWeight2);
1552 m_shaderEffect->setProperty(name: "blurWeight1", value: m_blurWeight1);
1553 m_shaderEffect->setProperty(name: "blurWeight2", value: m_blurWeight2);
1554}
1555
1556void QQuickMultiEffectPrivate::updateShadowBlurWeights()
1557{
1558 if (!m_shaderEffect)
1559 return;
1560 float blurLod = calculateLod(blurAmount: m_shadowBlur);
1561 getBlurWeights(blurLod, blurWeight1&: m_shadowBlurWeight1, blurWeight2&: m_shadowBlurWeight2);
1562 m_shaderEffect->setProperty(name: "shadowBlurWeight1", value: m_shadowBlurWeight1);
1563 m_shaderEffect->setProperty(name: "shadowBlurWeight2", value: m_shadowBlurWeight2);
1564}
1565
1566void QQuickMultiEffectPrivate::updateBlurItemSizes(bool forceUpdate)
1567{
1568 if (m_blurEffects.isEmpty() || !m_shaderSource || !m_sourceItem)
1569 return;
1570
1571 // First blur item size to be half of th source item
1572 // extended size, rounded to next divisible by 16.
1573 QSizeF sourceSize = itemRect().size();
1574 QSizeF firstItemSize(std::ceil(x: sourceSize.width() / 16) * 8,
1575 std::ceil(x: sourceSize.height() / 16) * 8);
1576
1577 if (!forceUpdate && m_firstBlurItemSize == firstItemSize)
1578 return;
1579
1580 qCDebug(lcQuickEffect) << "Source size:" << sourceSize;
1581 m_firstBlurItemSize = firstItemSize;
1582
1583 for (int i = 0; i < m_blurEffects.size(); i++) {
1584 auto *blurEffect = m_blurEffects[i];
1585 QSizeF itemSize = (i == 0) ? firstItemSize : m_blurEffects[i - 1]->size() * 0.5;
1586 qCDebug(lcQuickEffect) << "Blur item" << i << ":" << itemSize;
1587 blurEffect->setSize(itemSize);
1588
1589 const QVector2D offset((1.0 + m_blurMultiplier) / itemSize.width(),
1590 (1.0 + m_blurMultiplier) / itemSize.height());
1591 blurEffect->setProperty(name: "offset", value: offset);
1592 }
1593}
1594
1595void QQuickMultiEffectPrivate::updateEffectShaders()
1596{
1597 Q_Q(QQuickMultiEffect);
1598 if (!q->isComponentComplete())
1599 return;
1600
1601 QString vShader = QStringLiteral("multieffect_c");
1602 if (m_shadowEnabled)
1603 vShader += QStringLiteral("s");
1604
1605 QString fShader = QStringLiteral("multieffect_c");
1606 if (m_maskEnabled)
1607 fShader += QStringLiteral("m");
1608 if (m_blurEnabled && m_blurMax > 0)
1609 fShader += QStringLiteral("b");
1610 if (m_shadowEnabled)
1611 fShader += QStringLiteral("s");
1612
1613 fShader += QString::number(m_blurLevel);
1614
1615 bool shaderChanged = false;
1616 if (fShader != m_fragShader) {
1617 shaderChanged = true;
1618 m_fragShader = fShader;
1619 QUrl fs = QUrl(QStringLiteral("qrc:/data/shaders/%1.frag.qsb").arg(a: m_fragShader));
1620 m_shaderEffect->setFragmentShader(fs);
1621 Q_EMIT q->fragmentShaderChanged();
1622 }
1623 if (vShader != m_vertShader) {
1624 shaderChanged = true;
1625 m_vertShader = vShader;
1626 QUrl vs = QUrl(QStringLiteral("qrc:/data/shaders/%1.vert.qsb").arg(a: m_vertShader));
1627 m_shaderEffect->setVertexShader(vs);
1628 Q_EMIT q->vertexShaderChanged();
1629 }
1630 if (shaderChanged) {
1631 qCDebug(lcQuickEffect) << this << "Shaders: " << m_fragShader << m_vertShader;
1632 Q_EMIT q->shaderChanged();
1633 }
1634}
1635
1636void QQuickMultiEffectPrivate::updateBlurLevel(bool forceUpdate)
1637{
1638 int blurLevel = 0;
1639 if ((m_blurEnabled || m_shadowEnabled) && m_blurMax > 0) {
1640 if (m_blurMax > 32)
1641 blurLevel = 3;
1642 else if (m_blurMax > 16)
1643 blurLevel = 2;
1644 else
1645 blurLevel = 1;
1646 }
1647
1648 if (blurLevel != m_blurLevel || (blurLevel > 0 && m_blurEffects.isEmpty()) || forceUpdate) {
1649 // Blur level has changed or blur items need to be
1650 // initially created.
1651 updateBlurItemsAmount(blurLevel);
1652 // When the level grows, new items must be resized
1653 if (blurLevel > m_blurLevel)
1654 updateBlurItemSizes(forceUpdate: true);
1655 }
1656 m_blurLevel = blurLevel;
1657}
1658
1659void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel)
1660{
1661 Q_Q(QQuickMultiEffect);
1662 if (!m_shaderEffect)
1663 return;
1664
1665 // Lowest blur level uses 3 items, highest 5 items.
1666 int itemsAmount = blurLevel == 0 ? 0 : blurLevel + 2;
1667
1668 if (m_blurEffects.size() < itemsAmount) {
1669 // Add more blur items.
1670 // Note that by design blur items are only added and never reduced
1671 // during the lifetime of the effect component.
1672 const auto engine = qmlEngine(q);
1673 QUrl blurVs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.vert.qsb"));
1674 QUrl blurFs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.frag.qsb"));
1675 QQmlComponent blurComponent(engine, QUrl(QStringLiteral("qrc:/data/BlurItem.qml")));
1676 for (int i = m_blurEffects.size(); i < itemsAmount; i++) {
1677 auto blurEffect = qobject_cast<QQuickShaderEffect*>(object: blurComponent.create());
1678 blurEffect->setParent(q);
1679 blurEffect->setParentItem(q);
1680 auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: blurEffect);
1681 QString sourceProperty = QStringLiteral("blurSrc%1").arg(a: i + 1);
1682 m_shaderEffect->setProperty(name: sourceProperty.toUtf8(), value: sourceVariant);
1683 // Initial value to avoid "'source' does not have a matching property" warning.
1684 // Will be updated with the correct one few lines forward.
1685 blurEffect->setProperty(name: "source", value: sourceVariant);
1686 QQuickItemPrivate *priv = QQuickItemPrivate::get(item: blurEffect);
1687 priv->layer()->setEnabled(true);
1688 priv->layer()->setSmooth(true);
1689 blurEffect->setVertexShader(blurVs);
1690 blurEffect->setFragmentShader(blurFs);
1691 m_blurEffects << blurEffect;
1692 }
1693 }
1694
1695 // Set the blur items source components
1696 static const auto dummyShaderSource = new QQuickShaderEffectSource(q);
1697 for (int i = 0; i < m_blurEffects.size(); i++) {
1698 auto *blurEffect = m_blurEffects[i];
1699 auto sourceItem = (i >= itemsAmount) ?
1700 static_cast<QQuickItem *>(dummyShaderSource) : (i == 0) ?
1701 static_cast<QQuickItem *>(m_shaderSource->output()) :
1702 static_cast<QQuickItem *>(m_blurEffects[i - 1]);
1703 auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: sourceItem);
1704 blurEffect->setProperty(name: "source", value: sourceVariant);
1705 }
1706}
1707
1708void QQuickMultiEffectPrivate::updateSourcePadding()
1709{
1710 Q_Q(QQuickMultiEffect);
1711 if (!m_shaderEffect || !m_shaderSource)
1712 return;
1713
1714 const bool blurItemsNeeded = (m_blurEnabled || m_shadowEnabled) && (m_blurMax > 0);
1715 const int itemPadding = m_autoPaddingEnabled && blurItemsNeeded ? m_blurMax * (1.0 + m_blurMultiplier) : 0;
1716
1717 // Set the shader effect size
1718 if (m_paddingRect != QRectF() || itemPadding > 0) {
1719 QRectF effectRect(-m_paddingRect.x() - itemPadding,
1720 -m_paddingRect.y() - itemPadding,
1721 q->width() + m_paddingRect.x() + m_paddingRect.width() + (itemPadding * 2),
1722 q->height() + m_paddingRect.y() + m_paddingRect.height() + (itemPadding * 2));
1723 m_shaderEffect->setX(effectRect.x());
1724 m_shaderEffect->setY(effectRect.y());
1725 m_shaderEffect->setWidth(effectRect.width());
1726 m_shaderEffect->setHeight(effectRect.height());
1727
1728 // Set the source size
1729 m_shaderSource->setSize(m_shaderEffect->size());
1730
1731 // When m_sourceItem is set and has size, use that as the base size.
1732 // When effect is used as a component in Item "layer.effect", source
1733 // doesn't have a size and then we follow the effect item size.
1734 const qreal baseWidth = m_sourceItem && m_sourceItem->width() > 0 ? m_sourceItem->width() : q->width();
1735 const qreal baseHeight = m_sourceItem && m_sourceItem->height() > 0 ? m_sourceItem->height() : q->height();
1736
1737 // Set the source rect
1738 const qreal widthMultiplier = q->width() > 0 ? baseWidth / q->width() : 1.0;
1739 const qreal heightMultiplier = q->height() > 0 ? baseHeight / q->height() : 1.0;
1740 const qreal xPadding = itemPadding * widthMultiplier;
1741 const qreal yPadding = itemPadding * heightMultiplier;
1742 QRectF rect = QRectF(m_paddingRect.x() * widthMultiplier,
1743 m_paddingRect.y() * heightMultiplier,
1744 m_paddingRect.width() * widthMultiplier,
1745 m_paddingRect.height() * heightMultiplier);
1746 QRectF sourceRect = QRectF(-rect.x() - xPadding,
1747 -rect.y() - yPadding,
1748 baseWidth + rect.x() + rect.width() + xPadding * 2,
1749 baseHeight + rect.y() + rect.height() + yPadding * 2);
1750 m_shaderSource->setSourceRect(sourceRect);
1751 } else {
1752 m_shaderEffect->setX(0);
1753 m_shaderEffect->setY(0);
1754 m_shaderEffect->setSize(q->size());
1755 m_shaderSource->setSize(q->size());
1756 m_shaderSource->setSourceRect(QRectF());
1757 }
1758
1759 updateShadowOffset();
1760 updateProxyActiveCheck();
1761 updateBlurItemSizes();
1762 Q_EMIT q->paddingRectChanged();
1763 Q_EMIT q->itemRectChanged();
1764 Q_EMIT q->itemSizeChanged();
1765}
1766
1767void QQuickMultiEffectPrivate::proxyOutputChanged()
1768{
1769 if (!m_shaderSource)
1770 return;
1771
1772 auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: m_shaderSource->output());
1773 m_shaderEffect->setProperty(name: "src", value: sourceVariant);
1774
1775 // Force updating the blur items since the source output has changed
1776 updateBlurLevel(forceUpdate: true);
1777 updateBlurItemSizes();
1778 updateSourcePadding();
1779}
1780
1781void QQuickMultiEffectPrivate::updateProxyActiveCheck()
1782{
1783 if (!m_shaderSource)
1784 return;
1785
1786 m_shaderSource->polish();
1787}
1788
1789QT_END_NAMESPACE
1790
1791#include "moc_qquickmultieffect_p.cpp"
1792

source code of qtdeclarative/src/effects/qquickmultieffect.cpp