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