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