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 RecursiveBlur
45    \inqmlmodule QtGraphicalEffects
46    \since QtGraphicalEffects 1.0
47    \inherits QtQuick2::Item
48    \ingroup qtgraphicaleffects-blur
49    \brief Blurs repeatedly, providing a strong blur effect.
50
51    The RecursiveBlur effect softens the image by blurring it with an algorithm
52    that uses a recursive feedback loop to blur the source multiple times. The
53    effect may give more blurry results than
54    \l{QtGraphicalEffects::GaussianBlur}{GaussianBlur} or
55    \l{QtGraphicalEffects::FastBlur}{FastBlur}, but the result is produced
56    asynchronously and takes more time.
57
58    \table
59    \header
60        \li Source
61        \li Effect applied
62    \row
63        \li \image Original_bug.png
64        \li \image RecursiveBlur_bug.png
65    \endtable
66
67    \note This effect is available when running with OpenGL.
68
69    \section1 Example
70
71    The following example shows how to apply the effect.
72    \snippet RecursiveBlur-example.qml example
73
74*/
75Item {
76    id: rootItem
77
78    /*!
79        This property defines the source item that is going to be blurred.
80
81        \note It is not supported to let the effect include itself, for
82        instance by setting source to the effect's parent.
83    */
84    property variant source
85
86    /*!
87        This property defines the distance of neighboring pixels which influence
88        the blurring of individual pixels. A larger radius provides better
89        quality, but is slower to render.
90
91        \b Note: The radius value in this effect is not intended to be changed
92        or animated frequently. The correct way to use it is to set the correct
93        value and keep it unchanged for the whole duration of the iterative blur
94        sequence.
95
96        The value ranges from (no blur) to 16.0 (maximum blur step). By default,
97        the property is set to \c 0.0 (no blur).
98
99        \table
100        \header
101        \li Output examples with different radius values
102        \li
103        \li
104        \row
105            \li \image RecursiveBlur_radius1.png
106            \li \image RecursiveBlur_radius2.png
107            \li \image RecursiveBlur_radius3.png
108        \row
109            \li \b { radius: 2.5 }
110            \li \b { radius: 4.5 }
111            \li \b { radius: 7.5 }
112        \row
113            \li \l loops: 20
114            \li \l loops: 20
115            \li \l loops: 20
116        \endtable
117
118    */
119    property real radius: 0.0
120
121    /*!
122        This property allows the effect output pixels to be cached in order to
123        improve the rendering performance.
124
125        Every time the source or effect properties are changed, the pixels in
126        the cache must be updated. Memory consumption is increased, because an
127        extra buffer of memory is required for storing the effect output.
128
129        It is recommended to disable the cache when the source or the effect
130        properties are animated.
131
132        By default, the property is set to \c false.
133
134    */
135    property bool cached: false
136
137    /*!
138        This property defines the blur behavior near the edges of the item,
139        where the pixel blurring is affected by the pixels outside the source
140        edges.
141
142        If the property is set to \c true, the pixels outside the source are
143        interpreted to be transparent, which is similar to OpenGL
144        clamp-to-border extension. The blur is expanded slightly outside the
145        effect item area.
146
147        If the property is set to \c false, the pixels outside the source are
148        interpreted to contain the same color as the pixels at the edge of the
149        item, which is similar to OpenGL clamp-to-edge behavior. The blur does
150        not expand outside the effect item area.
151
152        By default, the property is set to \c false.
153
154        \table
155        \header
156        \li Output examples with different transparentBorder values
157        \li
158        \li
159        \row
160            \li \image RecursiveBlur_transparentBorder1.png
161            \li \image RecursiveBlur_transparentBorder2.png
162        \row
163            \li \b { transparentBorder: false }
164            \li \b { transparentBorder: true }
165        \row
166            \li \l loops: 20
167            \li \l loops: 20
168        \row
169            \li \l radius: 7.5
170            \li \l radius: 7.5
171        \endtable
172    */
173    property bool transparentBorder: false
174
175    /*!
176        This property defines the amount of blur iterations that are going to be
177        performed for the source. When the property changes, the iterative
178        blurring process starts. If the value is decreased or if the value
179        changes from zero to non-zero, a snapshot is taken from the source. The
180        snapshot is used as a starting point for the process.
181
182        The iteration loop tries to run as fast as possible. The speed might be
183        limited by the VSYNC or the time needed for one blur step, or both.
184        Sometimes it may be desirable to perform the blurring with a slower
185        pace. In that case, it may be convenient to control the property with
186        Animation which increases the value.
187
188        The value ranges from 0 to inf. By default, the property is set to \c 0.
189
190        \table
191        \header
192        \li Output examples with different loops values
193        \li
194        \li
195        \row
196            \li \image RecursiveBlur_loops1.png
197            \li \image RecursiveBlur_loops2.png
198            \li \image RecursiveBlur_loops3.png
199        \row
200            \li \b { loops: 4 }
201            \li \b { loops: 20 }
202            \li \b { loops: 70 }
203        \row
204            \li \l radius: 7.5
205            \li \l radius: 7.5
206            \li \l radius: 7.5
207        \endtable
208
209    */
210    property int loops: 0
211
212    /*!
213        This property holds the progress of asynchronous source blurring
214        process, from 0.0 (nothing blurred) to 1.0 (finished).
215    */
216    property real progress: loops > 0.0 ? Math.min(1.0, recursionTimer.counter / loops) : 0.0
217
218    onLoopsChanged: recursiveSource.scheduleUpdate()
219    onSourceChanged: recursionTimer.reset()
220    onRadiusChanged: recursionTimer.reset()
221    onTransparentBorderChanged: recursionTimer.reset()
222
223    SourceProxy {
224        id: sourceProxy
225        input: rootItem.source
226        sourceRect: rootItem.transparentBorder ? Qt.rect(-1, -1, parent.width + 2, parent.height + 2) : Qt.rect(0, 0, 0, 0)
227    }
228
229    ShaderEffectSource {
230        id: cacheItem
231        anchors.fill: verticalBlur
232        smooth: true
233        visible: rootItem.cached
234        hideSource: visible
235        live: true
236        sourceItem: inputItem.visible ? inputItem : verticalBlur
237    }
238
239    Item {
240        id: recursionTimer
241        property int counter: 0
242
243        function reset() {
244            counter = 0
245            recursiveSource.scheduleUpdate()
246        }
247
248        function nextFrame() {
249            if (loops < counter)
250                recursionTimer.counter = 0
251
252            if (counter > 0)
253                recursiveSource.sourceItem = verticalBlur
254            else
255                recursiveSource.sourceItem = inputItem
256
257            if (counter < loops) {
258                recursiveSource.scheduleUpdate()
259                counter++
260            }
261        }
262    }
263
264    ShaderEffect {
265        id: inputItem
266        property variant source: sourceProxy.output
267        property real expandX: rootItem.transparentBorder ? (horizontalBlur.maximumRadius) / horizontalBlur.width : 0.0
268        property real expandY: rootItem.transparentBorder ? (horizontalBlur.maximumRadius) / horizontalBlur.height : 0.0
269
270        anchors.fill: verticalBlur
271        visible: !verticalBlur.visible
272
273        vertexShader: "qrc:/qt-project.org/imports/QtGraphicalEffects/shaders/recursiveblur.vert"
274
275        fragmentShader: "qrc:/qt-project.org/imports/QtGraphicalEffects/shaders/recursiveblur.frag"
276    }
277
278    ShaderEffectSource {
279        id: recursiveSource
280        visible: false
281        smooth: true
282        hideSource: false
283        live: false
284        sourceItem: inputItem
285        recursive: true
286        onSourceItemChanged: scheduleUpdate()
287        onScheduledUpdateCompleted: recursionTimer.nextFrame()
288    }
289
290    GaussianDirectionalBlur {
291        id: verticalBlur
292        x: rootItem.transparentBorder ? -horizontalBlur.maximumRadius - 1 : 0
293        y: rootItem.transparentBorder ? -horizontalBlur.maximumRadius - 1 : 0
294        width: horizontalBlur.width + 2
295        height: horizontalBlur.height + 2
296
297        horizontalStep: 0.0
298        verticalStep: 1.0 / parent.height
299
300        source: ShaderEffectSource {
301            sourceItem: horizontalBlur
302            hideSource: true
303            visible: false
304            smooth: true
305        }
306
307        deviation: (radius + 1) / 2.3333
308        radius: rootItem.radius
309        maximumRadius: Math.ceil(rootItem.radius)
310        transparentBorder: false
311        visible: loops > 0
312    }
313
314    GaussianDirectionalBlur {
315        id: horizontalBlur
316        width: rootItem.transparentBorder ? parent.width + 2 * maximumRadius + 2 : parent.width
317        height: rootItem.transparentBorder ? parent.height + 2 * maximumRadius + 2 : parent.height
318
319        horizontalStep: 1.0 / parent.width
320        verticalStep: 0.0
321
322        source: recursiveSource
323        deviation: (radius + 1) / 2.3333
324        radius: rootItem.radius
325        maximumRadius: Math.ceil(rootItem.radius)
326        transparentBorder: false
327        visible: false
328    }
329}
330