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