1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Graphical Effects module.
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
40import QtQuick 2.12
41import QtGraphicalEffects.private 1.12
42
43/*!
44    \qmltype FastBlur
45    \inqmlmodule QtGraphicalEffects
46    \since QtGraphicalEffects 1.0
47    \inherits QtQuick2::Item
48    \ingroup qtgraphicaleffects-blur
49    \brief Applies a fast blur effect to one or more source items.
50
51    FastBlur offers lower blur quality than
52    \l{QtGraphicalEffects::GaussianBlur}{GaussianBlur}, but it is faster to
53    render. The FastBlur effect softens the source content by blurring it with
54    algorithm which uses the source content downscaling and bilinear filtering.
55    Use this effect in situations where the source content is rapidly changing
56    and the highest possible blur quality is not
57    needed.
58
59    \table
60    \header
61        \li Source
62        \li Effect applied
63    \row
64        \li \image Original_bug.png
65        \li \image FastBlur_bug.png
66    \endtable
67
68    \note This effect is available when running with OpenGL.
69    s
70    \section1 Example
71
72    The following example shows how to apply the effect.
73    \snippet FastBlur-example.qml example
74
75*/
76Item {
77    id: rootItem
78
79    /*!
80        This property defines the source item that is going to be blurred.
81
82        \note It is not supported to let the effect include itself, for
83        instance by setting source to the effect's parent.
84    */
85    property variant source
86
87    /*!
88        This property defines the distance of the neighboring pixels which affect
89        the blurring of an individual pixel. A larger radius increases the blur
90        effect. FastBlur algorithm may internally reduce the accuracy of the radius in order to
91        provide good rendering performance.
92
93        The value ranges from 0.0 (no blur) to inf. Visual quality of the blur is reduced when
94        radius exceeds value 64. By default, the property is set to \c 0.0 (no blur).
95
96        \table
97        \header
98        \li Output examples with different blur values
99        \li
100        \li
101        \row
102            \li \image FastBlur_radius1.png
103            \li \image FastBlur_radius2.png
104            \li \image FastBlur_radius3.png
105        \row
106            \li \b { radius: 0 }
107            \li \b { radius: 32 }
108            \li \b { radius: 64 }
109        \endtable
110    */
111    property real radius: 0.0
112
113    /*!
114        This property defines the blur behavior near the edges of the item,
115        where the pixel blurring is affected by the pixels outside the source
116        edges.
117
118        If the property is set to \c true, the pixels outside the source are
119        interpreted to be transparent, which is similar to OpenGL
120        clamp-to-border extension. The blur is expanded slightly outside the
121        effect item area.
122
123        If the property is set to \c false, the pixels outside the source are
124        interpreted to contain the same color as the pixels at the edge of the
125        item, which is similar to OpenGL clamp-to-edge behavior. The blur does
126        not expand outside the effect item area.
127
128        By default, the property is set to \c false.
129
130        \table
131        \header
132        \li Output examples with different transparentBorder values
133        \li
134        \li
135        \row
136            \li \image FastBlur_transparentBorder1.png
137            \li \image FastBlur_transparentBorder2.png
138        \row
139            \li \b { transparentBorder: false }
140            \li \b { transparentBorder: true }
141        \row
142            \li \l radius: 64
143            \li \l radius: 64
144        \endtable
145    */
146    property bool transparentBorder: false
147
148    /*!
149        This property allows the effect output pixels to be cached in order to
150        improve the rendering performance.
151
152        Every time the source or effect properties are changed, the pixels in
153        the cache must be updated. Memory consumption is increased, because an
154        extra buffer of memory is required for storing the effect output.
155
156        It is recommended to disable the cache when the source or the effect
157        properties are animated.
158
159        By default, the property is set to \c false.
160
161    */
162    property bool cached: false
163
164    SourceProxy {
165        id: sourceProxy
166        input: rootItem.source
167    }
168
169    ShaderEffectSource {
170        id: cacheItem
171        anchors.fill: shaderItem
172        visible: rootItem.cached
173        sourceItem: shaderItem
174        live: true
175        hideSource: visible
176        smooth: rootItem.radius > 0
177    }
178
179    /*! \internal */
180    property string __internalBlurVertexShader: "qrc:/qt-project.org/imports/QtGraphicalEffects/shaders/fastblur_internal.vert"
181
182    /*! \internal */
183    property string __internalBlurFragmentShader: "qrc:/qt-project.org/imports/QtGraphicalEffects/shaders/fastblur_internal.frag"
184
185    ShaderEffect {
186        id: level0
187        property variant source: sourceProxy.output
188        anchors.fill: parent
189        visible: false
190        smooth: true
191    }
192
193    ShaderEffectSource {
194        id: level1
195        width: Math.ceil(shaderItem.width / 32) * 32
196        height: Math.ceil(shaderItem.height / 32) * 32
197        sourceItem: level0
198        hideSource: rootItem.visible
199        sourceRect: transparentBorder ? Qt.rect(-64, -64, shaderItem.width, shaderItem.height) : Qt.rect(0, 0, 0, 0)
200        visible: false
201        smooth: rootItem.radius > 0
202    }
203
204    ShaderEffect {
205        id: effect1
206        property variant source: level1
207        property real yStep: 1/height
208        property real xStep: 1/width
209        anchors.fill: level2
210        visible: false
211        smooth: true
212        vertexShader: __internalBlurVertexShader
213        fragmentShader: __internalBlurFragmentShader
214    }
215
216    ShaderEffectSource {
217        id: level2
218        width: level1.width / 2
219        height: level1.height / 2
220        sourceItem: effect1
221        hideSource: rootItem.visible
222        visible: false
223        smooth: true
224    }
225
226    ShaderEffect {
227        id: effect2
228        property variant source: level2
229        property real yStep: 1/height
230        property real xStep: 1/width
231        anchors.fill: level3
232        visible: false
233        smooth: true
234        vertexShader: __internalBlurVertexShader
235        fragmentShader: __internalBlurFragmentShader
236    }
237
238    ShaderEffectSource {
239        id: level3
240        width: level2.width / 2
241        height: level2.height / 2
242        sourceItem: effect2
243        hideSource: rootItem.visible
244        visible: false
245        smooth: true
246    }
247
248    ShaderEffect {
249        id: effect3
250        property variant source: level3
251        property real yStep: 1/height
252        property real xStep: 1/width
253        anchors.fill: level4
254        visible: false
255        smooth: true
256        vertexShader: __internalBlurVertexShader
257        fragmentShader: __internalBlurFragmentShader
258    }
259
260    ShaderEffectSource {
261        id: level4
262        width: level3.width / 2
263        height: level3.height / 2
264        sourceItem: effect3
265        hideSource: rootItem.visible
266        visible: false
267        smooth: true
268    }
269
270    ShaderEffect {
271        id: effect4
272        property variant source: level4
273        property real yStep: 1/height
274        property real xStep: 1/width
275        anchors.fill: level5
276        visible: false
277        smooth: true
278        vertexShader: __internalBlurVertexShader
279        fragmentShader: __internalBlurFragmentShader
280    }
281
282    ShaderEffectSource {
283        id: level5
284        width: level4.width / 2
285        height: level4.height / 2
286        sourceItem: effect4
287        hideSource: rootItem.visible
288        visible: false
289        smooth: true
290    }
291
292    ShaderEffect {
293        id: effect5
294        property variant source: level5
295        property real yStep: 1/height
296        property real xStep: 1/width
297        anchors.fill: level6
298        visible: false
299        smooth: true
300        vertexShader: __internalBlurVertexShader
301        fragmentShader: __internalBlurFragmentShader
302    }
303
304    ShaderEffectSource {
305        id: level6
306        width: level5.width / 2
307        height: level5.height / 2
308        sourceItem: effect5
309        hideSource: rootItem.visible
310        visible: false
311        smooth: true
312    }
313
314    Item {
315        id: dummysource
316        width: 1
317        height: 1
318        visible: false
319    }
320
321    ShaderEffectSource {
322        id: dummy
323        width: 1
324        height: 1
325        sourceItem: dummysource
326        visible: false
327        smooth: false
328        live: false
329    }
330
331    ShaderEffect {
332        id: shaderItem
333
334        property variant source1: level1
335        property variant source2: level2
336        property variant source3: level3
337        property variant source4: level4
338        property variant source5: level5
339        property variant source6: level6
340        property real lod: Math.sqrt(rootItem.radius / 64.0) * 1.2 - 0.2
341        property real weight1
342        property real weight2
343        property real weight3
344        property real weight4
345        property real weight5
346        property real weight6
347
348        x: transparentBorder ? -64 : 0
349        y: transparentBorder ? -64 : 0
350        width: transparentBorder ? parent.width + 128 : parent.width
351        height: transparentBorder ? parent.height + 128 : parent.height
352
353        function weight(v) {
354            if (v <= 0.0)
355                return 1.0
356            if (v >= 0.5)
357                return 0.0
358
359            return 1.0 - v * 2.0
360        }
361
362        function calculateWeights() {
363
364            var w1 = weight(Math.abs(lod - 0.100))
365            var w2 = weight(Math.abs(lod - 0.300))
366            var w3 = weight(Math.abs(lod - 0.500))
367            var w4 = weight(Math.abs(lod - 0.700))
368            var w5 = weight(Math.abs(lod - 0.900))
369            var w6 = weight(Math.abs(lod - 1.100))
370
371            var sum = w1 + w2 + w3 + w4 + w5 + w6;
372            weight1 = w1 / sum;
373            weight2 = w2 / sum;
374            weight3 = w3 / sum;
375            weight4 = w4 / sum;
376            weight5 = w5 / sum;
377            weight6 = w6 / sum;
378
379            upateSources()
380        }
381
382        function upateSources() {
383            var sources = new Array();
384            var weights = new Array();
385
386            if (weight1 > 0) {
387                sources.push(level1)
388                weights.push(weight1)
389            }
390
391            if (weight2 > 0) {
392                sources.push(level2)
393                weights.push(weight2)
394            }
395
396            if (weight3 > 0) {
397                sources.push(level3)
398                weights.push(weight3)
399            }
400
401            if (weight4 > 0) {
402                sources.push(level4)
403                weights.push(weight4)
404            }
405
406            if (weight5 > 0) {
407                sources.push(level5)
408                weights.push(weight5)
409            }
410
411            if (weight6 > 0) {
412                sources.push(level6)
413                weights.push(weight6)
414            }
415
416            for (var j = sources.length; j < 6; j++) {
417                sources.push(dummy)
418                weights.push(0.0)
419            }
420
421            source1 = sources[0]
422            source2 = sources[1]
423            source3 = sources[2]
424            source4 = sources[3]
425            source5 = sources[4]
426            source6 = sources[5]
427
428            weight1 = weights[0]
429            weight2 = weights[1]
430            weight3 = weights[2]
431            weight4 = weights[3]
432            weight5 = weights[4]
433            weight6 = weights[5]
434        }
435
436        Component.onCompleted: calculateWeights()
437
438        onLodChanged: calculateWeights()
439
440        fragmentShader: "qrc:/qt-project.org/imports/QtGraphicalEffects/shaders/fastblur.frag"
441    }
442}
443