1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the Qt Graphical Effects module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qgfxsourceproxy_p.h" |
41 | |
42 | #include <private/qquickshadereffectsource_p.h> |
43 | #include <private/qquickitem_p.h> |
44 | #include <private/qquickimage_p.h> |
45 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | QGfxSourceProxy::QGfxSourceProxy(QQuickItem *parentItem) |
49 | : QQuickItem(parentItem) |
50 | , m_input(0) |
51 | , m_output(0) |
52 | , m_proxy(0) |
53 | , m_interpolation(AnyInterpolation) |
54 | { |
55 | } |
56 | |
57 | QGfxSourceProxy::~QGfxSourceProxy() |
58 | { |
59 | delete m_proxy; |
60 | } |
61 | |
62 | void QGfxSourceProxy::setInput(QQuickItem *input) |
63 | { |
64 | if (m_input == input) |
65 | return; |
66 | |
67 | if (m_input != nullptr) |
68 | disconnect(sender: m_input, signal: nullptr, receiver: this, member: nullptr); |
69 | m_input = input; |
70 | polish(); |
71 | if (m_input != nullptr) { |
72 | if (QQuickImage *image = qobject_cast<QQuickImage *>(object: m_input)) { |
73 | connect(sender: image, signal: &QQuickImage::sourceSizeChanged, receiver: this, slot: &QGfxSourceProxy::repolish); |
74 | connect(sender: image, signal: &QQuickImage::fillModeChanged, receiver: this, slot: &QGfxSourceProxy::repolish); |
75 | } |
76 | connect(sender: m_input, signal: &QQuickItem::childrenChanged, receiver: this, slot: &QGfxSourceProxy::repolish); |
77 | } |
78 | emit inputChanged(); |
79 | } |
80 | |
81 | void QGfxSourceProxy::setOutput(QQuickItem *output) |
82 | { |
83 | if (m_output == output) |
84 | return; |
85 | m_output = output; |
86 | emit activeChanged(); |
87 | emit outputChanged(); |
88 | } |
89 | |
90 | void QGfxSourceProxy::setSourceRect(const QRectF &sourceRect) |
91 | { |
92 | if (m_sourceRect == sourceRect) |
93 | return; |
94 | m_sourceRect = sourceRect; |
95 | polish(); |
96 | emit sourceRectChanged(); |
97 | } |
98 | |
99 | void QGfxSourceProxy::setInterpolation(Interpolation i) |
100 | { |
101 | if (m_interpolation == i) |
102 | return; |
103 | m_interpolation = i; |
104 | polish(); |
105 | emit interpolationChanged(); |
106 | } |
107 | |
108 | |
109 | void QGfxSourceProxy::useProxy() |
110 | { |
111 | if (!m_proxy) |
112 | m_proxy = new QQuickShaderEffectSource(this); |
113 | m_proxy->setSourceRect(m_sourceRect); |
114 | m_proxy->setSourceItem(m_input); |
115 | m_proxy->setSmooth(m_interpolation != NearestInterpolation); |
116 | setOutput(m_proxy); |
117 | } |
118 | |
119 | void QGfxSourceProxy::repolish() |
120 | { |
121 | polish(); |
122 | } |
123 | |
124 | QObject *QGfxSourceProxy::findLayer(QQuickItem *item) |
125 | { |
126 | if (!item) |
127 | return 0; |
128 | QQuickItemPrivate *d = QQuickItemPrivate::get(item); |
129 | if (d->extra.isAllocated() && d->extra->layer) { |
130 | QObject *layer = qvariant_cast<QObject *>(v: item->property(name: "layer" )); |
131 | if (layer && layer->property(name: "enabled" ).toBool()) |
132 | return layer; |
133 | } |
134 | return 0; |
135 | } |
136 | |
137 | void QGfxSourceProxy::updatePolish() |
138 | { |
139 | if (m_input == 0) { |
140 | setOutput(0); |
141 | return; |
142 | } |
143 | |
144 | QQuickImage *image = qobject_cast<QQuickImage *>(object: m_input); |
145 | QQuickShaderEffectSource *shaderSource = qobject_cast<QQuickShaderEffectSource *>(object: m_input); |
146 | bool childless = m_input->childItems().size() == 0; |
147 | bool interpOk = m_interpolation == AnyInterpolation |
148 | || (m_interpolation == LinearInterpolation && m_input->smooth() == true) |
149 | || (m_interpolation == NearestInterpolation && m_input->smooth() == false); |
150 | |
151 | // Layers can be used in two different ways. Option 1 is when the item is |
152 | // used as input to a separate ShaderEffect component. In this case, |
153 | // m_input will be the item itself. |
154 | QObject *layer = findLayer(item: m_input); |
155 | if (!layer && shaderSource) { |
156 | // Alternatively, the effect is applied via layer.effect, and the |
157 | // input to the effect will be the layer's internal ShaderEffectSource |
158 | // item. In this case, we need to backtrack and find the item that has |
159 | // the layer and configure it accordingly. |
160 | layer = findLayer(item: shaderSource->sourceItem()); |
161 | } |
162 | |
163 | // A bit crude test, but we're only using source rect for |
164 | // blurring+transparent edge, so this is good enough. |
165 | bool padded = m_sourceRect.x() < 0 || m_sourceRect.y() < 0; |
166 | |
167 | bool direct = false; |
168 | |
169 | if (layer) { |
170 | // Auto-configure the layer so interpolation and padding works as |
171 | // expected without allocating additional FBOs. In edgecases, where |
172 | // this feature is undesiered, the user can simply use |
173 | // ShaderEffectSource rather than layer. |
174 | layer->setProperty(name: "sourceRect" , value: m_sourceRect); |
175 | layer->setProperty(name: "smooth" , value: m_interpolation != NearestInterpolation); |
176 | direct = true; |
177 | |
178 | } else if (childless && interpOk) { |
179 | |
180 | if (shaderSource) { |
181 | if (shaderSource->sourceRect() == m_sourceRect || m_sourceRect.isEmpty()) |
182 | direct = true; |
183 | |
184 | } else if (!padded && ((image && image->fillMode() == QQuickImage::Stretch && !image->sourceSize().isNull()) |
185 | || (!image && m_input->isTextureProvider()) |
186 | ) |
187 | ) { |
188 | direct = true; |
189 | } |
190 | } |
191 | |
192 | if (direct) { |
193 | setOutput(m_input); |
194 | } else { |
195 | useProxy(); |
196 | } |
197 | |
198 | // Remove the proxy if it is not in use.. |
199 | if (m_proxy && m_output == m_input) { |
200 | delete m_proxy; |
201 | m_proxy = 0; |
202 | } |
203 | } |
204 | |
205 | QT_END_NAMESPACE |
206 | |