1/****************************************************************************
2**
3** Copyright (C) 2016 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:BSD$
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** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24**   * Redistributions of source code must retain the above copyright
25**     notice, this list of conditions and the following disclaimer.
26**   * Redistributions in binary form must reproduce the above copyright
27**     notice, this list of conditions and the following disclaimer in
28**     the documentation and/or other materials provided with the
29**     distribution.
30**   * Neither the name of The Qt Company Ltd nor the names of its
31**     contributors may be used to endorse or promote products derived
32**     from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51import QtQuick 2.0
52import "private"
53
54/*!
55    \qmltype RadialGradient
56    \inqmlmodule QtGraphicalEffects 1.0
57    \since QtGraphicalEffects 1.0
58    \inherits QtQuick2::Item
59    \ingroup qtgraphicaleffects-gradient
60    \brief Draws a radial gradient.
61
62    A gradient is defined by two or more colors, which are blended seamlessly.
63    The colors start from the middle of the item and end at the borders.
64
65    \table
66    \header
67        \li Effect applied
68    \row
69        \li \image RadialGradient.png
70    \endtable
71
72    \section1 Example
73
74    The following example shows how to apply the effect.
75    \snippet RadialGradient-example.qml example
76
77*/
78Item {
79    id: rootItem
80
81    /*!
82        This property allows the effect output pixels to be cached in order to
83        improve the rendering performance.
84
85        Every time the source or effect properties are changed, the pixels in
86        the cache must be updated. Memory consumption is increased, because an
87        extra buffer of memory is required for storing the effect output.
88
89        It is recommended to disable the cache when the source or the effect
90        properties are animated.
91
92        By default, the property is set to \c false.
93    */
94    property bool cached: false
95
96    /*!
97        The HorizontalOffset and verticalOffset properties define the offset in
98        pixels for the center point of the gradient compared to the item center.
99
100        The values range from -inf to inf. By default, these properties are set
101        to \c 0.
102
103        \table
104        \header
105        \li Output examples with different horizontalOffset values
106        \li
107        \li
108        \row
109            \li \image RadialGradient_horizontalOffset1.png
110            \li \image RadialGradient_horizontalOffset2.png
111            \li \image RadialGradient_horizontalOffset3.png
112        \row
113            \li \b { horizontalOffset: -150 }
114            \li \b { horizontalOffset: 0 }
115            \li \b { horizontalOffset: 150 }
116        \row
117            \li \l verticalOffset: 0
118            \li \l verticalOffset: 0
119            \li \l verticalOffset: 0
120        \row
121            \li \l horizontalRadius: 300
122            \li \l horizontalRadius: 300
123            \li \l horizontalRadius: 300
124        \row
125            \li \l verticalRadius: 300
126            \li \l verticalRadius: 300
127            \li \l verticalRadius: 300
128        \row
129            \li \l angle: 0
130            \li \l angle: 0
131            \li \l angle: 0
132        \endtable
133
134    */
135    property real horizontalOffset: 0.0
136    property real verticalOffset: 0.0
137
138    /*!
139        The HorizontalRadius and verticalRadius properties define the shape and
140        size of the radial gradient. If the radiuses are equal, the shape of the
141        gradient is a circle. If the horizontal and vertical radiuses differ,
142        the shape is elliptical. The radiuses are given in pixels.
143
144        The value ranges from -inf to inf. By default, horizontalRadius is bound
145        to width and verticalRadius is bound to height.
146
147        \table
148        \header
149        \li Output examples with different horizontalRadius values
150        \li
151        \li
152        \row
153            \li \image RadialGradient_horizontalRadius1.png
154            \li \image RadialGradient_horizontalRadius2.png
155        \row
156            \li \b { horizontalRadius: 300 }
157            \li \b { horizontalRadius: 100 }
158        \row
159            \li \l horizontalOffset: 0
160            \li \l horizontalOffset: 0
161        \row
162            \li \l verticalOffset: 0
163            \li \l verticalOffset: 0
164        \row
165            \li \l verticalRadius: 300
166            \li \l verticalRadius: 300
167        \row
168            \li \l angle: 0
169            \li \l angle: 0
170        \row
171            \li \l gradient: QQuickGradient(0xa05fb10)
172            \li \l gradient: QQuickGradient(0xa05fb10)
173        \endtable
174
175    */
176    property real horizontalRadius: width
177    property real verticalRadius: height
178
179    /*!
180        This property defines the rotation of the gradient around its center
181        point. The rotation is only visible when the
182        \l{RadialGradient::horizontalRadius}{horizontalRadius} and
183        \l{RadialGradient::verticalRadius}{verticalRadius} properties are not
184        equal. The angle is given in degrees and the default value is \c 0.
185
186        \table
187        \header
188        \li Output examples with different angle values
189        \li
190        \li
191        \row
192            \li \image RadialGradient_angle1.png
193            \li \image RadialGradient_angle2.png
194            \li \image RadialGradient_angle3.png
195        \row
196            \li \b { angle: 0 }
197            \li \b { angle: 45 }
198            \li \b { angle: 90 }
199        \row
200            \li \l horizontalOffset: 0
201            \li \l horizontalOffset: 0
202            \li \l horizontalOffset: 0
203        \row
204            \li \l verticalOffset: 0
205            \li \l verticalOffset: 0
206            \li \l verticalOffset: 0
207        \row
208            \li \l horizontalRadius: 100
209            \li \l horizontalRadius: 100
210            \li \l horizontalRadius: 100
211        \row
212            \li \l verticalRadius: 300
213            \li \l verticalRadius: 300
214            \li \l verticalRadius: 300
215        \endtable
216    */
217    property real angle: 0.0
218
219    /*!
220        This property defines the item that is going to be filled with gradient.
221        Source item gets rendered into an intermediate pixel buffer and the
222        alpha values from the result are used to determine the gradient's pixels
223        visibility in the display. The default value for source is undefined and
224        in that case whole effect area is filled with gradient.
225
226        \table
227        \header
228        \li Output examples with different source values
229        \li
230        \li
231        \row
232            \li \image RadialGradient_maskSource1.png
233            \li \image RadialGradient_maskSource2.png
234        \row
235            \li \b { source: undefined }
236            \li \b { source: Image { source: images/butterfly.png } }
237        \row
238            \li \l horizontalOffset: 0
239            \li \l horizontalOffset: 0
240        \row
241            \li \l verticalOffset: 0
242            \li \l verticalOffset: 0
243        \row
244            \li \l horizontalRadius: 300
245            \li \l horizontalRadius: 300
246        \row
247            \li \l verticalRadius: 300
248            \li \l verticalRadius: 300
249        \row
250            \li \l angle: 0
251            \li \l angle: 0
252        \endtable
253
254        \note It is not supported to let the effect include itself, for
255        instance by setting source to the effect's parent.
256    */
257    property variant source
258
259    /*!
260        A gradient is defined by two or more colors, which are blended
261        seamlessly. The colors are specified as a set of GradientStop child
262        items, each of which defines a position on the gradient from 0.0 to 1.0
263        and a color. The position of each GradientStop is defined by setting the
264        position property. The color is defined by setting the color property.
265
266        \table
267        \header
268        \li Output examples with different gradient values
269        \li
270        \li
271        \row
272            \li \image RadialGradient_gradient1.png
273            \li \image RadialGradient_gradient2.png
274            \li \image RadialGradient_gradient3.png
275        \row
276            \li \b {gradient:} \code
277    Gradient {
278      GradientStop { position: 0.000
279      color: Qt.rgba(1, 0, 0, 1) }
280      GradientStop { position: 0.167;
281      color: Qt.rgba(1, 1, 0, 1) }
282      GradientStop { position: 0.333;
283      color: Qt.rgba(0, 1, 0, 1) }
284      GradientStop { position: 0.500;
285      color: Qt.rgba(0, 1, 1, 1) }
286      GradientStop { position: 0.667;
287      color: Qt.rgba(0, 0, 1, 1) }
288      GradientStop { position: 0.833;
289      color: Qt.rgba(1, 0, 1, 1) }
290      GradientStop { position: 1.000;
291      color: Qt.rgba(1, 0, 0, 1) }
292    }
293        \endcode
294            \li \b {gradient:} \code
295    Gradient {
296      GradientStop { position: 0.0
297      color: "#F0F0F0"
298      }
299      GradientStop { position: 0.5
300      color: "#000000"
301      }
302      GradientStop { position: 1.0
303      color: "#F0F0F0"
304      }
305    }
306        \endcode
307            \li \b {gradient:}
308    \code
309    Gradient {
310      GradientStop { position: 0.0
311        color: "#00000000"
312      }
313      GradientStop { position: 1.0
314        color: "#FF000000"
315      }
316    }
317    \endcode
318        \row
319            \li \l horizontalOffset: 0
320            \li \l horizontalOffset: 0
321            \li \l horizontalOffset: 0
322        \row
323            \li \l verticalOffset: 0
324            \li \l verticalOffset: 0
325            \li \l verticalOffset: 0
326        \row
327            \li \l horizontalRadius: 300
328            \li \l horizontalRadius: 300
329            \li \l horizontalRadius: 300
330        \row
331            \li \l verticalRadius: 300
332            \li \l verticalRadius: 300
333            \li \l verticalRadius: 300
334        \row
335            \li \l angle: 0
336            \li \l angle: 0
337            \li \l angle: 0
338        \endtable
339    */
340    property Gradient gradient: Gradient {
341        GradientStop { position: 0.0; color: "white" }
342        GradientStop { position: 1.0; color: "black" }
343    }
344
345    SourceProxy {
346        id: maskSourceProxy
347        input: rootItem.source
348    }
349
350    ShaderEffectSource {
351        id: gradientSource
352        sourceItem: Rectangle {
353            width: 16
354            height: 256
355            gradient: rootItem.gradient
356            smooth: true
357        }
358        smooth: true
359        hideSource: true
360        visible: false
361    }
362
363   ShaderEffectSource {
364        id: cacheItem
365        anchors.fill: parent
366        visible: rootItem.cached
367        smooth: true
368        sourceItem: shaderItem
369        live: true
370        hideSource: visible
371    }
372
373    ShaderEffect {
374        id: shaderItem
375        property variant gradientImage: gradientSource
376        property variant maskSource: maskSourceProxy.output
377        property variant center: Qt.point(0.5 + rootItem.horizontalOffset / width, 0.5 + rootItem.verticalOffset / height)
378        property real horizontalRatio: rootItem.horizontalRadius > 0 ? width / (2 * rootItem.horizontalRadius) : width * 16384
379        property real verticalRatio: rootItem.verticalRadius > 0 ? height / (2 * rootItem.verticalRadius) : height * 16384
380        property real angle: -rootItem.angle / 360 * 2 * Math.PI
381        property variant matrixData: Qt.point(Math.sin(angle), Math.cos(angle))
382
383        anchors.fill: parent
384
385        vertexShader: "
386            attribute highp vec4 qt_Vertex;
387            attribute highp vec2 qt_MultiTexCoord0;
388            uniform highp mat4 qt_Matrix;
389            uniform highp vec2 matrixData;
390            uniform highp float horizontalRatio;
391            uniform highp float verticalRatio;
392            uniform highp vec2 center;
393            varying highp vec2 qt_TexCoord0;
394            varying highp vec2 qt_TexCoord1;
395            varying highp vec2 centerPoint;
396
397            void main() {
398                highp vec2 ratio = vec2(horizontalRatio, verticalRatio);
399
400                // Rotation matrix
401                highp mat2 rot = mat2(matrixData.y, -matrixData.x,
402                                      matrixData.x,  matrixData.y);
403
404                qt_TexCoord0 = qt_MultiTexCoord0;
405
406                qt_TexCoord1 = qt_MultiTexCoord0;
407                qt_TexCoord1 -= center;
408                qt_TexCoord1 *= rot;
409                qt_TexCoord1 += center;
410                qt_TexCoord1 *= ratio;
411
412                centerPoint = center * ratio;
413
414                gl_Position = qt_Matrix * qt_Vertex;
415            }
416        "
417
418        fragmentShader: maskSource == undefined ? noMaskShader : maskShader
419
420        onFragmentShaderChanged: horizontalRatioChanged()
421
422        property string maskShader: "
423            uniform lowp sampler2D gradientImage;
424            uniform lowp sampler2D maskSource;
425            uniform lowp float qt_Opacity;
426            varying highp vec2 qt_TexCoord0;
427            varying highp vec2 qt_TexCoord1;
428            varying highp vec2 centerPoint;
429
430            void main() {
431                lowp vec4 gradientColor = texture2D(gradientImage, vec2(0.0, 2.0 * distance(qt_TexCoord1, centerPoint)));
432                lowp float maskAlpha = texture2D(maskSource, qt_TexCoord0).a;
433                gl_FragColor = gradientColor * maskAlpha * qt_Opacity;
434            }
435        "
436
437        property string noMaskShader: "
438            uniform lowp sampler2D gradientImage;
439            uniform lowp float qt_Opacity;
440            varying highp vec2 qt_TexCoord1;
441            varying highp vec2 centerPoint;
442
443            void main() {
444                lowp vec4 gradientColor = texture2D(gradientImage, vec2(0.0, 2.0 * distance(qt_TexCoord1, centerPoint)));
445                gl_FragColor = gradientColor * qt_Opacity;
446            }
447        "
448    }
449}
450