aboutsummaryrefslogtreecommitdiffstats
path: root/src/effects/qquickmultieffect.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/effects/qquickmultieffect.cpp')
-rw-r--r--src/effects/qquickmultieffect.cpp1796
1 files changed, 1796 insertions, 0 deletions
diff --git a/src/effects/qquickmultieffect.cpp b/src/effects/qquickmultieffect.cpp
new file mode 100644
index 0000000000..7561649fbf
--- /dev/null
+++ b/src/effects/qquickmultieffect.cpp
@@ -0,0 +1,1796 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qtypes.h"
+#include <private/qquickmultieffect_p_p.h>
+#include <private/qquickshadereffect_p.h>
+#include <private/qquickshadereffectsource_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects")
+
+/*!
+ \keyword Qt Quick Effects
+ \qmlmodule QtQuick.Effects
+ \title Qt Quick Effects QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for applying one or more simple graphical effects to Qt Quick items.
+
+ To use the types in this module, import the module with the following line:
+
+ \qml
+ import QtQuick.Effects
+ \endqml
+*/
+
+/*!
+ \qmltype MultiEffect
+ \instantiates QQuickMultiEffect
+ \inqmlmodule QtQuick.Effects
+ \inherits Item
+ \ingroup qtquick-effects
+ \brief Applies post-processing effect to an item.
+
+ The MultiEffect type, the successor to the deprecated Qt Graphical Effects
+ from Qt 5, applies a post-processing effect to the \l source item. Compared
+ to the Qt Graphical Effects module, MultiEffect combines multiple
+ effects (blur, shadow, colorization etc.) into a single item and shader
+ which makes it better for multiple effects. There are several shader
+ variations and the most optimal one gets selected based on the features
+ used.
+
+ MultiEffect is designed specifically for most common effects and can be easily animated.
+ If the MultiEffect doesn't contain the effect you need, consider implementing a custom
+ effect using \l {Qt Quick Effect Maker}. For more information about shader effects,
+ see the \l ShaderEffect reference documentation.
+
+ \section1 Example Usage
+
+ The following simple example shows how to apply a saturation effect on an item:
+
+ \table 70%
+ \row
+ \li \image multieffect-example1.png
+ \li \qml
+ import QtQuick
+ import QtQuick.Effects
+
+ ...
+ Image {
+ id: sourceItem
+ source: "qt_logo_green_rgb.png"
+ visible: false
+ }
+ MultiEffect {
+ source: sourceItem
+ anchors.fill: sourceItem
+ saturation: -1.0
+ }
+ \endqml
+ \endtable
+
+ Here is a bit more complex example, applying multiple effects at the same time:
+
+ \table 70%
+ \row
+ \li \image multieffect-example2.png
+ \li \qml
+ import QtQuick
+ import QtQuick.Effects
+
+ ...
+ MultiEffect {
+ source: sourceItem
+ anchors.fill: sourceItem
+ brightness: 0.4
+ saturation: 0.2
+ blurEnabled: true
+ blurMax: 64
+ blur: 1.0
+ }
+ \endqml
+ \endtable
+
+ Below is an example of how to use the mask, colorization and brightness effects together to
+ fade away an element. This kind of hiding/showing of elements can be, for example, bind
+ to a slider value or animations like NumberAnimation. Note how the \c visible property is
+ false when the item is totally faded away, to avoid unnecessary rendering of the effect.
+
+ \table 70%
+ \row
+ \li \image multieffect-example3.png
+ \li \qml
+ import QtQuick
+ import QtQuick.Effects
+ import QtQuick.Controls.Material
+
+ ...
+ MultiEffect {
+ property real effectAmount: effectSlider.value
+ source: sourceItem
+ anchors.fill: sourceItem
+ brightness: effectAmount
+ colorizationColor: "#ff20d0"
+ colorization: effectAmount
+ maskEnabled: true
+ maskSource: Image {
+ source: "mask.png"
+ }
+ maskSpreadAtMin: 0.2
+ maskThresholdMin: effectAmount
+ visible: effectAmount < 1.0
+ }
+ Slider {
+ id: effectSlider
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ \endqml
+ \endtable
+
+ \section1 Performance
+ There are a few things to consider for optimal performance:
+ \list
+ \li To get the most optimal shader, enable only the effects which you actually use
+ (see \l blurEnabled, \l shadowEnabled, \l maskEnabled). Simple color effects (\l brightness,
+ \l contrast, \l saturation, \l colorization) are always enabled, so using them doesn't
+ add extra overhead.
+ \li See the \b {Performance notes} of the properties which may change the shader or the effect
+ item size and don't modify these during animations.
+ \li When the MultiEffect isn't used, remember to set its \c visible property to be false to
+ avoid rendering the effects in the background.
+ \li Blur and shadow are the heaviest effects. With these, prefer increasing \l blurMultiplier
+ over \l blurMax and avoid using \l source items which animate, so blurring doesn't need
+ to be regenerated in every frame.
+ \li Apply effects to optimally sized QML elements as more pixels means more work for
+ the GPU. When applying the blur effect to the whole background, remember to set
+ \l autoPaddingEnabled false or the effect grows "outside" the window / screen.
+ \endlist
+*/
+
+/*!
+ \qmlsignal QtQuick.Effects::MultiEffect::shaderChanged()
+
+ This signal is emitted when the used shader changes.
+ \sa fragmentShader, vertexShader
+*/
+
+QQuickMultiEffect::QQuickMultiEffect(QQuickItem *parent)
+ : QQuickItem(*new QQuickMultiEffectPrivate, parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QQuickMultiEffect::~QQuickMultiEffect()
+{
+}
+
+/*!
+ \qmlproperty Item QtQuick.Effects::MultiEffect::source
+
+ This property holds the item to be used as a source for the effect.
+ If needed, MultiEffect will internally generate a \l ShaderEffectSource
+ as the texture source.
+
+ \note It is not supported to let the effect include itself, for instance
+ by setting source to the effect's parent.
+
+ \note If the source item has \l {QtQuick::Item::layer.enabled} {layer.enabled} set to true,
+ it will be used directly. This is good for the performance and often desired, when the source
+ is hidden. But if the source remains visible and the effect adds padding (autoPaddingEnabled,
+ paddingRect), that padding can affect the appearance of the source item.
+
+ \sa hasProxySource
+*/
+QQuickItem *QQuickMultiEffect::source() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->source();
+}
+
+void QQuickMultiEffect::setSource(QQuickItem *item)
+{
+ Q_D(QQuickMultiEffect);
+ d->setSource(item);
+}
+
+/*!
+ \qmlproperty bool QtQuick.Effects::MultiEffect::autoPaddingEnabled
+
+ When blur or shadow effects are enabled and this is set to true (default),
+ the item size is padded automatically based on blurMax and blurMultiplier.
+ Note that \l paddingRect is always added to the size.
+
+ \image multieffect-example4.png
+
+ \sa paddingRect
+
+ \include notes.qdocinc performance item size
+
+ \include notes.qdocinc performance item resize
+*/
+bool QQuickMultiEffect::autoPaddingEnabled() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->autoPaddingEnabled();
+}
+
+void QQuickMultiEffect::setAutoPaddingEnabled(bool enabled)
+{
+ Q_D(QQuickMultiEffect);
+ d->setAutoPaddingEnabled(enabled);
+}
+
+/*!
+ \qmlproperty rect QtQuick.Effects::MultiEffect::paddingRect
+
+ Set this to increase item size manually so that blur and/or shadows will fit.
+ If autoPaddingEnabled is true and paddingRect is not set, the item is padded
+ to fit maximally blurred item based on blurMax and blurMultiplier. When
+ enabling the shadow, you typically need to take \l shadowHorizontalOffset and
+ \l shadowVerticalOffset into account and adjust this paddingRect accordingly.
+
+ Below is an example of adjusting paddingRect with autoPaddingEnabled set to
+ false so that the shadow fits inside the MultiEffect item.
+
+ \image multieffect-example5.png
+
+ \sa autoPaddingEnabled
+
+ \include notes.qdocinc performance item size
+
+ \include notes.qdocinc performance item resize
+*/
+QRectF QQuickMultiEffect::paddingRect() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->paddingRect();
+}
+
+void QQuickMultiEffect::setPaddingRect(const QRectF &rect)
+{
+ Q_D(QQuickMultiEffect);
+ d->setPaddingRect(rect);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::brightness
+
+ This property defines how much the source brightness is increased or
+ decreased.
+
+ The value ranges from -1.0 to 1.0. By default, the property is set to \c
+ 0.0 (no change).
+*/
+qreal QQuickMultiEffect::brightness() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->brightness();
+}
+
+void QQuickMultiEffect::setBrightness(qreal brightness)
+{
+ Q_D(QQuickMultiEffect);
+ d->setBrightness(brightness);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::contrast
+
+ This property defines how much the source contrast is increased or
+ decreased.
+
+ The value ranges from -1.0 to 1.0. By default, the property is set to \c
+ 0.0 (no change).
+*/
+qreal QQuickMultiEffect::contrast() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->contrast();
+}
+
+void QQuickMultiEffect::setContrast(qreal contrast)
+{
+ Q_D(QQuickMultiEffect);
+ d->setContrast(contrast);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::saturation
+
+ This property defines how much the source saturation is increased or
+ decreased.
+
+ The value ranges from -1.0 (totally desaturated) to inf. By default,
+ the property is set to \c 0.0 (no change).
+*/
+qreal QQuickMultiEffect::saturation() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->saturation();
+}
+
+void QQuickMultiEffect::setSaturation(qreal saturation)
+{
+ Q_D(QQuickMultiEffect);
+ d->setSaturation(saturation);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::colorization
+
+ This property defines how much the source is colorized with the
+ colorizationColor.
+
+ The value ranges from 0.0 (not colorized) to 1.0 (fully colorized).
+ By default, the property is set to \c 0.0 (no change).
+*/
+qreal QQuickMultiEffect::colorization() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->colorization();
+}
+
+void QQuickMultiEffect::setColorization(qreal colorization)
+{
+ Q_D(QQuickMultiEffect);
+ d->setColorization(colorization);
+}
+
+/*!
+ \qmlproperty color QtQuick.Effects::MultiEffect::colorizationColor
+
+ This property defines the RGBA color value which is used to
+ colorize the source.
+
+ By default, the property is set to \c {Qt.rgba(1.0, 0.0, 0.0, 1.0)} (red).
+
+ \sa colorization
+*/
+QColor QQuickMultiEffect::colorizationColor() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->colorizationColor();
+}
+
+void QQuickMultiEffect::setColorizationColor(const QColor &color)
+{
+ Q_D(QQuickMultiEffect);
+ d->setColorizationColor(color);
+}
+
+/*!
+ \qmlproperty bool QtQuick.Effects::MultiEffect::blurEnabled
+
+ Enables the blur effect.
+
+ \include notes.qdocinc performance shader regen
+*/
+bool QQuickMultiEffect::blurEnabled() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->blurEnabled();
+}
+
+void QQuickMultiEffect::setBlurEnabled(bool enabled)
+{
+ Q_D(QQuickMultiEffect);
+ d->setBlurEnabled(enabled);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::blur
+
+ This property defines how much blur (radius) is applied to the source.
+
+ The value ranges from 0.0 (no blur) to 1.0 (full blur). By default,
+ the property is set to \c 0.0 (no change). The amount of full blur
+ is affected by blurMax and blurMultiplier.
+
+ \b {Performance note:} If you don't need to go close to 1.0 at any point
+ of blur animations, consider reducing blurMax or blurMultiplier for
+ optimal performance.
+*/
+qreal QQuickMultiEffect::blur() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->blur();
+}
+
+void QQuickMultiEffect::setBlur(qreal blur)
+{
+ Q_D(QQuickMultiEffect);
+ d->setBlur(blur);
+}
+
+/*!
+ \qmlproperty int QtQuick.Effects::MultiEffect::blurMax
+
+ This property defines the maximum pixel radius that blur with value
+ 1.0 will reach.
+
+ Meaningful range of this value is from 2 (subtle blur) to 64 (high
+ blur). By default, the property is set to \c 32. For the most optimal
+ performance, select as small value as you need.
+
+ \note This affects to both blur and shadow effects.
+
+ \include notes.qdocinc performance shader regen
+
+ \include notes.qdocinc performance item resize
+*/
+int QQuickMultiEffect::blurMax() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->blurMax();
+}
+
+void QQuickMultiEffect::setBlurMax(int blurMax)
+{
+ Q_D(QQuickMultiEffect);
+ d->setBlurMax(blurMax);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::blurMultiplier
+
+ This property defines a multiplier for extending the blur radius.
+
+ The value ranges from 0.0 (not multiplied) to inf. By default,
+ the property is set to \c 0.0. Incresing the multiplier extends the
+ blur radius, but decreases the blur quality. This is more performant
+ option for a bigger blur radius than blurMax as it doesn't increase
+ the amount of texture lookups.
+
+ \note This affects to both blur and shadow effects.
+
+ \include notes.qdocinc performance item resize
+*/
+qreal QQuickMultiEffect::blurMultiplier() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->blurMultiplier();
+}
+
+void QQuickMultiEffect::setBlurMultiplier(qreal blurMultiplier)
+{
+ Q_D(QQuickMultiEffect);
+ d->setBlurMultiplier(blurMultiplier);
+}
+
+/*!
+ \qmlproperty bool QtQuick.Effects::MultiEffect::shadowEnabled
+
+ Enables the shadow effect.
+
+ \include notes.qdocinc performance shader regen
+*/
+bool QQuickMultiEffect::shadowEnabled() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->shadowEnabled();
+}
+
+void QQuickMultiEffect::setShadowEnabled(bool enabled)
+{
+ Q_D(QQuickMultiEffect);
+ d->setShadowEnabled(enabled);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowOpacity
+
+ This property defines the opacity of the drop shadow. This value
+ is multiplied with the \c shadowColor alpha value.
+
+ The value ranges from 0.0 (fully transparent) to 1.0 (fully opaque).
+ By default, the property is set to \c 1.0.
+*/
+qreal QQuickMultiEffect::shadowOpacity() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->shadowOpacity();
+}
+
+void QQuickMultiEffect::setShadowOpacity(qreal shadowOpacity)
+{
+ Q_D(QQuickMultiEffect);
+ d->setShadowOpacity(shadowOpacity);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowBlur
+
+ This property defines how much blur (radius) is applied to the shadow.
+
+ The value ranges from 0.0 (no blur) to 1.0 (full blur). By default,
+ the property is set to \c 1.0. The amount of full blur
+ is affected by blurMax and blurMultiplier.
+
+ \b {Performance note:} The most optimal way to reduce shadow blurring is
+ to make blurMax smaller (if it isn't needed for item blur). Just remember
+ to not adjust blurMax during animations.
+*/
+qreal QQuickMultiEffect::shadowBlur() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->shadowBlur();
+}
+
+void QQuickMultiEffect::setShadowBlur(qreal shadowBlur)
+{
+ Q_D(QQuickMultiEffect);
+ d->setShadowBlur(shadowBlur);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowHorizontalOffset
+
+ This property defines the horizontal offset of the shadow from the
+ item center.
+
+ The value ranges from -inf to inf. By default, the property is set
+ to \c 0.0.
+
+ \note When moving shadow position away from center and adding
+ shadowBlur, you possibly also need to increase the paddingRect
+ accordingly if you want the shadow to not be clipped.
+*/
+qreal QQuickMultiEffect::shadowHorizontalOffset() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->shadowHorizontalOffset();
+}
+
+void QQuickMultiEffect::setShadowHorizontalOffset(qreal offset)
+{
+ Q_D(QQuickMultiEffect);
+ d->setShadowHorizontalOffset(offset);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowVerticalOffset
+
+ This property defines the vertical offset of the shadow from the
+ item center.
+
+ The value ranges from -inf to inf. By default, the property is set
+ to \c 0.0.
+
+ \note When moving shadow position away from center and adding
+ shadowBlur, you possibly also need to increase the paddingRect
+ accordingly if you want the shadow to not be clipped.
+*/
+qreal QQuickMultiEffect::shadowVerticalOffset() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->shadowVerticalOffset();
+}
+
+void QQuickMultiEffect::setShadowVerticalOffset(qreal offset)
+{
+ Q_D(QQuickMultiEffect);
+ d->setShadowVerticalOffset(offset);
+}
+
+/*!
+ \qmlproperty color QtQuick.Effects::MultiEffect::shadowColor
+
+ This property defines the RGBA color value which is used for
+ the shadow. It is useful for example when a shadow is used for
+ simulating a glow effect.
+
+ By default, the property is set to \c {Qt.rgba(0.0, 0.0, 0.0, 1.0)}
+ (black).
+*/
+QColor QQuickMultiEffect::shadowColor() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->shadowColor();
+}
+
+void QQuickMultiEffect::setShadowColor(const QColor &color)
+{
+ Q_D(QQuickMultiEffect);
+ d->setShadowColor(color);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::shadowScale
+
+ This property defines the scale of the shadow. Scaling is applied from
+ the center of the item.
+
+ The value ranges from 0 to inf. By default, the property is set to
+ \c 1.0.
+
+ \note When increasing the shadowScale, you possibly also need to
+ increase the paddingRect accordingly to avoid the shadow from being
+ clipped.
+*/
+qreal QQuickMultiEffect::shadowScale() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->shadowScale();
+}
+
+void QQuickMultiEffect::setShadowScale(qreal shadowScale)
+{
+ Q_D(QQuickMultiEffect);
+ d->setShadowScale(shadowScale);
+}
+
+/*!
+ \qmlproperty bool QtQuick.Effects::MultiEffect::maskEnabled
+
+ Enables the mask effect.
+
+ \include notes.qdocinc performance shader regen
+*/
+bool QQuickMultiEffect::maskEnabled() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->maskEnabled();
+}
+
+void QQuickMultiEffect::setMaskEnabled(bool enabled)
+{
+ Q_D(QQuickMultiEffect);
+ d->setMaskEnabled(enabled);
+}
+
+/*!
+ \qmlproperty Item QtQuick.Effects::MultiEffect::maskSource
+
+ Source item for the mask effect. Should point to ShaderEffectSource,
+ item with \l {QtQuick::Item::layer.enabled} {layer.enabled} set to \c true,
+ or to an item that can be directly used as a texture source (e.g.
+ \l [QML] Image). The alpha channel of the source item is used for masking.
+*/
+QQuickItem *QQuickMultiEffect::maskSource() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->maskSource();
+}
+
+void QQuickMultiEffect::setMaskSource(QQuickItem *item)
+{
+ Q_D(QQuickMultiEffect);
+ d->setMaskSource(item);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMin
+
+ This property defines a lower threshold value for the mask pixels.
+ The mask pixels that have an alpha value below this property are used
+ to completely mask away the corresponding pixels from the source item.
+ The mask pixels that have a higher alpha value are used to alphablend
+ the source item to the display.
+
+ The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By
+ default, the property is set to \c 0.0.
+*/
+qreal QQuickMultiEffect::maskThresholdMin() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->maskThresholdMin();
+}
+
+void QQuickMultiEffect::setMaskThresholdMin(qreal threshold)
+{
+ Q_D(QQuickMultiEffect);
+ d->setMaskThresholdMin(threshold);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMin
+
+ This property defines the smoothness of the mask edges near the
+ maskThresholdMin. Setting higher spread values softens the transition
+ from the transparent mask pixels towards opaque mask pixels by adding
+ interpolated values between them.
+
+ The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge).
+ By default, the property is set to \c 0.0.
+*/
+qreal QQuickMultiEffect::maskSpreadAtMin() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->maskSpreadAtMin();
+}
+
+void QQuickMultiEffect::setMaskSpreadAtMin(qreal spread)
+{
+ Q_D(QQuickMultiEffect);
+ d->setMaskSpreadAtMin(spread);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMax
+
+ This property defines an upper threshold value for the mask pixels.
+ The mask pixels that have an alpha value below this property are used
+ to completely mask away the corresponding pixels from the source item.
+ The mask pixels that have a higher alpha value are used to alphablend
+ the source item to the display.
+
+ The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By
+ default, the property is set to \c 1.0.
+*/
+qreal QQuickMultiEffect::maskThresholdMax() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->maskThresholdMax();
+}
+
+void QQuickMultiEffect::setMaskThresholdMax(qreal threshold)
+{
+ Q_D(QQuickMultiEffect);
+ d->setMaskThresholdMax(threshold);
+}
+
+/*!
+ \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMax
+
+ This property defines the smoothness of the mask edges near the
+ maskThresholdMax. Using higher spread values softens the transition
+ from the transparent mask pixels towards opaque mask pixels by adding
+ interpolated values between them.
+
+ The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge).
+ By default, the property is set to \c 0.0.
+*/
+qreal QQuickMultiEffect::maskSpreadAtMax() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->maskSpreadAtMax();
+}
+
+void QQuickMultiEffect::setMaskSpreadAtMax(qreal spread)
+{
+ Q_D(QQuickMultiEffect);
+ d->setMaskSpreadAtMax(spread);
+}
+
+/*!
+ \qmlproperty bool QtQuick.Effects::MultiEffect::maskInverted
+
+ This property switches the mask to the opposite side; instead of
+ masking away the content outside maskThresholdMin and maskThresholdMax,
+ content between them will get masked away.
+
+ By default, the property is set to \c false.
+*/
+bool QQuickMultiEffect::maskInverted() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->maskInverted();
+}
+
+void QQuickMultiEffect::setMaskInverted(bool inverted)
+{
+ Q_D(QQuickMultiEffect);
+ d->setMaskInverted(inverted);
+}
+
+/*!
+ \qmlproperty rect QtQuick.Effects::MultiEffect::itemRect
+ \readonly
+
+ Read-only access to effect item rectangle. This can be used e.g. to see
+ the area item covers.
+
+ \sa paddingRect, autoPaddingEnabled
+*/
+QRectF QQuickMultiEffect::itemRect() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->itemRect();
+}
+
+/*!
+ \qmlproperty string QtQuick.Effects::MultiEffect::fragmentShader
+ \readonly
+
+ Read-only access to filename of the currently used fragment shader.
+*/
+QString QQuickMultiEffect::fragmentShader() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->fragmentShader();
+}
+
+/*!
+ \qmlproperty string QtQuick.Effects::MultiEffect::vertexShader
+ \readonly
+
+ Read-only access to filename of the currently used vertex shader.
+*/
+QString QQuickMultiEffect::vertexShader() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->vertexShader();
+}
+
+/*!
+ \qmlproperty bool QtQuick.Effects::MultiEffect::hasProxySource
+ \readonly
+
+ Returns true when the MultiEffect internally creates \l ShaderEffectSource
+ for the \l source item and false when \l source item is used as-is.
+ For example when source is \l Image element or \l Item with
+ \l {QtQuick::Item::layer.enabled} {layer.enabled} set to \c true,
+ this additional proxy source is not needed.
+*/
+bool QQuickMultiEffect::hasProxySource() const
+{
+ Q_D(const QQuickMultiEffect);
+ return d->hasProxySource();
+}
+
+// *** protected ***
+
+void QQuickMultiEffect::componentComplete()
+{
+ Q_D(QQuickMultiEffect);
+ QQuickItem::componentComplete();
+ d->initialize();
+}
+
+void QQuickMultiEffect::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickMultiEffect);
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+ if (width() > 0 && height() > 0)
+ d->handleGeometryChange(newGeometry, oldGeometry);
+}
+
+void QQuickMultiEffect::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QQuickMultiEffect);
+ d->handleItemChange(change, value);
+ QQuickItem::itemChange(change, value);
+}
+
+// *** private ***
+
+QQuickMultiEffectPrivate::QQuickMultiEffectPrivate()
+{
+}
+
+QQuickMultiEffectPrivate::~QQuickMultiEffectPrivate()
+{
+}
+
+void QQuickMultiEffectPrivate::handleGeometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_UNUSED(oldGeometry);
+ Q_UNUSED(newGeometry);
+ initialize();
+ if (!m_shaderEffect)
+ return;
+ updateBlurItemSizes();
+ updateSourcePadding();
+}
+
+void QQuickMultiEffectPrivate::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ Q_UNUSED(value);
+ if (change == QQuickItem::ItemSceneChange)
+ initialize();
+}
+
+
+QQuickItem *QQuickMultiEffectPrivate::source() const
+{
+ return m_sourceItem;
+}
+
+void QQuickMultiEffectPrivate::setSource(QQuickItem *item)
+{
+ Q_Q(QQuickMultiEffect);
+ if (item == m_sourceItem)
+ return;
+
+ m_sourceItem = item;
+ if (m_shaderSource)
+ m_shaderSource->setInput(m_sourceItem);
+
+ updateSourcePadding();
+ q->update();
+ Q_EMIT q->sourceChanged();
+}
+
+bool QQuickMultiEffectPrivate::autoPaddingEnabled() const
+{
+ return m_autoPaddingEnabled;
+}
+
+void QQuickMultiEffectPrivate::setAutoPaddingEnabled(bool enabled)
+{
+ Q_Q(QQuickMultiEffect);
+ if (enabled == m_autoPaddingEnabled)
+ return;
+
+ m_autoPaddingEnabled = enabled;
+ updateSourcePadding();
+ q->update();
+ Q_EMIT q->autoPaddingEnabledChanged();
+}
+
+QRectF QQuickMultiEffectPrivate::paddingRect() const
+{
+ return m_paddingRect;
+}
+
+void QQuickMultiEffectPrivate::setPaddingRect(const QRectF &rect)
+{
+ Q_Q(QQuickMultiEffect);
+ if (rect == m_paddingRect)
+ return;
+ m_paddingRect = rect;
+ updateCenterOffset();
+ updateSourcePadding();
+ q->update();
+ emit q->paddingRectChanged();
+}
+
+qreal QQuickMultiEffectPrivate::brightness() const
+{
+ return m_brightness;
+}
+
+void QQuickMultiEffectPrivate::setBrightness(qreal brightness)
+{
+ Q_Q(QQuickMultiEffect);
+ if (brightness == m_brightness)
+ return;
+
+ m_brightness = brightness;
+ if (m_shaderEffect)
+ m_shaderEffect->setProperty("brightness", m_brightness);
+
+ q->update();
+ Q_EMIT q->brightnessChanged();
+}
+
+qreal QQuickMultiEffectPrivate::contrast() const
+{
+ return m_contrast;
+}
+
+void QQuickMultiEffectPrivate::setContrast(qreal contrast)
+{
+ Q_Q(QQuickMultiEffect);
+ if (contrast == m_contrast)
+ return;
+
+ m_contrast = contrast;
+ if (m_shaderEffect)
+ m_shaderEffect->setProperty("contrast", m_contrast);
+
+ q->update();
+ Q_EMIT q->contrastChanged();
+}
+
+qreal QQuickMultiEffectPrivate::saturation() const
+{
+ return m_saturation;
+}
+
+void QQuickMultiEffectPrivate::setSaturation(qreal saturation)
+{
+ Q_Q(QQuickMultiEffect);
+ if (saturation == m_saturation)
+ return;
+
+ m_saturation = saturation;
+ if (m_shaderEffect)
+ m_shaderEffect->setProperty("saturation", m_saturation);
+
+ q->update();
+ Q_EMIT q->saturationChanged();
+}
+
+qreal QQuickMultiEffectPrivate::colorization() const
+{
+ return m_colorization;
+}
+
+void QQuickMultiEffectPrivate::setColorization(qreal colorization)
+{
+ Q_Q(QQuickMultiEffect);
+ if (colorization == m_colorization)
+ return;
+
+ m_colorization = colorization;
+ updateColorizationColor();
+
+ q->update();
+ Q_EMIT q->colorizationChanged();
+}
+
+QColor QQuickMultiEffectPrivate::colorizationColor() const
+{
+ return m_colorizationColor;
+}
+
+void QQuickMultiEffectPrivate::setColorizationColor(const QColor &color)
+{
+ Q_Q(QQuickMultiEffect);
+ if (color == m_colorizationColor)
+ return;
+
+ m_colorizationColor = color;
+ updateColorizationColor();
+
+ q->update();
+ Q_EMIT q->colorizationColorChanged();
+}
+
+bool QQuickMultiEffectPrivate::blurEnabled() const
+{
+ return m_blurEnabled;
+}
+
+void QQuickMultiEffectPrivate::setBlurEnabled(bool enabled)
+{
+ Q_Q(QQuickMultiEffect);
+ if (enabled == m_blurEnabled)
+ return;
+
+ m_blurEnabled = enabled;
+ updateSourcePadding();
+ updateBlurLevel();
+ updateEffectShaders();
+
+ q->update();
+ Q_EMIT q->blurEnabledChanged();
+}
+
+qreal QQuickMultiEffectPrivate::blur() const
+{
+ return m_blur;
+}
+
+void QQuickMultiEffectPrivate::setBlur(qreal blur)
+{
+ Q_Q(QQuickMultiEffect);
+ if (blur == m_blur)
+ return;
+
+ m_blur = blur;
+ updateBlurWeights();
+
+ q->update();
+ Q_EMIT q->blurChanged();
+}
+
+int QQuickMultiEffectPrivate::blurMax() const
+{
+ return m_blurMax;
+}
+
+void QQuickMultiEffectPrivate::setBlurMax(int blurMax)
+{
+ Q_Q(QQuickMultiEffect);
+ if (blurMax == m_blurMax)
+ return;
+
+ m_blurMax = blurMax;
+ updateSourcePadding();
+ updateBlurLevel();
+ updateBlurItemSizes();
+ updateBlurWeights();
+ updateShadowBlurWeights();
+ updateEffectShaders();
+
+ q->update();
+ Q_EMIT q->blurMaxChanged();
+}
+
+qreal QQuickMultiEffectPrivate::blurMultiplier() const
+{
+ return m_blurMultiplier;
+}
+
+void QQuickMultiEffectPrivate::setBlurMultiplier(qreal blurMultiplier)
+{
+ Q_Q(QQuickMultiEffect);
+ if (blurMultiplier == m_blurMultiplier)
+ return;
+
+ m_blurMultiplier = blurMultiplier;
+ updateSourcePadding();
+ updateBlurItemSizes(true);
+ updateBlurWeights();
+ updateShadowBlurWeights();
+
+ q->update();
+ Q_EMIT q->blurMultiplierChanged();
+}
+
+bool QQuickMultiEffectPrivate::shadowEnabled() const
+{
+ return m_shadowEnabled;
+}
+
+void QQuickMultiEffectPrivate::setShadowEnabled(bool enabled)
+{
+ Q_Q(QQuickMultiEffect);
+ if (enabled == m_shadowEnabled)
+ return;
+
+ m_shadowEnabled = enabled;
+ updateSourcePadding();
+ updateBlurLevel();
+ updateEffectShaders();
+
+ q->update();
+ Q_EMIT q->shadowEnabledChanged();
+}
+
+qreal QQuickMultiEffectPrivate::shadowOpacity() const
+{
+ return m_shadowOpacity;
+}
+
+void QQuickMultiEffectPrivate::setShadowOpacity(qreal shadowOpacity)
+{
+ Q_Q(QQuickMultiEffect);
+ if (shadowOpacity == m_shadowOpacity)
+ return;
+
+ m_shadowOpacity = shadowOpacity;
+ updateShadowColor();
+
+ q->update();
+ Q_EMIT q->shadowOpacityChanged();
+}
+
+qreal QQuickMultiEffectPrivate::shadowBlur() const
+{
+ return m_shadowBlur;
+}
+
+void QQuickMultiEffectPrivate::setShadowBlur(qreal shadowBlur)
+{
+ Q_Q(QQuickMultiEffect);
+ if (shadowBlur == m_shadowBlur)
+ return;
+
+ m_shadowBlur = shadowBlur;
+ updateShadowBlurWeights();
+
+ q->update();
+ Q_EMIT q->shadowBlurChanged();
+}
+
+qreal QQuickMultiEffectPrivate::shadowHorizontalOffset() const
+{
+ return m_shadowHorizontalOffset;
+}
+
+void QQuickMultiEffectPrivate::setShadowHorizontalOffset(qreal offset)
+{
+ Q_Q(QQuickMultiEffect);
+ if (offset == m_shadowHorizontalOffset)
+ return;
+
+ m_shadowHorizontalOffset = offset;
+ updateShadowOffset();
+
+ q->update();
+ Q_EMIT q->shadowHorizontalOffsetChanged();
+}
+
+qreal QQuickMultiEffectPrivate::shadowVerticalOffset() const
+{
+ return m_shadowVerticalOffset;
+}
+
+void QQuickMultiEffectPrivate::setShadowVerticalOffset(qreal offset)
+{
+ Q_Q(QQuickMultiEffect);
+ if (offset == m_shadowVerticalOffset)
+ return;
+
+ m_shadowVerticalOffset = offset;
+ updateShadowOffset();
+
+ q->update();
+ Q_EMIT q->shadowVerticalOffsetChanged();
+}
+
+QColor QQuickMultiEffectPrivate::shadowColor() const
+{
+ return m_shadowColor;
+}
+
+void QQuickMultiEffectPrivate::setShadowColor(const QColor &color)
+{
+ Q_Q(QQuickMultiEffect);
+ if (color == m_shadowColor)
+ return;
+
+ m_shadowColor = color;
+ updateShadowColor();
+
+ q->update();
+ Q_EMIT q->shadowColorChanged();
+}
+
+qreal QQuickMultiEffectPrivate::shadowScale() const
+{
+ return m_shadowScale;
+}
+
+void QQuickMultiEffectPrivate::setShadowScale(qreal shadowScale)
+{
+ Q_Q(QQuickMultiEffect);
+ if (shadowScale == m_shadowScale)
+ return;
+
+ m_shadowScale = shadowScale;
+ updateCenterOffset();
+ if (m_shaderEffect)
+ m_shaderEffect->setProperty("shadowScale", 1.0 / m_shadowScale);
+
+ q->update();
+ Q_EMIT q->shadowScaleChanged();
+}
+
+bool QQuickMultiEffectPrivate::maskEnabled() const
+{
+ return m_maskEnabled;
+}
+
+void QQuickMultiEffectPrivate::setMaskEnabled(bool enabled)
+{
+ Q_Q(QQuickMultiEffect);
+ if (enabled == m_maskEnabled)
+ return;
+
+ m_maskEnabled = enabled;
+ updateEffectShaders();
+
+ q->update();
+ Q_EMIT q->maskEnabledChanged();
+}
+
+QQuickItem *QQuickMultiEffectPrivate::maskSource() const
+{
+ return m_maskSourceItem;
+}
+
+void QQuickMultiEffectPrivate::setMaskSource(QQuickItem *item)
+{
+ Q_Q(QQuickMultiEffect);
+ if (item == m_maskSourceItem)
+ return;
+
+ m_maskSourceItem = item;
+ if (m_shaderEffect) {
+ auto maskSourceVariant = QVariant::fromValue<QQuickItem*>(m_maskSourceItem);
+ m_shaderEffect->setProperty("maskSrc", maskSourceVariant);
+ }
+
+ q->update();
+ Q_EMIT q->maskSourceChanged();
+}
+
+qreal QQuickMultiEffectPrivate::maskThresholdMin() const
+{
+ return m_maskThresholdMin;
+}
+
+void QQuickMultiEffectPrivate::setMaskThresholdMin(qreal threshold)
+{
+ Q_Q(QQuickMultiEffect);
+ if (threshold == m_maskThresholdMin)
+ return;
+
+ m_maskThresholdMin = threshold;
+ updateMaskThresholdSpread();
+
+ q->update();
+ Q_EMIT q->maskThresholdMinChanged();
+}
+
+qreal QQuickMultiEffectPrivate::maskSpreadAtMin() const
+{
+ return m_maskSpreadAtMin;
+}
+
+void QQuickMultiEffectPrivate::setMaskSpreadAtMin(qreal spread)
+{
+ Q_Q(QQuickMultiEffect);
+ if (spread == m_maskSpreadAtMin)
+ return;
+
+ m_maskSpreadAtMin = spread;
+ updateMaskThresholdSpread();
+
+ q->update();
+ Q_EMIT q->maskSpreadAtMinChanged();
+}
+
+qreal QQuickMultiEffectPrivate::maskThresholdMax() const
+{
+ return m_maskThresholdMax;
+}
+
+void QQuickMultiEffectPrivate::setMaskThresholdMax(qreal threshold)
+{
+ Q_Q(QQuickMultiEffect);
+ if (threshold == m_maskThresholdMax)
+ return;
+
+ m_maskThresholdMax = threshold;
+ updateMaskThresholdSpread();
+
+ q->update();
+ Q_EMIT q->maskThresholdMaxChanged();
+}
+
+qreal QQuickMultiEffectPrivate::maskSpreadAtMax() const
+{
+ return m_maskSpreadAtMax;
+}
+
+void QQuickMultiEffectPrivate::setMaskSpreadAtMax(qreal spread)
+{
+ Q_Q(QQuickMultiEffect);
+ if (spread == m_maskSpreadAtMax)
+ return;
+
+ m_maskSpreadAtMax = spread;
+ updateMaskThresholdSpread();
+
+ q->update();
+ Q_EMIT q->maskSpreadAtMaxChanged();
+}
+
+bool QQuickMultiEffectPrivate::maskInverted() const
+{
+ return m_maskInverted;
+}
+
+void QQuickMultiEffectPrivate::setMaskInverted(bool inverted)
+{
+ Q_Q(QQuickMultiEffect);
+ if (inverted == m_maskInverted)
+ return;
+
+ m_maskInverted = inverted;
+ if (m_shaderEffect)
+ m_shaderEffect->setProperty("maskInverted", float(m_maskInverted));
+
+ q->update();
+ Q_EMIT q->maskInvertedChanged();
+}
+
+QRectF QQuickMultiEffectPrivate::itemRect() const
+{
+ if (!m_shaderEffect || !m_shaderSource)
+ return QRectF();
+
+ QRectF sourceRect = m_shaderSource->sourceRect();
+ if (sourceRect.width() > 0 && sourceRect.height() > 0)
+ return sourceRect;
+ else
+ return m_shaderEffect->boundingRect();
+}
+
+QString QQuickMultiEffectPrivate::fragmentShader() const
+{
+ return m_fragShader;
+}
+
+QString QQuickMultiEffectPrivate::vertexShader() const
+{
+ return m_vertShader;
+}
+
+bool QQuickMultiEffectPrivate::hasProxySource() const
+{
+ return m_shaderSource && m_shaderSource->isActive();
+}
+
+// This initializes the component. It will be ran once, when
+// the component is ready and it has a valid size.
+void QQuickMultiEffectPrivate::initialize()
+{
+ Q_Q(QQuickMultiEffect);
+ if (m_initialized)
+ return;
+ if (!q->isComponentComplete())
+ return;
+ if (!q->window())
+ return;
+ if (q->width() <= 0 || q->height() <= 0)
+ return;
+
+ m_shaderEffect = new QQuickShaderEffect(q);
+ m_shaderSource = new QGfxSourceProxy(q);
+ QObject::connect(m_shaderSource, &QGfxSourceProxy::outputChanged, q, [this] { proxyOutputChanged(); });
+ QObject::connect(m_shaderSource, &QGfxSourceProxy::activeChanged, q, &QQuickMultiEffect::hasProxySourceChanged);
+
+ m_shaderEffect->setParentItem(q);
+ m_shaderEffect->setSize(q->size());
+
+ m_shaderSource->setParentItem(q);
+ m_shaderSource->setSize(q->size());
+ m_shaderSource->setInput(m_sourceItem);
+
+ updateCenterOffset();
+ updateMaskThresholdSpread();
+ updateBlurWeights();
+ updateShadowBlurWeights();
+ updateColorizationColor();
+ updateShadowColor();
+ updateShadowOffset();
+
+ // Create properties
+ auto sourceVariant = QVariant::fromValue<QQuickItem*>(m_shaderSource->output());
+ m_shaderEffect->setProperty("src", sourceVariant);
+ m_shaderEffect->setProperty("brightness", m_brightness);
+ m_shaderEffect->setProperty("contrast", m_contrast);
+ m_shaderEffect->setProperty("saturation", m_saturation);
+ m_shaderEffect->setProperty("shadowScale", 1.0 / m_shadowScale);
+ auto maskSourceVariant = QVariant::fromValue<QQuickItem*>(m_maskSourceItem);
+ m_shaderEffect->setProperty("maskSrc", maskSourceVariant);
+ m_shaderEffect->setProperty("maskInverted", float(m_maskInverted));
+
+ updateBlurLevel();
+ updateBlurItemSizes();
+ updateSourcePadding();
+
+ updateEffectShaders();
+
+ m_initialized = true;
+}
+
+void QQuickMultiEffectPrivate::updateMaskThresholdSpread()
+{
+ if (!m_shaderEffect)
+ return;
+
+ // Calculate threshold and spread values for mask
+ // smoothstep, keeping always edge0 < edge1.
+ const qreal c0 = 0.0001;
+ const qreal c1 = 1.0 - c0;
+ const qreal mt1 = m_maskThresholdMin + c0;
+ const qreal ms1 = m_maskSpreadAtMin + 1.0;
+ const qreal mt2 = c1 - m_maskThresholdMax;
+ const qreal ms2 = m_maskSpreadAtMax + 1.0;
+ const QVector4D maskThresholdSpread = QVector4D(
+ mt1 * ms1 - (ms1 - c1),
+ mt1 * ms1,
+ mt2 * ms2 - (ms2 - c1),
+ mt2 * ms2);
+ m_shaderEffect->setProperty("mask", maskThresholdSpread);
+}
+
+void QQuickMultiEffectPrivate::updateCenterOffset()
+{
+ if (!m_shaderEffect)
+ return;
+
+ const qreal scale = 1.0 / m_shadowScale;
+ QVector2D centerOffset((1.0 - scale) * (0.5 + 0.5 * (m_paddingRect.x() - m_paddingRect.width()) / m_shaderEffect->width()),
+ (1.0 - scale) * (0.5 + 0.5 * (m_paddingRect.y() - m_paddingRect.height()) / m_shaderEffect->height()));
+ m_shaderEffect->setProperty("centerOffset", centerOffset);
+}
+
+void QQuickMultiEffectPrivate::updateShadowOffset()
+{
+ if (!m_shaderEffect)
+ return;
+
+ QVector2D shadowOffset = QVector2D(m_shadowHorizontalOffset / m_shaderEffect->width(), m_shadowVerticalOffset / m_shaderEffect->height());
+ m_shaderEffect->setProperty("shadowOffset", shadowOffset);
+}
+
+void QQuickMultiEffectPrivate::updateColorizationColor()
+{
+ if (!m_shaderEffect)
+ return;
+
+ float alpha = std::clamp(float(m_colorizationColor.alphaF() * m_colorization), 0.0f, 1.0f);
+ QVector4D colorizationColor(m_colorizationColor.redF(),
+ m_colorizationColor.greenF(),
+ m_colorizationColor.blueF(),
+ alpha);
+ m_shaderEffect->setProperty("colorizationColor", colorizationColor);
+}
+
+void QQuickMultiEffectPrivate::updateShadowColor()
+{
+ if (!m_shaderEffect)
+ return;
+
+ float alpha = std::clamp(float(m_shadowColor.alphaF() * m_shadowOpacity), 0.0f, 1.0f);
+ QVector4D shadowColor(m_shadowColor.redF(),
+ m_shadowColor.greenF(),
+ m_shadowColor.blueF(),
+ alpha);
+
+ m_shaderEffect->setProperty("shadowColor", shadowColor);
+}
+
+float QQuickMultiEffectPrivate::calculateLod(float blurAmount)
+{
+ return qSqrt(blurAmount * float(m_blurMax) / 64.0f) * 1.2f - 0.2f;
+}
+
+float QQuickMultiEffectPrivate::blurWeight(float v)
+{
+ return std::max(0.0f, std::min(1.0f, 1.0f - v * 2.0f));
+}
+
+void QQuickMultiEffectPrivate::getBlurWeights(float blurLod, QVector4D &blurWeight1, QVector2D &blurWeight2)
+{
+ float bw1 = blurWeight(std::fabs(blurLod - 0.1f));
+ float bw2 = blurWeight(std::fabs(blurLod - 0.3f));
+ float bw3 = blurWeight(std::fabs(blurLod - 0.5f));
+ float bw4 = blurWeight(std::fabs(blurLod - 0.7f));
+ float bw5 = blurWeight(std::fabs(blurLod - 0.9f));
+ float bw6 = blurWeight(std::fabs(blurLod - 1.1f));
+ float bsum = bw1 + bw2 + bw3 + bw4 + bw5 + bw6;
+ blurWeight1 = QVector4D(bw1 / bsum, bw2 / bsum, bw3 / bsum, bw4 / bsum);
+ blurWeight2 = QVector2D(bw5 / bsum, bw6 / bsum);
+}
+
+void QQuickMultiEffectPrivate::updateBlurWeights()
+{
+ if (!m_shaderEffect)
+ return;
+ float blurLod = calculateLod(m_blur);
+ getBlurWeights(blurLod, m_blurWeight1, m_blurWeight2);
+ m_shaderEffect->setProperty("blurWeight1", m_blurWeight1);
+ m_shaderEffect->setProperty("blurWeight2", m_blurWeight2);
+}
+
+void QQuickMultiEffectPrivate::updateShadowBlurWeights()
+{
+ if (!m_shaderEffect)
+ return;
+ float blurLod = calculateLod(m_shadowBlur);
+ getBlurWeights(blurLod, m_shadowBlurWeight1, m_shadowBlurWeight2);
+ m_shaderEffect->setProperty("shadowBlurWeight1", m_shadowBlurWeight1);
+ m_shaderEffect->setProperty("shadowBlurWeight2", m_shadowBlurWeight2);
+}
+
+void QQuickMultiEffectPrivate::updateBlurItemSizes(bool forceUpdate)
+{
+ if (m_blurEffects.isEmpty() || !m_shaderSource || !m_sourceItem)
+ return;
+
+ // First blur item size to be half of th source item
+ // extended size, rounded to next divisible by 16.
+ QSizeF sourceSize = itemRect().size();
+ QSizeF firstItemSize(std::ceil(sourceSize.width() / 16) * 8,
+ std::ceil(sourceSize.height() / 16) * 8);
+
+ if (!forceUpdate && m_firstBlurItemSize == firstItemSize)
+ return;
+
+ qCDebug(lcQuickEffect) << "Source size:" << sourceSize;
+ m_firstBlurItemSize = firstItemSize;
+
+ for (int i = 0; i < m_blurEffects.size(); i++) {
+ auto *blurEffect = m_blurEffects[i];
+ QSizeF itemSize = (i == 0) ? firstItemSize : m_blurEffects[i - 1]->size() * 0.5;
+ qCDebug(lcQuickEffect) << "Blur item" << i << ":" << itemSize;
+ blurEffect->setSize(itemSize);
+
+ const QVector2D offset((1.0 + m_blurMultiplier) / itemSize.width(),
+ (1.0 + m_blurMultiplier) / itemSize.height());
+ blurEffect->setProperty("offset", offset);
+ }
+}
+
+void QQuickMultiEffectPrivate::updateEffectShaders()
+{
+ Q_Q(QQuickMultiEffect);
+ if (!q->isComponentComplete() || !m_shaderEffect)
+ return;
+
+ QString vShader = QStringLiteral("multieffect_c");
+ if (m_shadowEnabled)
+ vShader += QStringLiteral("s");
+
+ QString fShader = QStringLiteral("multieffect_c");
+ if (m_maskEnabled)
+ fShader += QStringLiteral("m");
+ if (m_blurEnabled && m_blurMax > 0)
+ fShader += QStringLiteral("b");
+ if (m_shadowEnabled)
+ fShader += QStringLiteral("s");
+
+ fShader += QString::number(m_blurLevel);
+
+ bool shaderChanged = false;
+ if (fShader != m_fragShader) {
+ shaderChanged = true;
+ m_fragShader = fShader;
+ QUrl fs = QUrl(QStringLiteral("qrc:/data/shaders/%1.frag.qsb").arg(m_fragShader));
+ m_shaderEffect->setFragmentShader(fs);
+ Q_EMIT q->fragmentShaderChanged();
+ }
+ if (vShader != m_vertShader) {
+ shaderChanged = true;
+ m_vertShader = vShader;
+ QUrl vs = QUrl(QStringLiteral("qrc:/data/shaders/%1.vert.qsb").arg(m_vertShader));
+ m_shaderEffect->setVertexShader(vs);
+ Q_EMIT q->vertexShaderChanged();
+ }
+ if (shaderChanged) {
+ qCDebug(lcQuickEffect) << this << "Shaders: " << m_fragShader << m_vertShader;
+ Q_EMIT q->shaderChanged();
+ }
+}
+
+void QQuickMultiEffectPrivate::updateBlurLevel(bool forceUpdate)
+{
+ int blurLevel = 0;
+ if ((m_blurEnabled || m_shadowEnabled) && m_blurMax > 0) {
+ if (m_blurMax > 32)
+ blurLevel = 3;
+ else if (m_blurMax > 16)
+ blurLevel = 2;
+ else
+ blurLevel = 1;
+ }
+
+ if (blurLevel != m_blurLevel || (blurLevel > 0 && m_blurEffects.isEmpty()) || forceUpdate) {
+ // Blur level has changed or blur items need to be
+ // initially created.
+ updateBlurItemsAmount(blurLevel);
+ // When the level grows, new items must be resized
+ if (blurLevel > m_blurLevel)
+ updateBlurItemSizes(true);
+ }
+ m_blurLevel = blurLevel;
+}
+
+void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel)
+{
+ Q_Q(QQuickMultiEffect);
+ if (!m_shaderEffect)
+ return;
+
+ const auto engine = qmlEngine(q);
+ if (!engine)
+ return;
+
+ // Lowest blur level uses 3 items, highest 5 items.
+ int itemsAmount = blurLevel == 0 ? 0 : blurLevel + 2;
+
+ if (m_blurEffects.size() < itemsAmount) {
+ // Add more blur items.
+ // Note that by design blur items are only added and never reduced
+ // during the lifetime of the effect component.
+ QUrl blurVs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.vert.qsb"));
+ QUrl blurFs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.frag.qsb"));
+ QQmlComponent blurComponent(engine, QUrl(QStringLiteral("qrc:/data/BlurItem.qml")));
+ for (int i = m_blurEffects.size(); i < itemsAmount; i++) {
+ auto blurEffect = qobject_cast<QQuickShaderEffect*>(blurComponent.create());
+ blurEffect->setParent(q);
+ blurEffect->setParentItem(q);
+ auto sourceVariant = QVariant::fromValue<QQuickItem*>(blurEffect);
+ QString sourceProperty = QStringLiteral("blurSrc%1").arg(i + 1);
+ m_shaderEffect->setProperty(sourceProperty.toUtf8(), sourceVariant);
+ // Initial value to avoid "'source' does not have a matching property" warning.
+ // Will be updated with the correct one few lines forward.
+ blurEffect->setProperty("source", sourceVariant);
+ QQuickItemPrivate *priv = QQuickItemPrivate::get(blurEffect);
+ priv->layer()->setEnabled(true);
+ priv->layer()->setSmooth(true);
+ blurEffect->setVertexShader(blurVs);
+ blurEffect->setFragmentShader(blurFs);
+ m_blurEffects << blurEffect;
+ }
+ }
+
+ // Set the blur items source components
+ if (!m_dummyShaderSource)
+ m_dummyShaderSource = new QQuickShaderEffectSource(q);
+ for (int i = 0; i < m_blurEffects.size(); i++) {
+ auto *blurEffect = m_blurEffects[i];
+ auto sourceItem = (i >= itemsAmount) ?
+ static_cast<QQuickItem *>(m_dummyShaderSource) : (i == 0) ?
+ static_cast<QQuickItem *>(m_shaderSource->output()) :
+ static_cast<QQuickItem *>(m_blurEffects[i - 1]);
+ auto sourceVariant = QVariant::fromValue<QQuickItem*>(sourceItem);
+ blurEffect->setProperty("source", sourceVariant);
+ }
+}
+
+void QQuickMultiEffectPrivate::updateSourcePadding()
+{
+ Q_Q(QQuickMultiEffect);
+ if (!m_shaderEffect || !m_shaderSource)
+ return;
+
+ const bool blurItemsNeeded = (m_blurEnabled || m_shadowEnabled) && (m_blurMax > 0);
+ const int itemPadding = m_autoPaddingEnabled && blurItemsNeeded ? m_blurMax * (1.0 + m_blurMultiplier) : 0;
+
+ // Set the shader effect size
+ if (m_paddingRect != QRectF() || itemPadding > 0) {
+ QRectF effectRect(-m_paddingRect.x() - itemPadding,
+ -m_paddingRect.y() - itemPadding,
+ q->width() + m_paddingRect.x() + m_paddingRect.width() + (itemPadding * 2),
+ q->height() + m_paddingRect.y() + m_paddingRect.height() + (itemPadding * 2));
+ m_shaderEffect->setX(effectRect.x());
+ m_shaderEffect->setY(effectRect.y());
+ m_shaderEffect->setWidth(effectRect.width());
+ m_shaderEffect->setHeight(effectRect.height());
+
+ // Set the source size
+ m_shaderSource->setSize(m_shaderEffect->size());
+
+ // When m_sourceItem is set and has size, use that as the base size.
+ // When effect is used as a component in Item "layer.effect", source
+ // doesn't have a size and then we follow the effect item size.
+ const qreal baseWidth = m_sourceItem && m_sourceItem->width() > 0 ? m_sourceItem->width() : q->width();
+ const qreal baseHeight = m_sourceItem && m_sourceItem->height() > 0 ? m_sourceItem->height() : q->height();
+
+ // Set the source rect
+ const qreal widthMultiplier = q->width() > 0 ? baseWidth / q->width() : 1.0;
+ const qreal heightMultiplier = q->height() > 0 ? baseHeight / q->height() : 1.0;
+ const qreal xPadding = itemPadding * widthMultiplier;
+ const qreal yPadding = itemPadding * heightMultiplier;
+ QRectF rect = QRectF(m_paddingRect.x() * widthMultiplier,
+ m_paddingRect.y() * heightMultiplier,
+ m_paddingRect.width() * widthMultiplier,
+ m_paddingRect.height() * heightMultiplier);
+ QRectF sourceRect = QRectF(-rect.x() - xPadding,
+ -rect.y() - yPadding,
+ baseWidth + rect.x() + rect.width() + xPadding * 2,
+ baseHeight + rect.y() + rect.height() + yPadding * 2);
+ m_shaderSource->setSourceRect(sourceRect);
+ } else {
+ m_shaderEffect->setX(0);
+ m_shaderEffect->setY(0);
+ m_shaderEffect->setSize(q->size());
+ m_shaderSource->setSize(q->size());
+ m_shaderSource->setSourceRect(QRectF());
+ }
+
+ updateShadowOffset();
+ updateProxyActiveCheck();
+ updateBlurItemSizes();
+ Q_EMIT q->paddingRectChanged();
+ Q_EMIT q->itemRectChanged();
+ Q_EMIT q->itemSizeChanged();
+}
+
+void QQuickMultiEffectPrivate::proxyOutputChanged()
+{
+ if (!m_shaderSource)
+ return;
+
+ auto sourceVariant = QVariant::fromValue<QQuickItem*>(m_shaderSource->output());
+ m_shaderEffect->setProperty("src", sourceVariant);
+
+ // Force updating the blur items since the source output has changed
+ updateBlurLevel(true);
+ updateBlurItemSizes();
+ updateSourcePadding();
+}
+
+void QQuickMultiEffectPrivate::updateProxyActiveCheck()
+{
+ if (!m_shaderSource)
+ return;
+
+ m_shaderSource->polish();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquickmultieffect_p.cpp"