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