qtdeclarative/src/effects/qquickmultieffect.cpp

1630 lines
43 KiB
C++
Raw Normal View History

// 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
/*!
\qmltype MultiEffect
\instantiates QQuickMultiEffect
\inqmlmodule QtQuick
\inherits Item
\ingroup qtquick-effects
\brief Applies post-processing effect to an item.
The MultiEffect type applies a post-processing effect to \c source item.
Compared to \c {Qt Graphical Effects} module, MultiEffect combines multiple
effects (blur, shadow, colorize etc.) into a single item and shader. This
makes it optimal for multiple effects.
MultiEffect is designed especially for animated effects. There are several
shader variations and the most optimal one gets selected based on the
features used.
*/
/*!
\qmlsignal QtQuick::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::MultiEffect::source
Source item for the effect. This does not need to be ShaderEffectSource
or have \c {layer.enabled} set to \c true as QuickMultiEffect will
internally generate a ShaderEffectSource as the texture source.
*/
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::MultiEffect::hideSource
When enabled, source item is hidden. This is often preferred when the
effect item replaces the source item. Another option is to set the source
item's \e visibility property to \c false, but that disables also user
interaction (for example, events from a MouseArea). Therefore, hiding the
the source item may be preferred.
By default, this property is set to \c true.
*/
bool QQuickMultiEffect::hideSource() const
{
Q_D(const QQuickMultiEffect);
return d->hideSource();
}
void QQuickMultiEffect::setHideSource(bool hide)
{
Q_D(QQuickMultiEffect);
d->setHideSource(hide);
}
/*!
\qmlproperty bool QtQuick::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 \c paddingRect is always added to the size.
\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::MultiEffect::paddingRect
Set this to increase item size manually so that blur and/or shadows will fit.
If paddingEnabled is enabled 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 needs to take shadowHorizontalOffset and
shadowVerticalOffset into account and adjust this paddingRect accordingly.
\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::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::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::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::MultiEffect::colorize
This property defines how much the source is colorized with the
colorizeColor.
The value ranges from 0.0 (no colorized) to 1.0 (fully colorized).
By default, the property is set to \c 0.0 (no change).
*/
qreal QQuickMultiEffect::colorize() const
{
Q_D(const QQuickMultiEffect);
return d->colorize();
}
void QQuickMultiEffect::setColorize(qreal colorize)
{
Q_D(QQuickMultiEffect);
d->setColorize(colorize);
}
/*!
\qmlproperty color QtQuick::MultiEffect::colorizeColor
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).
*/
QColor QQuickMultiEffect::colorizeColor() const
{
Q_D(const QQuickMultiEffect);
return d->colorizeColor();
}
void QQuickMultiEffect::setColorizeColor(const QColor &color)
{
Q_D(QQuickMultiEffect);
d->setColorizeColor(color);
}
/*!
\qmlproperty bool QtQuick::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::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::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::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::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::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::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::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 10.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::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 10.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::MultiEffect::shadowColor
This property defines the RGBA color value which is used to colorize
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::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::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::MultiEffect::maskSource
Source item for the mask effect. Should point to ShaderEffectSource,
item with \c {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::MultiEffect::maskThresholdLow
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::maskThresholdLow() const
{
Q_D(const QQuickMultiEffect);
return d->maskThresholdLow();
}
void QQuickMultiEffect::setMaskThresholdLow(qreal threshold)
{
Q_D(QQuickMultiEffect);
d->setMaskThresholdLow(threshold);
}
/*!
\qmlproperty real QtQuick::MultiEffect::maskSpreadLow
This property defines the smoothness of the mask edges near the
maskThresholdLow. 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::maskSpreadLow() const
{
Q_D(const QQuickMultiEffect);
return d->maskSpreadLow();
}
void QQuickMultiEffect::setMaskSpreadLow(qreal spread)
{
Q_D(QQuickMultiEffect);
d->setMaskSpreadLow(spread);
}
/*!
\qmlproperty real QtQuick::MultiEffect::maskThresholdUp
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::maskThresholdUp() const
{
Q_D(const QQuickMultiEffect);
return d->maskThresholdUp();
}
void QQuickMultiEffect::setMaskThresholdUp(qreal threshold)
{
Q_D(QQuickMultiEffect);
d->setMaskThresholdUp(threshold);
}
/*!
\qmlproperty real QtQuick::MultiEffect::maskSpreadUp
This property defines the smoothness of the mask edges near the
maskThresholdUp. 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::maskSpreadUp() const
{
Q_D(const QQuickMultiEffect);
return d->maskSpreadUp();
}
void QQuickMultiEffect::setMaskSpreadUp(qreal spread)
{
Q_D(QQuickMultiEffect);
d->setMaskSpreadUp(spread);
}
/*!
\qmlproperty bool QtQuick::MultiEffect::maskInverted
This property switches the mask to the opposite side; instead of
masking away the content outside maskThresholdLow and maskThresholdUp,
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::MultiEffect::itemRect
Read-only access to effect item rectangle. This can be used e.g. to see
the area item covers.
*/
QRectF QQuickMultiEffect::itemRect() const
{
Q_D(const QQuickMultiEffect);
return d->itemRect();
}
/*!
\qmlproperty string QtQuick::MultiEffect::fragmentShader
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::MultiEffect::vertexShader
Read-only access to filename of the currently used vertex shader.
*/
QString QQuickMultiEffect::vertexShader() const
{
Q_D(const QQuickMultiEffect);
return d->vertexShader();
}
// *** 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->setSourceItem(m_sourceItem);
updateSourcePadding();
q->update();
Q_EMIT q->sourceChanged();
}
bool QQuickMultiEffectPrivate::hideSource() const
{
return m_hideSource;
}
void QQuickMultiEffectPrivate::setHideSource(bool hide)
{
Q_Q(QQuickMultiEffect);
if (hide == m_hideSource)
return;
m_hideSource = hide;
if (m_shaderSource)
m_shaderSource->setHideSource(m_hideSource);
q->update();
Q_EMIT q->hideSourceChanged();
}
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::colorize() const
{
return m_colorize;
}
void QQuickMultiEffectPrivate::setColorize(qreal colorize)
{
Q_Q(QQuickMultiEffect);
if (colorize == m_colorize)
return;
m_colorize = colorize;
updateColorizeColor();
q->update();
Q_EMIT q->colorizeChanged();
}
QColor QQuickMultiEffectPrivate::colorizeColor() const
{
return m_colorizeColor;
}
void QQuickMultiEffectPrivate::setColorizeColor(const QColor &color)
{
Q_Q(QQuickMultiEffect);
if (color == m_colorizeColor)
return;
m_colorizeColor = color;
updateColorizeColor();
q->update();
Q_EMIT q->colorizeColorChanged();
}
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();
if (m_shaderEffect)
m_shaderEffect->setProperty("blurEnabled", m_blurEnabled);
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();
if (m_shaderEffect)
m_shaderEffect->setProperty("blur", m_blur);
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();
if (m_shaderEffect)
m_shaderEffect->setProperty("blurMax", m_blurMax);
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();
updateBlurWeights();
updateShadowBlurWeights();
if (m_shaderEffect)
m_shaderEffect->setProperty("blurMultiplier", m_blurMultiplier);
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();
if (m_shaderEffect)
m_shaderEffect->setProperty("shadowEnabled", m_shadowEnabled);
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();
if (m_shaderEffect)
m_shaderEffect->setProperty("shadowBlur", m_shadowBlur);
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();
if (m_shaderEffect)
m_shaderEffect->setProperty("maskEnabled", m_maskEnabled);
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::maskThresholdLow() const
{
return m_maskThresholdLow;
}
void QQuickMultiEffectPrivate::setMaskThresholdLow(qreal threshold)
{
Q_Q(QQuickMultiEffect);
if (threshold == m_maskThresholdLow)
return;
m_maskThresholdLow = threshold;
updateMaskThresholdSpread();
q->update();
Q_EMIT q->maskThresholdLowChanged();
}
qreal QQuickMultiEffectPrivate::maskSpreadLow() const
{
return m_maskSpreadLow;
}
void QQuickMultiEffectPrivate::setMaskSpreadLow(qreal spread)
{
Q_Q(QQuickMultiEffect);
if (spread == m_maskSpreadLow)
return;
m_maskSpreadLow = spread;
updateMaskThresholdSpread();
q->update();
Q_EMIT q->maskSpreadLowChanged();
}
qreal QQuickMultiEffectPrivate::maskThresholdUp() const
{
return m_maskThresholdUp;
}
void QQuickMultiEffectPrivate::setMaskThresholdUp(qreal threshold)
{
Q_Q(QQuickMultiEffect);
if (threshold == m_maskThresholdUp)
return;
m_maskThresholdUp = threshold;
updateMaskThresholdSpread();
q->update();
Q_EMIT q->maskThresholdUpChanged();
}
qreal QQuickMultiEffectPrivate::maskSpreadUp() const
{
return m_maskSpreadUp;
}
void QQuickMultiEffectPrivate::setMaskSpreadUp(qreal spread)
{
Q_Q(QQuickMultiEffect);
if (spread == m_maskSpreadUp)
return;
m_maskSpreadUp = spread;
updateMaskThresholdSpread();
q->update();
Q_EMIT q->maskSpreadUpChanged();
}
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;
}
// 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 QQuickShaderEffectSource(q);
m_shaderEffect->setParentItem(q);
m_shaderEffect->setSize(q->size());
m_shaderSource->setParentItem(q);
m_shaderSource->setSize(q->size());
m_shaderSource->setSourceItem(m_sourceItem);
m_shaderSource->setHideSource(m_hideSource);
m_shaderSource->setVisible(false);
updateCenterOffset();
updateMaskThresholdSpread();
updateBlurWeights();
updateShadowBlurWeights();
updateColorizeColor();
updateShadowOffset();
// Create properties
auto sourceVariant = QVariant::fromValue<QQuickItem*>(m_shaderSource);
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("blurEnabled", m_blurEnabled);
m_shaderEffect->setProperty("blur", m_blur);
m_shaderEffect->setProperty("blurMax", m_blurMax);
m_shaderEffect->setProperty("blurMultiplier", m_blurMultiplier);
m_shaderEffect->setProperty("shadowEnabled", m_shadowEnabled);
m_shaderEffect->setProperty("shadowOpacity", m_shadowOpacity);
m_shaderEffect->setProperty("shadowBlur", m_shadowBlur);
m_shaderEffect->setProperty("shadowColor", m_shadowColor);
m_shaderEffect->setProperty("shadowScale", 1.0 / m_shadowScale);
m_shaderEffect->setProperty("maskEnabled", m_maskEnabled);
auto maskSourceVariant = QVariant::fromValue<QQuickItem*>(m_maskSourceItem);
m_shaderEffect->setProperty("maskSrc", maskSourceVariant);
m_shaderEffect->setProperty("maskInverted", float(m_maskInverted));
m_shaderEffect->setProperty("centerOffset", m_centerOffset);
updateBlurLevel();
updateBlurItemSizes();
updateSourcePadding();
updateEffectShaders();
m_initialized = true;
}
void QQuickMultiEffectPrivate::updateMaskThresholdSpread()
{
m_maskThresholdSpread = QVector4D(
m_maskThresholdLow,
m_maskSpreadLow + 1.0,
m_maskThresholdUp,
m_maskSpreadUp + 1.0
);
if (m_shaderEffect)
m_shaderEffect->setProperty("mask", m_maskThresholdSpread);
}
void QQuickMultiEffectPrivate::updateCenterOffset()
{
if (!m_shaderEffect)
return;
const qreal scale = 1.0 / m_shadowScale;
m_centerOffset = QVector2D((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", m_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::updateColorizeColor()
{
if (!m_shaderEffect)
return;
int alpha = std::clamp(int(m_colorizeColor.alpha() * m_colorize), 0, 255);
QColor colorizeColor = m_colorizeColor;
colorizeColor.setAlpha(alpha);
m_shaderEffect->setProperty("colorizeColor", colorizeColor);
}
void QQuickMultiEffectPrivate::updateShadowColor()
{
if (!m_shaderEffect)
return;
int alpha = std::clamp(int(m_shadowColor.alpha() * m_shadowOpacity), 0, 255);
QColor shadowColor = m_shadowColor;
shadowColor.setAlpha(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()
{
Q_Q(QQuickMultiEffect);
if (m_blurEffects.isEmpty())
return;
for (int i = 0; i < m_blurEffects.size(); i++) {
auto *blurEffect = m_blurEffects[i];
const QSizeF firstItemSize = QSizeF(std::ceil(q->width() / 64) * 64,
std::ceil(q->height() / 64) * 64);
QSizeF itemSize = (i == 0) ? firstItemSize : m_blurEffects[i - 1]->size() * 0.5;
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())
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)
Q_EMIT q->shaderChanged();
}
void QQuickMultiEffectPrivate::updateBlurLevel()
{
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())) {
// Blur level has changed or blur items need to be
// initially created.
updateBlurItemsAmount(blurLevel);
}
m_blurLevel = blurLevel;
}
void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel)
{
Q_Q(QQuickMultiEffect);
if (!m_shaderEffect)
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.
const auto engine = qmlEngine(q);
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->setParentItem(q);
blurEffect->setVisible(false);
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
static const auto 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 *>(dummyShaderSource) : (i == 0) ?
static_cast<QQuickItem *>(m_shaderSource) :
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 ? 0.8 * 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());
// Set the source rect
const qreal baseWidth = m_sourceItem ? m_sourceItem->width() : 1;
const qreal baseHeight = m_sourceItem ? m_sourceItem->height() : 1;
const qreal widthMultiplier = baseWidth / q->width();
const qreal heightMultiplier = baseHeight / q->height();
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();
Q_EMIT q->paddingRectChanged();
Q_EMIT q->itemRectChanged();
Q_EMIT q->itemSizeChanged();
}
QT_END_NAMESPACE
#include "moc_qquickmultieffect_p.cpp"