1// Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qgfxsourceproxy_p.h"
6
7#include <private/qquickshadereffectsource_p.h>
8#include <private/qquickitem_p.h>
9#include <private/qquickimage_p.h>
10
11QT_BEGIN_NAMESPACE
12
13QGfxSourceProxy::QGfxSourceProxy(QQuickItem *parentItem)
14 : QQuickItem(parentItem)
15{
16}
17
18QGfxSourceProxy::~QGfxSourceProxy()
19{
20 delete m_proxy;
21}
22
23void QGfxSourceProxy::setInput(QQuickItem *input)
24{
25 if (m_input == input)
26 return;
27
28 if (m_input)
29 disconnect(sender: m_input, signal: nullptr, receiver: this, member: nullptr);
30 m_input = input;
31 polish();
32 if (m_input) {
33 if (QQuickImage *image = qobject_cast<QQuickImage *>(object: m_input)) {
34 connect(sender: image, signal: &QQuickImage::sourceSizeChanged, context: this, slot: &QGfxSourceProxy::repolish);
35 connect(sender: image, signal: &QQuickImage::fillModeChanged, context: this, slot: &QGfxSourceProxy::repolish);
36 }
37 connect(sender: m_input, signal: &QQuickItem::childrenChanged, context: this, slot: &QGfxSourceProxy::repolish);
38 }
39 Q_EMIT inputChanged();
40}
41
42void QGfxSourceProxy::setOutput(QQuickItem *output)
43{
44 if (m_output == output)
45 return;
46 m_output = output;
47 Q_EMIT activeChanged();
48 Q_EMIT outputChanged();
49}
50
51void QGfxSourceProxy::setSourceRect(const QRectF &sourceRect)
52{
53 if (m_sourceRect == sourceRect)
54 return;
55 m_sourceRect = sourceRect;
56 polish();
57 Q_EMIT sourceRectChanged();
58}
59
60void QGfxSourceProxy::setInterpolation(Interpolation i)
61{
62 if (m_interpolation == i)
63 return;
64 m_interpolation = i;
65 polish();
66 Q_EMIT interpolationChanged();
67}
68
69void QGfxSourceProxy::useProxy()
70{
71 if (!m_proxy)
72 m_proxy = new QQuickShaderEffectSource(this);
73 m_proxy->setSourceRect(m_sourceRect);
74 m_proxy->setSourceItem(m_input);
75 m_proxy->setSmooth(m_interpolation != Interpolation::Nearest);
76 setOutput(m_proxy);
77}
78
79void QGfxSourceProxy::repolish()
80{
81 polish();
82}
83
84QObject *QGfxSourceProxy::findLayer(QQuickItem *item)
85{
86 if (!item)
87 return nullptr;
88 QQuickItemPrivate *d = QQuickItemPrivate::get(item);
89 if (d->extra.isAllocated() && d->extra->layer) {
90 QObject *layer = qvariant_cast<QObject *>(v: item->property(name: "layer"));
91 if (layer && layer->property(name: "enabled").toBool())
92 return layer;
93 }
94 return nullptr;
95}
96
97void QGfxSourceProxy::updatePolish()
98{
99 if (!m_input) {
100 setOutput(nullptr);
101 return;
102 }
103
104 QQuickImage *image = qobject_cast<QQuickImage *>(object: m_input);
105 QQuickShaderEffectSource *shaderSource = qobject_cast<QQuickShaderEffectSource *>(object: m_input);
106 bool childless = m_input->childItems().size() == 0;
107 bool interpOk = m_interpolation == Interpolation::Any
108 || (m_interpolation == Interpolation::Linear && m_input->smooth() == true)
109 || (m_interpolation == Interpolation::Nearest && m_input->smooth() == false);
110
111 // Layers can be used in two different ways. Option 1 is when the item is
112 // used as input to a separate ShaderEffect component. In this case,
113 // m_input will be the item itself.
114 QObject *layer = findLayer(item: m_input);
115 if (!layer && shaderSource) {
116 // Alternatively, the effect is applied via layer.effect, and the
117 // input to the effect will be the layer's internal ShaderEffectSource
118 // item. In this case, we need to backtrack and find the item that has
119 // the layer and configure it accordingly.
120 layer = findLayer(item: shaderSource->sourceItem());
121 }
122
123 // A bit crude test, but we're only using source rect for
124 // blurring+transparent edge, so this is good enough.
125 bool padded = m_sourceRect.x() < 0 || m_sourceRect.y() < 0;
126
127 bool direct = false;
128
129 if (layer) {
130 // Auto-configure the layer so interpolation and padding works as
131 // expected without allocating additional FBOs. In edgecases, where
132 // this feature is undesiered, the user can simply use
133 // ShaderEffectSource rather than layer.
134 layer->setProperty(name: "sourceRect", value: m_sourceRect);
135 layer->setProperty(name: "smooth", value: m_interpolation != Interpolation::Nearest);
136 direct = true;
137
138 } else if (childless && interpOk) {
139 if (shaderSource) {
140 if (shaderSource->sourceRect() == m_sourceRect || m_sourceRect.isEmpty())
141 direct = true;
142
143 } else if (!padded && ((image && image->fillMode() == QQuickImage::Stretch && !image->sourceSize().isNull())
144 || (!image && m_input->isTextureProvider())
145 )
146 ) {
147 direct = true;
148 }
149 }
150
151 if (direct)
152 setOutput(m_input);
153 else
154 useProxy();
155
156 // Remove the proxy if it is not in use..
157 if (m_proxy && m_output == m_input) {
158 delete m_proxy;
159 m_proxy = nullptr;
160 }
161}
162
163QT_END_NAMESPACE
164
165#include "moc_qgfxsourceproxy_p.cpp"
166

source code of qtdeclarative/src/effects/qgfxsourceproxy.cpp