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 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | QGfxSourceProxy::QGfxSourceProxy(QQuickItem *parentItem) |
14 | : QQuickItem(parentItem) |
15 | { |
16 | } |
17 | |
18 | QGfxSourceProxy::~QGfxSourceProxy() |
19 | { |
20 | delete m_proxy; |
21 | } |
22 | |
23 | void 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 | |
42 | void 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 | |
51 | void 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 | |
60 | void QGfxSourceProxy::setInterpolation(Interpolation i) |
61 | { |
62 | if (m_interpolation == i) |
63 | return; |
64 | m_interpolation = i; |
65 | polish(); |
66 | Q_EMIT interpolationChanged(); |
67 | } |
68 | |
69 | void 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 | |
79 | void QGfxSourceProxy::repolish() |
80 | { |
81 | polish(); |
82 | } |
83 | |
84 | QObject *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 | |
97 | void 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 | |
163 | QT_END_NAMESPACE |
164 | |
165 | #include "moc_qgfxsourceproxy_p.cpp" |
166 | |