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

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