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 ConicalGradient
56    \inqmlmodule QtGraphicalEffects 1.0
57    \since QtGraphicalEffects 1.0
58    \inherits QtQuick2::Item
59    \ingroup qtgraphicaleffects-gradient
60    \brief Draws a conical gradient.
61
62    A gradient is defined by two or more colors, which are blended seamlessly.
63    The colors start from the specified angle and end at 360 degrees larger
64    angle value.
65
66    \table
67    \header
68        \li Effect applied
69    \row
70        \li \image ConicalGradient.png
71    \endtable
72
73    \section1 Example
74
75    The following example shows how to apply the effect.
76    \snippet ConicalGradient-example.qml example
77
78*/
79Item {
80    id: rootItem
81
82    /*!
83        This property allows the effect output pixels to be cached in order to
84        improve the rendering performance.
85
86        Every time the source or effect properties are changed, the pixels in
87        the cache must be updated. Memory consumption is increased, because an
88        extra buffer of memory is required for storing the effect output.
89
90        It is recommended to disable the cache when the source or the effect
91        properties are animated.
92
93        By default, the property is set to \c false.
94
95    */
96    property bool cached: false
97
98    /*!
99        This property defines the starting angle where the color at the gradient
100        position of 0.0 is rendered. Colors at larger position values are
101        rendered into larger angle values and blended seamlessly. Angle values
102        increase clockwise.
103
104        \table
105        \header
106        \li Output examples with different angle values
107        \li
108        \li
109        \row
110            \li \image ConicalGradient_angle1.png
111            \li \image ConicalGradient_angle2.png
112            \li \image ConicalGradient_angle3.png
113        \row
114            \li \b { angle: 0 }
115            \li \b { angle: 45 }
116            \li \b { angle: 185 }
117        \row
118            \li \l horizontalOffset: 0
119            \li \l horizontalOffset: 0
120            \li \l horizontalOffset: 0
121        \row
122            \li \l verticalOffset: 0
123            \li \l verticalOffset: 0
124            \li \l verticalOffset: 0
125        \endtable
126
127    */
128    property real angle: 0.0
129
130    /*!
131    \qmlproperty real QtGraphicalEffects1::ConicalGradient::horizontalOffset
132    \qmlproperty real QtGraphicalEffects1::ConicalGradient::verticalOffset
133
134    The horizontalOffset and verticalOffset properties define the offset in
135    pixels for the center point of the gradient compared to the item center.
136
137    The value ranges from -inf to inf. By default, the properties are set to \c
138    0.
139
140    \table
141    \header
142    \li Output examples with different horizontalOffset values
143    \li
144    \li
145    \row
146        \li \image ConicalGradient_horizontalOffset1.png
147        \li \image ConicalGradient_horizontalOffset2.png
148        \li \image ConicalGradient_horizontalOffset3.png
149    \row
150        \li \b { horizontalOffset: -50 }
151        \li \b { horizontalOffset: 0 }
152        \li \b { horizontalOffset: 50 }
153    \row
154        \li \l angle: 0
155        \li \l angle: 0
156        \li \l angle: 0
157    \row
158        \li \l verticalOffset: 0
159        \li \l verticalOffset: 0
160        \li \l verticalOffset: 0
161    \endtable
162    */
163    property real horizontalOffset: 0.0
164    property real verticalOffset: 0.0
165
166    /*!
167        This property defines the item that is going to be filled with gradient.
168        Source item gets rendered into an intermediate pixel buffer and the
169        alpha values from the result are used to determine the gradient's pixels
170        visibility in the display. The default value for source is undefined and
171        in that case whole effect area is filled with gradient.
172
173        \table
174        \header
175        \li Output examples with different source values
176        \li
177        \row
178            \li \image ConicalGradient_maskSource1.png
179            \li \image ConicalGradient_maskSource2.png
180        \row
181            \li \b { source: undefined }
182            \li \b { source:  }
183        \row
184            \li \l angle: 0
185            \li \l angle: 0
186        \row
187            \li \l horizontalOffset: 0
188            \li \l horizontalOffset: 0
189        \row
190            \li \l verticalOffset: 0
191            \li \l verticalOffset: 0
192        \endtable
193
194
195        \note It is not supported to let the effect include itself, for
196        instance by setting source to the effect's parent.
197    */
198    property variant source
199
200/*!
201    A gradient is defined by two or more colors, which are blended seamlessly.
202    The colors are specified as a set of GradientStop child items, each of which
203    defines a position on the gradient (from 0.0 to 1.0), and a color.
204    The position of each GradientStop is defined by the position property.
205    The color is defined by the color property.
206
207    \table
208    \header
209    \li Output examples with different gradient values
210    \li
211    \li
212    \row
213        \li \image ConicalGradient_gradient1.png
214        \li \image ConicalGradient_gradient2.png
215        \li \image ConicalGradient_gradient3.png
216    \row
217        \li \b {gradient:} \code
218Gradient {
219  GradientStop { position: 0.000
220  color: Qt.rgba(1, 0, 0, 1) }
221  GradientStop { position: 0.167;
222  color: Qt.rgba(1, 1, 0, 1) }
223  GradientStop { position: 0.333;
224  color: Qt.rgba(0, 1, 0, 1) }
225  GradientStop { position: 0.500;
226  color: Qt.rgba(0, 1, 1, 1) }
227  GradientStop { position: 0.667;
228  color: Qt.rgba(0, 0, 1, 1) }
229  GradientStop { position: 0.833;
230  color: Qt.rgba(1, 0, 1, 1) }
231  GradientStop { position: 1.000;
232  color: Qt.rgba(1, 0, 0, 1) }
233}
234    \endcode
235        \li \b {gradient:} \code
236Gradient {
237  GradientStop { position: 0.0
238  color: "#F0F0F0"
239  }
240  GradientStop { position: 0.5
241  color: "#000000"
242  }
243  GradientStop { position: 1.0
244  color: "#F0F0F0"
245  }
246}
247    \endcode
248        \li \b {gradient:} \code
249Gradient {
250  GradientStop { position: 0.0
251    color: "#00000000"
252  }
253  GradientStop { position: 1.0
254    color: "#FF000000"
255  }
256}
257    \endcode
258    \row
259        \li \l angle: 0
260        \li \l angle: 0
261        \li \l angle: 0
262    \row
263        \li \l horizontalOffset: 0
264        \li \l horizontalOffset: 0
265        \li \l horizontalOffset: 0
266    \row
267        \li \l verticalOffset: 0
268        \li \l verticalOffset: 0
269        \li \l verticalOffset: 0
270    \endtable
271
272*/
273    property Gradient gradient: Gradient {
274        GradientStop { position: 0.0; color: "white" }
275        GradientStop { position: 1.0; color: "black" }
276    }
277
278    SourceProxy {
279        id: maskSourceProxy
280        input: rootItem.source
281    }
282
283    Rectangle {
284        id: gradientRect
285        width: 16
286        height: 256
287        gradient: rootItem.gradient
288        smooth: true
289   }
290
291    ShaderEffectSource {
292        id: cacheItem
293        anchors.fill: parent
294        visible: rootItem.cached
295        smooth: true
296        rotation: shaderItem.rotation
297        sourceItem: shaderItem
298        live: true
299        hideSource: visible
300    }
301
302    ShaderEffect {
303        id: shaderItem
304        property variant gradientSource: ShaderEffectSource {
305            sourceItem: gradientRect
306            smooth: true
307            hideSource: true
308            visible: false
309        }
310        property variant maskSource: maskSourceProxy.output
311        property real startAngle: (rootItem.angle - 90) * Math.PI/180
312        property variant center: Qt.point(0.5 + horizontalOffset / width, 0.5 + verticalOffset / height)
313
314        anchors.fill: parent
315
316        fragmentShader: maskSource == undefined ? noMaskShader : maskShader
317
318        onFragmentShaderChanged: startAngleChanged()
319
320        property string noMaskShader: "
321            varying mediump vec2 qt_TexCoord0;
322            uniform lowp sampler2D gradientSource;
323            uniform highp float qt_Opacity;
324            uniform highp float startAngle;
325            uniform highp vec2 center;
326
327            void main() {
328                const highp float PI = 3.14159265;
329                const highp float PIx2inv = 0.1591549;
330                highp float a = (atan((center.y - qt_TexCoord0.t), (center.x - qt_TexCoord0.s)) + PI - startAngle) * PIx2inv;
331                gl_FragColor = texture2D(gradientSource, vec2(0.0, fract(a))) * qt_Opacity;
332            }
333        "
334
335        property string maskShader: "
336            varying mediump vec2 qt_TexCoord0;
337            uniform lowp sampler2D gradientSource;
338            uniform lowp sampler2D maskSource;
339            uniform highp float qt_Opacity;
340            uniform highp float startAngle;
341            uniform highp vec2 center;
342
343            void main() {
344                lowp float maskAlpha = texture2D(maskSource, qt_TexCoord0).a;
345                const highp float PI = 3.14159265;
346                const highp float PIx2inv = 0.1591549;
347                highp float a = (atan((center.y - qt_TexCoord0.t), (center.x - qt_TexCoord0.s)) + PI - startAngle) * PIx2inv;
348                gl_FragColor = texture2D(gradientSource, vec2(0.0, fract(a))) * maskAlpha * qt_Opacity;
349            }
350        "
351    }
352}
353