Document recursion limitations of the graphical effects.
[qt:qtgraphicaleffects.git] / src / effects / RecursiveBlur.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Graphical Effects module.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 **     of its contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 import QtQuick 2.0
42 import "private"
43
44 /*!
45     \qmltype RecursiveBlur
46     \inqmlmodule QtGraphicalEffects 1.0
47     \since QtGraphicalEffects 1.0
48     \inherits QtQuick2::Item
49     \ingroup qtgraphicaleffects-blur
50     \brief Blurs repeatedly, providing a strong blur effect.
51
52     The RecursiveBlur effect softens the image by blurring it with an algorithm
53     that uses a recursive feedback loop to blur the source multiple times. The
54     effect may give more blurry results than
55     \l{QtGraphicalEffects1::GaussianBlur}{GaussianBlur} or
56     \l{QtGraphicalEffects1::FastBlur}{FastBlur}, but the result is produced
57     asynchronously and takes more time.
58
59     \table
60     \header
61         \li Source
62         \li Effect applied
63     \row
64         \li \image Original_bug.png
65         \li \image RecursiveBlur_bug.png
66     \endtable
67
68     \section1 Example
69
70     The following example shows how to apply the effect.
71     \snippet RecursiveBlur-example.qml example
72
73 */
74 Item {
75     id: rootItem
76
77     /*!
78         This property defines the source item that is going to be blurred.
79
80         \note It is not supported to let the effect include itself, for
81         instance by setting source to the effect's parent.
82     */
83     property variant source
84
85     /*!
86         This property defines the distance of neighboring pixels which influence
87         the blurring of individual pixels. A larger radius provides better
88         quality, but is slower to render.
89
90         \b Note: The radius value in this effect is not intended to be changed
91         or animated frequently. The correct way to use it is to set the correct
92         value and keep it unchanged for the whole duration of the iterative blur
93         sequence.
94
95         The value ranges from (no blur) to 16.0 (maximum blur step). By default,
96         the property is set to \c 0.0 (no blur).
97
98         \table
99         \header
100         \li Output examples with different radius values
101         \li
102         \li
103         \row
104             \li \image RecursiveBlur_radius1.png
105             \li \image RecursiveBlur_radius2.png
106             \li \image RecursiveBlur_radius3.png
107         \row
108             \li \b { radius: 2.5 }
109             \li \b { radius: 4.5 }
110             \li \b { radius: 7.5 }
111         \row
112             \li \l loops: 20
113             \li \l loops: 20
114             \li \l loops: 20
115         \endtable
116
117     */
118     property real radius: 0.0
119
120     /*!
121         This property allows the effect output pixels to be cached in order to
122         improve the rendering performance.
123
124         Every time the source or effect properties are changed, the pixels in
125         the cache must be updated. Memory consumption is increased, because an
126         extra buffer of memory is required for storing the effect output.
127
128         It is recommended to disable the cache when the source or the effect
129         properties are animated.
130
131         By default, the property is set to \c false.
132
133     */
134     property bool cached: false
135
136     /*!
137         This property defines the blur behavior near the edges of the item,
138         where the pixel blurring is affected by the pixels outside the source
139         edges.
140
141         If the property is set to \c true, the pixels outside the source are
142         interpreted to be transparent, which is similar to OpenGL
143         clamp-to-border extension. The blur is expanded slightly outside the
144         effect item area.
145
146         If the property is set to \c false, the pixels outside the source are
147         interpreted to contain the same color as the pixels at the edge of the
148         item, which is similar to OpenGL clamp-to-edge behavior. The blur does
149         not expand outside the effect item area.
150
151         By default, the property is set to \c false.
152
153         \table
154         \header
155         \li Output examples with different transparentBorder values
156         \li
157         \li
158         \row
159             \li \image RecursiveBlur_transparentBorder1.png
160             \li \image RecursiveBlur_transparentBorder2.png
161         \row
162             \li \b { transparentBorder: false }
163             \li \b { transparentBorder: true }
164         \row
165             \li \l loops: 20
166             \li \l loops: 20
167         \row
168             \li \l radius: 7.5
169             \li \l radius: 7.5
170         \endtable
171     */
172     property bool transparentBorder: false
173
174     /*!
175         This property defines the amount of blur iterations that are going to be
176         performed for the source. When the property changes, the iterative
177         blurring process starts. If the value is decreased or if the value
178         changes from zero to non-zero, a snapshot is taken from the source. The
179         snapshot is used as a starting point for the process.
180
181         The iteration loop tries to run as fast as possible. The speed might be
182         limited by the VSYNC or the time needed for one blur step, or both.
183         Sometimes it may be desirable to perform the blurring with a slower
184         pace. In that case, it may be convenient to control the property with
185         Animation which increases the value.
186
187         The value ranges from 0 to inf. By default, the property is set to \c 0.
188
189         \table
190         \header
191         \li Output examples with different loops values
192         \li
193         \li
194         \row
195             \li \image RecursiveBlur_loops1.png
196             \li \image RecursiveBlur_loops2.png
197             \li \image RecursiveBlur_loops3.png
198         \row
199             \li \b { loops: 4 }
200             \li \b { loops: 20 }
201             \li \b { loops: 70 }
202         \row
203             \li \l radius: 7.5
204             \li \l radius: 7.5
205             \li \l radius: 7.5
206         \endtable
207
208     */
209     property int loops: 0
210
211     /*!
212         This property holds the progress of asynchronous source blurring
213         process, from 0.0 (nothing blurred) to 1.0 (finished).
214     */
215     property real progress: loops > 0.0 ? Math.min(1.0, recursionTimer.counter / loops) : 0.0
216
217     onLoopsChanged: recursiveSource.scheduleUpdate()
218     onSourceChanged: recursionTimer.reset()
219     onRadiusChanged: recursionTimer.reset()
220     onTransparentBorderChanged: recursionTimer.reset()
221
222     SourceProxy {
223         id: sourceProxy
224         input: rootItem.source
225         sourceRect: rootItem.transparentBorder ? Qt.rect(-1, -1, parent.width + 2, parent.height + 2) : Qt.rect(0, 0, 0, 0)
226     }
227
228     ShaderEffectSource {
229         id: cacheItem
230         anchors.fill: verticalBlur
231         smooth: true
232         visible: rootItem.cached
233         hideSource: visible
234         live: true
235         sourceItem: inputItem.visible ? inputItem : verticalBlur
236     }
237
238     Item {
239         id: recursionTimer
240         property int counter: 0
241
242         function reset() {
243             counter = 0
244             recursiveSource.scheduleUpdate()
245         }
246
247         function nextFrame() {
248             if (loops < counter)
249                 recursionTimer.counter = 0
250
251             if (counter > 0)
252                 recursiveSource.sourceItem = verticalBlur
253             else
254                 recursiveSource.sourceItem = inputItem
255
256             if (counter < loops) {
257                 recursiveSource.scheduleUpdate()
258                 counter++
259             }
260         }
261     }
262
263     ShaderEffect {
264         id: inputItem
265         property variant source: sourceProxy.output
266         property real expandX: rootItem.transparentBorder ? (horizontalBlur.maximumRadius) / horizontalBlur.width : 0.0
267         property real expandY: rootItem.transparentBorder ? (horizontalBlur.maximumRadius) / horizontalBlur.height : 0.0
268
269         anchors.fill: verticalBlur
270         visible: !verticalBlur.visible
271
272         vertexShader: "
273             attribute highp vec4 qt_Vertex;
274             attribute highp vec2 qt_MultiTexCoord0;
275             uniform highp mat4 qt_Matrix;
276             uniform highp float expandX;
277             uniform highp float expandY;
278             varying highp vec2 qt_TexCoord0;
279
280             void main() {
281                 mediump vec2 texCoord = qt_MultiTexCoord0;
282                 texCoord.s = (texCoord.s - expandX) / (1.0 - 2.0 * expandX);
283                 texCoord.t = (texCoord.t - expandY) / (1.0 - 2.0 * expandY);
284                 qt_TexCoord0 = texCoord;
285                 gl_Position = qt_Matrix * qt_Vertex;
286             }
287         "
288
289         fragmentShader: "
290             varying mediump vec2 qt_TexCoord0;
291             uniform highp float qt_Opacity;
292             uniform lowp sampler2D source;
293             void main() {
294                 gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
295             }
296         "
297     }
298
299     ShaderEffectSource {
300         id: recursiveSource
301         visible: false
302         smooth: true
303         hideSource: false
304         live: false
305         sourceItem: inputItem
306         recursive: true
307         onSourceItemChanged: scheduleUpdate()
308         onScheduledUpdateCompleted: recursionTimer.nextFrame()
309     }
310
311     GaussianDirectionalBlur {
312         id: verticalBlur
313         x: rootItem.transparentBorder ? -horizontalBlur.maximumRadius - 1 : 0
314         y: rootItem.transparentBorder ? -horizontalBlur.maximumRadius - 1 : 0
315         width: horizontalBlur.width + 2
316         height: horizontalBlur.height + 2
317
318         horizontalStep: 0.0
319         verticalStep: 1.0 / parent.height
320
321         source: ShaderEffectSource {
322             sourceItem: horizontalBlur
323             hideSource: true
324             visible: false
325             smooth: true
326         }
327
328         deviation: (radius + 1) / 2.3333
329         radius: rootItem.radius
330         maximumRadius: Math.ceil(rootItem.radius)
331         transparentBorder: false
332         visible: loops > 0
333     }
334
335     GaussianDirectionalBlur {
336         id: horizontalBlur
337         width: rootItem.transparentBorder ? parent.width + 2 * maximumRadius + 2 : parent.width
338         height: rootItem.transparentBorder ? parent.height + 2 * maximumRadius + 2 : parent.height
339
340         horizontalStep: 1.0 / parent.width
341         verticalStep: 0.0
342
343         source: recursiveSource
344         deviation: (radius + 1) / 2.3333
345         radius: rootItem.radius
346         maximumRadius: Math.ceil(rootItem.radius)
347         transparentBorder: false
348         visible: false
349     }
350 }