1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Copyright (C) 2017 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the Qt Graphical Effects module.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41import QtQuick 2.12
42import QtQuick.Window 2.12
43import QtGraphicalEffects.private 1.12
44
45/*!
46    \qmltype GaussianBlur
47    \inqmlmodule QtGraphicalEffects
48    \since QtGraphicalEffects 1.0
49    \inherits QtQuick2::Item
50    \ingroup qtgraphicaleffects-blur
51    \brief Applies a higher quality blur effect.
52
53    GaussianBlur effect softens the image by blurring it with an algorithm that
54    uses the Gaussian function to calculate the effect. The effect produces
55    higher quality than \l{QtGraphicalEffects::FastBlur}{FastBlur}, but is
56    slower to render.
57
58    \table
59    \header
60        \li Source
61        \li Effect applied
62    \row
63        \li \image Original_bug.png
64        \li \image GaussianBlur_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 GaussianBlur-example.qml example
73
74    Performing blur live is a costly operation. Fullscreen gaussian blur
75    with even a moderate number of samples will only run at 60 fps on highend
76    graphics hardware.
77
78*/
79Item {
80    id: root
81
82    /*!
83        This property defines the source item that is going to be blurred.
84
85        \note It is not supported to let the effect include itself, for
86        instance by setting source to the effect's parent.
87    */
88    property variant source
89
90    /*!
91        This property defines the distance of the neighboring pixels which
92        affect the blurring of an individual pixel. A larger radius increases
93        the blur effect.
94
95        The ideal blur is achieved by selecting \c samples and \c radius such
96        that \c {samples = 1 + radius * 2}, such as:
97
98        \table
99        \header \li Radius             \li Samples
100        \row    \li 0 \e{(no blur)}    \li 1
101        \row    \li 1                  \li 3
102        \row    \li 2                  \li 5
103        \row    \li 3                  \li 7
104        \endtable
105
106        The value ranges from 0.0 (no blur) to inf. By default, the property is
107        set to \c floor(samples / 2.0).
108
109        \table
110        \header
111        \li Output examples with different radius values
112        \li
113        \li
114        \row
115            \li \image GaussianBlur_radius1.png
116            \li \image GaussianBlur_radius2.png
117            \li \image GaussianBlur_radius3.png
118        \row
119            \li \b { radius: 0 }
120            \li \b { radius: 4 }
121            \li \b { radius: 8 }
122        \row
123            \li \l samples: 16
124            \li \l samples: 16
125            \li \l samples: 16
126        \row
127            \li \l deviation: 3
128            \li \l deviation: 3
129            \li \l deviation: 3
130        \endtable
131
132    */
133    property real radius: Math.floor(samples / 2);
134
135    /*!
136        This property defines how many samples are taken per pixel when blur
137        calculation is done. Larger value produces better quality, but is slower
138        to render.
139
140        Ideally, this value should be twice as large as the highest required
141        radius value plus 1, for example, if the radius is animated between 0.0
142        and 4.0, samples should be set to 9.
143
144        By default, the property is set to \c 9.
145
146        \note This property is not intended to be animated. Changing this property may
147        cause the underlying OpenGL shaders to be recompiled.
148
149    */
150    property int samples: 9
151
152    /*!
153        This property is a parameter to the gaussian function that is used when
154        calculating neighboring pixel weights for the blurring. A larger
155        deviation causes image to appear more blurry, but it also reduces the
156        quality of the blur. A very large deviation value causes the effect to
157        look a bit similar to what, for exmple, a box blur algorithm produces. A
158        too small deviation values makes the effect insignificant for the pixels
159        near the radius.
160
161        \inlineimage GaussianBlur_deviation_graph.png
162        \caption The image above shows the Gaussian function with two different
163        deviation values, yellow (1) and cyan (2.7). The y-axis shows the
164        weights, the x-axis shows the pixel distance.
165
166        The value ranges from 0.0 (no deviation) to inf (maximum deviation). By
167        default, devaition is binded to radius. When radius increases, deviation
168        is automatically increased linearly. With the radius value of 8, the
169        deviation default value becomes approximately 2.7034. This value
170        produces a compromise between the blur quality and overall blurriness.
171
172        \table
173        \header
174        \li Output examples with different deviation values
175        \li
176        \li
177        \row
178            \li \image GaussianBlur_deviation1.png
179            \li \image GaussianBlur_deviation2.png
180            \li \image GaussianBlur_deviation3.png
181        \row
182            \li \b { deviation: 1 }
183            \li \b { deviation: 2 }
184            \li \b { deviation: 4 }
185        \row
186            \li \l radius: 8
187            \li \l radius: 8
188            \li \l radius: 8
189        \row
190            \li \l samples: 16
191            \li \l samples: 16
192            \li \l samples: 16
193        \endtable
194
195    */
196    property real deviation: (radius + 1) / 3.3333
197
198    /*!
199        This property defines the blur behavior near the edges of the item,
200        where the pixel blurring is affected by the pixels outside the source
201        edges.
202
203        If the property is set to \c true, the pixels outside the source are
204        interpreted to be transparent, which is similar to OpenGL
205        clamp-to-border extension. The blur is expanded slightly outside the
206        effect item area.
207
208        If the property is set to \c false, the pixels outside the source are
209        interpreted to contain the same color as the pixels at the edge of the
210        item, which is similar to OpenGL clamp-to-edge behavior. The blur does
211        not expand outside the effect item area.
212
213        By default, the property is set to \c false.
214
215        \table
216        \header
217        \li Output examples with different transparentBorder values
218        \li
219        \li
220        \row
221            \li \image GaussianBlur_transparentBorder1.png
222            \li \image GaussianBlur_transparentBorder2.png
223        \row
224            \li \b { transparentBorder: false }
225            \li \b { transparentBorder: true }
226        \row
227            \li \l radius: 8
228            \li \l radius: 8
229        \row
230            \li \l samples: 16
231            \li \l samples: 16
232        \row
233            \li \l deviation: 2.7
234            \li \l deviation: 2.7
235        \endtable
236    */
237    property bool transparentBorder: false
238
239    /*!
240        This property allows the effect output pixels to be cached in order to
241        improve the rendering performance.
242        Every time the source or effect properties are changed, the pixels in
243        the cache must be updated. Memory consumption is increased, because an
244        extra buffer of memory is required for storing the effect output.
245
246        It is recommended to disable the cache when the source or the effect
247        properties are animated.
248
249        By default, the property is set to \c false.
250
251    */
252    property bool cached: false
253
254
255    // private members...
256    /*! \internal */
257    property int _paddedTexWidth: transparentBorder ? width + 2 * radius: width;
258    /*! \internal */
259    property int _paddedTexHeight: transparentBorder ? height  + 2 * radius: height;
260    /*! \internal */
261    property int _kernelRadius: Math.max(0, samples / 2);
262    /*! \internal */
263    property int _kernelSize: _kernelRadius * 2 + 1;
264    /*! \internal */
265    property real _dpr: Screen.devicePixelRatio;
266    /*! \internal */
267    property bool _alphaOnly: false;
268    /*! \internal */
269    property var _maskSource: undefined
270
271    /*! \internal */
272    property alias _output: sourceProxy.output;
273    /*! \internal */
274    property alias _outputRect: sourceProxy.sourceRect;
275    /*! \internal */
276    property alias _color: verticalBlur.color;
277    /*! \internal */
278    property real _thickness: 0;
279
280    onSamplesChanged: _rebuildShaders();
281    on_KernelSizeChanged: _rebuildShaders();
282    onDeviationChanged: _rebuildShaders();
283    on_DprChanged: _rebuildShaders();
284    on_MaskSourceChanged: _rebuildShaders();
285    Component.onCompleted: _rebuildShaders();
286
287    /*! \internal */
288    function _rebuildShaders() {
289        var params = {
290            radius: _kernelRadius,
291            // Limit deviation to something very small avoid getting NaN in the shader.
292            deviation: Math.max(0.00001, deviation),
293            alphaOnly: root._alphaOnly,
294            masked: _maskSource != undefined,
295            fallback: root.radius != _kernelRadius
296        }
297        var shaders = ShaderBuilder.gaussianBlur(params);
298        horizontalBlur.fragmentShader = shaders.fragmentShader;
299        horizontalBlur.vertexShader = shaders.vertexShader;
300    }
301
302    SourceProxy {
303        id: sourceProxy
304        interpolation: SourceProxy.LinearInterpolation
305        input: root.source
306        sourceRect: root.transparentBorder
307                    ? Qt.rect(-root.radius, 0, root._paddedTexWidth, parent.height)
308                    : Qt.rect(0, 0, 0, 0)
309    }
310
311    ShaderEffect {
312        id: horizontalBlur
313        width: root.transparentBorder ? root._paddedTexWidth : root.width
314        height: root.height;
315
316        // Used by all shaders
317        property Item source: sourceProxy.output;
318        property real spread: root.radius / root._kernelRadius;
319        property var dirstep: Qt.vector2d(1 / (root._paddedTexWidth * root._dpr), 0);
320
321        // Used by fallback shader (sampleCount exceeds number of varyings)
322        property real deviation: root.deviation
323
324        // Only in use for DropShadow and Glow
325        property color color: "white"
326        property real thickness: Math.max(0, Math.min(0.98, 1 - root._thickness * 0.98));
327
328        // Only in use for MaskedBlur
329        property var mask: root._maskSource;
330
331        layer.enabled: true
332        layer.smooth: true
333        layer.sourceRect: root.transparentBorder
334                          ? Qt.rect(0, -root.radius, width, root._paddedTexHeight)
335                          : Qt.rect(0, 0, 0, 0)
336        visible: false
337        blending: false
338    }
339
340    ShaderEffect {
341        id: verticalBlur
342        x: transparentBorder ? -root.radius : 0
343        y: x;
344        width: root.transparentBorder ? root._paddedTexWidth: root.width
345        height: root.transparentBorder ? root._paddedTexHeight : root.height;
346        fragmentShader: horizontalBlur.fragmentShader
347        vertexShader: horizontalBlur.vertexShader
348
349        property Item source: horizontalBlur
350        property real spread: horizontalBlur.spread
351        property var dirstep: Qt.vector2d(0, 1 / (root._paddedTexHeight * root._dpr));
352
353        property real deviation: horizontalBlur.deviation
354
355        property color color: "black"
356        property real thickness: horizontalBlur.thickness;
357
358        property var mask: horizontalBlur.mask;
359
360        visible: true
361    }
362
363    ShaderEffectSource {
364        id: cacheItem
365        anchors.fill: verticalBlur
366        visible: root.cached
367        smooth: true
368        sourceItem: verticalBlur
369        hideSource: visible
370    }
371
372}
373