diff options
Diffstat (limited to 'src/effects/qgfxsourceproxy.cpp')
-rw-r--r-- | src/effects/qgfxsourceproxy.cpp | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/effects/qgfxsourceproxy.cpp b/src/effects/qgfxsourceproxy.cpp new file mode 100644 index 0000000000..a05ed9e576 --- /dev/null +++ b/src/effects/qgfxsourceproxy.cpp @@ -0,0 +1,165 @@ +// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +// 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 "qgfxsourceproxy_p.h" + +#include <private/qquickshadereffectsource_p.h> +#include <private/qquickitem_p.h> +#include <private/qquickimage_p.h> + +QT_BEGIN_NAMESPACE + +QGfxSourceProxy::QGfxSourceProxy(QQuickItem *parentItem) + : QQuickItem(parentItem) +{ +} + +QGfxSourceProxy::~QGfxSourceProxy() +{ + delete m_proxy; +} + +void QGfxSourceProxy::setInput(QQuickItem *input) +{ + if (m_input == input) + return; + + if (m_input) + disconnect(m_input, nullptr, this, nullptr); + m_input = input; + polish(); + if (m_input) { + if (QQuickImage *image = qobject_cast<QQuickImage *>(m_input)) { + connect(image, &QQuickImage::sourceSizeChanged, this, &QGfxSourceProxy::repolish); + connect(image, &QQuickImage::fillModeChanged, this, &QGfxSourceProxy::repolish); + } + connect(m_input, &QQuickItem::childrenChanged, this, &QGfxSourceProxy::repolish); + } + Q_EMIT inputChanged(); +} + +void QGfxSourceProxy::setOutput(QQuickItem *output) +{ + if (m_output == output) + return; + m_output = output; + Q_EMIT activeChanged(); + Q_EMIT outputChanged(); +} + +void QGfxSourceProxy::setSourceRect(const QRectF &sourceRect) +{ + if (m_sourceRect == sourceRect) + return; + m_sourceRect = sourceRect; + polish(); + Q_EMIT sourceRectChanged(); +} + +void QGfxSourceProxy::setInterpolation(Interpolation i) +{ + if (m_interpolation == i) + return; + m_interpolation = i; + polish(); + Q_EMIT interpolationChanged(); +} + +void QGfxSourceProxy::useProxy() +{ + if (!m_proxy) + m_proxy = new QQuickShaderEffectSource(this); + m_proxy->setSourceRect(m_sourceRect); + m_proxy->setSourceItem(m_input); + m_proxy->setSmooth(m_interpolation != Interpolation::Nearest); + setOutput(m_proxy); +} + +void QGfxSourceProxy::repolish() +{ + polish(); +} + +QObject *QGfxSourceProxy::findLayer(QQuickItem *item) +{ + if (!item) + return nullptr; + QQuickItemPrivate *d = QQuickItemPrivate::get(item); + if (d->extra.isAllocated() && d->extra->layer) { + QObject *layer = qvariant_cast<QObject *>(item->property("layer")); + if (layer && layer->property("enabled").toBool()) + return layer; + } + return nullptr; +} + +void QGfxSourceProxy::updatePolish() +{ + if (!m_input) { + setOutput(nullptr); + return; + } + + QQuickImage *image = qobject_cast<QQuickImage *>(m_input); + QQuickShaderEffectSource *shaderSource = qobject_cast<QQuickShaderEffectSource *>(m_input); + bool childless = m_input->childItems().size() == 0; + bool interpOk = m_interpolation == Interpolation::Any + || (m_interpolation == Interpolation::Linear && m_input->smooth() == true) + || (m_interpolation == Interpolation::Nearest && m_input->smooth() == false); + + // Layers can be used in two different ways. Option 1 is when the item is + // used as input to a separate ShaderEffect component. In this case, + // m_input will be the item itself. + QObject *layer = findLayer(m_input); + if (!layer && shaderSource) { + // Alternatively, the effect is applied via layer.effect, and the + // input to the effect will be the layer's internal ShaderEffectSource + // item. In this case, we need to backtrack and find the item that has + // the layer and configure it accordingly. + layer = findLayer(shaderSource->sourceItem()); + } + + // A bit crude test, but we're only using source rect for + // blurring+transparent edge, so this is good enough. + bool padded = m_sourceRect.x() < 0 || m_sourceRect.y() < 0; + + bool direct = false; + + if (layer) { + // Auto-configure the layer so interpolation and padding works as + // expected without allocating additional FBOs. In edgecases, where + // this feature is undesiered, the user can simply use + // ShaderEffectSource rather than layer. + layer->setProperty("sourceRect", m_sourceRect); + layer->setProperty("smooth", m_interpolation != Interpolation::Nearest); + direct = true; + + } else if (childless && interpOk) { + if (shaderSource) { + if (shaderSource->sourceRect() == m_sourceRect || m_sourceRect.isEmpty()) + direct = true; + + } else if (!padded && ((image && image->fillMode() == QQuickImage::Stretch && !image->sourceSize().isNull()) + || (!image && m_input->isTextureProvider()) + ) + ) { + direct = true; + } + } + + if (direct) + setOutput(m_input); + else + useProxy(); + + // Remove the proxy if it is not in use.. + if (m_proxy && m_output == m_input) { + delete m_proxy; + m_proxy = nullptr; + } +} + +QT_END_NAMESPACE + +#include "moc_qgfxsourceproxy_p.cpp" |