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 DirectionalBlur
45    \inqmlmodule QtGraphicalEffects
46    \since QtGraphicalEffects 1.0
47    \inherits QtQuick2::Item
48    \ingroup qtgraphicaleffects-motion-blur
49    \brief Applies blur effect to the specified direction.
50
51    Effect creates perceived impression that the source item appears to be
52    moving in the direction of the blur. Blur is applied to both sides of
53    each pixel, therefore setting the direction to 0 and 180 provides the
54    same result.
55
56    Other available motionblur effects are \l{QtGraphicalEffects::ZoomBlur}{ZoomBlur} and
57    \l{QtGraphicalEffects::RadialBlur}{RadialBlur}.
58
59    \table
60    \header
61        \li Source
62        \li Effect applied
63    \row
64        \li \image Original_bug.png
65        \li \image DirectionalBlur_bug.png
66    \endtable
67
68    \note This effect is available when running with OpenGL.
69
70    \section1 Example
71
72    The following example shows how to apply the effect.
73    \snippet DirectionalBlur-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 perceived amount of movement for each pixel.
89        The movement is divided evenly to both sides of each pixel.
90
91        The quality of the blur depends on \l{DirectionalBlur::samples}{samples}
92        property. If length value is large, more samples are needed to keep the
93        visual quality at high level.
94
95        The value ranges from 0.0 to inf.
96        By default the property is set to \c 0.0 (no blur).
97
98        \table
99        \header
100        \li Output examples with different length values
101        \li
102        \li
103        \row
104            \li \image DirectionalBlur_length1.png
105            \li \image DirectionalBlur_length2.png
106            \li \image DirectionalBlur_length3.png
107        \row
108            \li \b { length: 0.0 }
109            \li \b { length: 32.0 }
110            \li \b { length: 48.0 }
111        \row
112            \li \l samples: 24
113            \li \l samples: 24
114            \li \l samples: 24
115        \row
116            \li \l angle: 0
117            \li \l angle: 0
118            \li \l angle: 0
119        \endtable
120
121    */
122    property real length: 0.0
123
124    /*!
125        This property defines how many samples are taken per pixel when blur
126        calculation is done. Larger value produces better quality, but is slower
127        to render.
128
129        This property is not intended to be animated. Changing this property may
130        cause the underlying OpenGL shaders to be recompiled.
131
132        Allowed values are between 0 and inf (practical maximum depends on GPU).
133        By default the property is set to \c 0 (no samples).
134
135    */
136    property int samples: 0
137
138    /*!
139        This property defines the direction for the blur. Blur is applied to
140        both sides of each pixel, therefore setting the direction to 0 and 180
141        produces the same result.
142
143        The value ranges from -180.0 to 180.0.
144        By default the property is set to \c 0.0.
145
146        \table
147        \header
148        \li Output examples with different angle values
149        \li
150        \li
151        \row
152            \li \image DirectionalBlur_angle1.png
153            \li \image DirectionalBlur_angle2.png
154            \li \image DirectionalBlur_angle3.png
155        \row
156            \li \b { angle: 0.0 }
157            \li \b { angle: 45.0 }
158            \li \b { angle: 90.0 }
159        \row
160            \li \l samples: 24
161            \li \l samples: 24
162            \li \l samples: 24
163        \row
164            \li \l length: 32
165            \li \l length: 32
166            \li \l length: 32
167        \endtable
168
169    */
170    property real angle: 0.0
171
172    /*!
173        This property defines the blur behavior near the edges of the item,
174        where the pixel blurring is affected by the pixels outside the source
175        edges.
176
177        If the property is set to \c true, the pixels outside the source are
178        interpreted to be transparent, which is similar to OpenGL
179        clamp-to-border extension. The blur is expanded slightly outside the
180        effect item area.
181
182        If the property is set to \c false, the pixels outside the source are
183        interpreted to contain the same color as the pixels at the edge of the
184        item, which is similar to OpenGL clamp-to-edge behavior. The blur does
185        not expand outside the effect item area.
186
187        By default, the property is set to \c false.
188
189    */
190    property bool transparentBorder: false
191
192    /*!
193        This property allows the effect output pixels to be cached in order to
194        improve the rendering performance.
195
196        Every time the source or effect properties are changed, the pixels in
197        the cache must be updated. Memory consumption is increased, because an
198        extra buffer of memory is required for storing the effect output.
199
200        It is recommended to disable the cache when the source or the effect
201        properties are animated.
202
203        By default, the property is set to \c false.
204
205    */
206    property bool cached: false
207
208    SourceProxy {
209        id: sourceProxy
210        input: rootItem.source
211        sourceRect: rootItem.transparentBorder ? Qt.rect(-1, -1, parent.width + 2.0, parent.height + 2.0) : Qt.rect(0, 0, 0, 0)
212    }
213
214    ShaderEffectSource {
215        id: cacheItem
216        anchors.fill: shaderItem
217        visible: rootItem.cached
218        smooth: true
219        sourceItem: shaderItem
220        live: true
221        hideSource: visible
222    }
223
224    ShaderEffect {
225        id: shaderItem
226        property variant source: sourceProxy.output
227        property real len: rootItem.length
228        property bool transparentBorder: rootItem.transparentBorder
229        property real samples: rootItem.samples
230        property real weight: 1.0 / Math.max(1.0, rootItem.samples)
231        property variant expandPixels: transparentBorder ? Qt.size(rootItem.samples, rootItem.samples) : Qt.size(0,0)
232        property variant expand: transparentBorder ? Qt.size(expandPixels.width / width, expandPixels.height / height) : Qt.size(0,0)
233        property variant delta: Qt.size(1.0 / rootItem.width * Math.cos((rootItem.angle + 90) * Math.PI/180), 1.0 / rootItem.height * Math.sin((rootItem.angle + 90) * Math.PI/180))
234
235        x: transparentBorder ? -expandPixels.width - 1: 0
236        y: transparentBorder ? -expandPixels.height - 1 : 0
237        width: transparentBorder ? parent.width + 2.0 * expandPixels.width + 2 : parent.width
238        height: transparentBorder ? parent.height + 2.0 * expandPixels.height + 2 : parent.height
239
240        property string fragmentShaderSkeleton: "
241            varying highp vec2 qt_TexCoord0;
242            uniform highp float qt_Opacity;
243            uniform lowp sampler2D source;
244            uniform highp float len;
245            uniform highp float samples;
246            uniform highp float weight;
247            uniform highp vec2 expand;
248            uniform highp vec2 delta;
249
250            void main(void) {
251                highp vec2 shift = delta * len / max(1.0, samples - 1.0);
252                mediump vec2 texCoord = qt_TexCoord0;
253                gl_FragColor = vec4(0.0);
254
255                PLACEHOLDER_EXPAND_STEPS
256
257                texCoord -= shift * max(0.0, samples - 1.0) * 0.5;
258
259                PLACEHOLDER_UNROLLED_LOOP
260
261                gl_FragColor *= weight * qt_Opacity;
262           }
263        "
264
265        function buildFragmentShader() {
266            var shader = ""
267            if (GraphicsInfo.profile === GraphicsInfo.OpenGLCoreProfile)
268                shader += "#version 150 core\n#define varying in\n#define texture2D texture\nout vec4 fragColor;\n#define gl_FragColor fragColor\n"
269            shader += fragmentShaderSkeleton
270            var expandSteps = ""
271
272            if (transparentBorder) {
273                expandSteps += "texCoord = (texCoord - expand) / (1.0 - 2.0 * expand);"
274            }
275
276            var unrolledLoop = "gl_FragColor += texture2D(source, texCoord);\n"
277
278            if (rootItem.samples > 1) {
279                unrolledLoop = ""
280                for (var i = 0; i < rootItem.samples; i++)
281                    unrolledLoop += "gl_FragColor += texture2D(source, texCoord); texCoord += shift;\n"
282            }
283
284            shader = shader.replace("PLACEHOLDER_EXPAND_STEPS", expandSteps)
285            fragmentShader = shader.replace("PLACEHOLDER_UNROLLED_LOOP", unrolledLoop)
286        }
287
288        onFragmentShaderChanged: sourceChanged()
289        onSamplesChanged: buildFragmentShader()
290        onTransparentBorderChanged: buildFragmentShader()
291        Component.onCompleted: buildFragmentShader()
292    }
293}
294