Document recursion limitations of the graphical effects.
[qt:qtgraphicaleffects.git] / src / effects / HueSaturation.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 HueSaturation
46     \inqmlmodule QtGraphicalEffects 1.0
47     \since QtGraphicalEffects 1.0
48     \inherits QtQuick2::Item
49     \ingroup qtgraphicaleffects-color
50     \brief Alters the source item colors in the HSL color space.
51
52     HueSaturation is similar to the \l{QtGraphicalEffects1::Colorize}{Colorize}
53     effect, but the hue and saturation property values are handled differently.
54     The HueSaturation effect always shifts the hue, saturation, and lightness
55     from the original, instead of setting them.
56
57     \table
58     \header
59         \li Source
60         \li Effect applied
61     \row
62         \li \image Original_bug.png
63         \li \image HueSaturation_bug.png
64     \endtable
65
66     \section1 Example
67
68     The following example shows how to apply the effect.
69     \snippet HueSaturation-example.qml example
70
71 */
72 Item {
73     id: rootItem
74
75     /*!
76         This property defines the source item that provides the source pixels
77         for the effect.
78
79         \note It is not supported to let the effect include itself, for
80         instance by setting source to the effect's parent.
81     */
82     property variant source: 0
83
84     /*!
85         This property defines the hue value which is added to the source hue
86         value.
87
88         The value ranges from -1.0 (decrease) to 1.0 (increase). By default, the
89         property is set to \c 0.0 (no change).
90
91         \table
92         \header
93         \li Output examples with different hue values
94         \li
95         \li
96         \row
97             \li \image HueSaturation_hue1.png
98             \li \image HueSaturation_hue2.png
99             \li \image HueSaturation_hue3.png
100         \row
101             \li \b { hue: -0.3 }
102             \li \b { hue: 0.0 }
103             \li \b { hue: 0.3 }
104         \row
105             \li \l saturation: 0
106             \li \l saturation: 0
107             \li \l saturation: 0
108         \row
109             \li \l lightness: 0
110             \li \l lightness: 0
111             \li \l lightness: 0
112         \endtable
113
114     */
115     property real hue: 0.0
116
117     /*!
118         This property defines the saturation value value which is added to the
119         source saturation value.
120
121         The value ranges from -1.0 (decrease) to 1.0 (increase). By default, the
122         property is set to \c 0.0 (no change).
123
124         \table
125         \header
126         \li Output examples with different saturation values
127         \li
128         \li
129         \row
130             \li \image HueSaturation_saturation1.png
131             \li \image HueSaturation_saturation2.png
132             \li \image HueSaturation_saturation3.png
133         \row
134             \li \b { saturation: -0.8 }
135             \li \b { saturation: 0.0 }
136             \li \b { saturation: 1.0 }
137         \row
138             \li \l hue: 0
139             \li \l hue: 0
140             \li \l hue: 0
141         \row
142             \li \l lightness: 0
143             \li \l lightness: 0
144             \li \l lightness: 0
145         \endtable
146
147     */
148     property real saturation: 0.0
149
150     /*!
151         This property defines the lightness value which is added to the source
152         saturation value.
153
154         The value ranges from -1.0 (decrease) to 1.0 (increase). By default, the
155         property is set to \c 0.0 (no change).
156
157         \table
158         \header
159         \li Output examples with different lightness values
160         \li
161         \li
162         \row
163             \li \image HueSaturation_lightness1.png
164             \li \image HueSaturation_lightness2.png
165             \li \image HueSaturation_lightness3.png
166         \row
167             \li \b { lightness: -0.5 }
168             \li \b { lightness: 0.0 }
169             \li \b { lightness: 0.5 }
170         \row
171             \li \l hue: 0
172             \li \l hue: 0
173             \li \l hue: 0
174         \row
175             \li \l saturation: 0
176             \li \l saturation: 0
177             \li \l saturation: 0
178         \endtable
179
180     */
181     property real lightness: 0.0
182
183     /*!
184         This property allows the effect output pixels to be cached in order to
185         improve the rendering performance.
186
187         Every time the source or effect properties are changed, the pixels in
188         the cache must be updated. Memory consumption is increased, because an
189         extra buffer of memory is required for storing the effect output.
190
191         It is recommended to disable the cache when the source or the effect
192         properties are animated.
193
194         By default, the property is set to \c false.
195     */
196     property bool cached: false
197
198     SourceProxy {
199         id: sourceProxy
200         input: rootItem.source
201     }
202
203     ShaderEffectSource {
204         id: cacheItem
205         anchors.fill: parent
206         visible: rootItem.cached
207         smooth: true
208         sourceItem: shaderItem
209         live: true
210         hideSource: visible
211     }
212
213     ShaderEffect {
214         id: shaderItem
215         property variant source: sourceProxy.output
216         property variant hsl: Qt.vector3d(rootItem.hue, rootItem.saturation, rootItem.lightness)
217
218         anchors.fill: parent
219
220         fragmentShader: "
221             varying highp vec2 qt_TexCoord0;
222             uniform highp float qt_Opacity;
223             uniform highp sampler2D source;
224             uniform highp vec3 hsl;
225
226             highp vec3 RGBtoHSL(highp vec3 color) {
227                 highp float cmin = min(color.r, min(color.g, color.b));
228                 highp float cmax = max(color.r, max(color.g, color.b));
229                 highp float h = 0.0;
230                 highp float s = 0.0;
231                 highp float l = (cmin + cmax) / 2.0;
232                 highp float diff = cmax - cmin;
233
234                 if (diff > 1.0 / 256.0) {
235                     if (l < 0.5)
236                         s = diff / (cmin + cmax);
237                     else
238                         s = diff / (2.0 - (cmin + cmax));
239
240                     if (color.r == cmax)
241                         h = (color.g - color.b) / diff;
242                     else if (color.g == cmax)
243                         h = 2.0 + (color.b - color.r) / diff;
244                     else
245                         h = 4.0 + (color.r - color.g) / diff;
246
247                     h /= 6.0;
248                 }
249                 return vec3(h, s, l);
250             }
251
252             highp float hueToIntensity(highp float v1, highp float v2, highp float h) {
253                 h = fract(h);
254                 if (h < 1.0 / 6.0)
255                     return v1 + (v2 - v1) * 6.0 * h;
256                 else if (h < 1.0 / 2.0)
257                     return v2;
258                 else if (h < 2.0 / 3.0)
259                     return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h);
260
261                 return v1;
262             }
263
264             highp vec3 HSLtoRGB(highp vec3 color) {
265                 highp float h = color.x;
266                 highp float l = color.z;
267                 highp float s = color.y;
268
269                 if (s < 1.0 / 256.0)
270                     return vec3(l);
271
272                 highp float v1;
273                 highp float v2;
274                 if (l < 0.5)
275                     v2 = l * (1.0 + s);
276                 else
277                     v2 = (l + s) - (s * l);
278
279                 v1 = 2.0 * l - v2;
280
281                 highp float d = 1.0 / 3.0;
282                 highp float r = hueToIntensity(v1, v2, h + d);
283                 highp float g = hueToIntensity(v1, v2, h);
284                 highp float b = hueToIntensity(v1, v2, h - d);
285                 return vec3(r, g, b);
286             }
287
288             void main() {
289                 lowp vec4 sample = texture2D(source, qt_TexCoord0);
290                 sample = vec4(sample.rgb / max(1.0/256.0, sample.a), sample.a);
291                 sample.rgb = mix(vec3(dot(sample.rgb, vec3(0.2125, 0.7154, 0.0721))), sample.rgb, 1.0 + hsl.y);
292                 sample.xyz = RGBtoHSL(sample.rgb);
293                 sample.rgb = HSLtoRGB(vec3(sample.x + hsl.x, sample.y, sample.z));
294                 highp float c = step(0.0, hsl.z);
295                 sample.rgb = mix(sample.rgb, vec3(c), abs(hsl.z));
296                 gl_FragColor = vec4(sample.rgb * sample.a, sample.a) * qt_Opacity;
297             }
298         "
299     }
300 }