1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qtypes.h" |
5 | #include <private/qquickmultieffect_p_p.h> |
6 | #include <private/qquickshadereffect_p.h> |
7 | #include <private/qquickshadereffectsource_p.h> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | Q_LOGGING_CATEGORY(lcQuickEffect, "qt.quick.effects" ) |
12 | |
13 | /*! |
14 | \keyword Qt Quick Effects |
15 | \qmlmodule QtQuick.Effects |
16 | \title Qt Quick Effects QML Types |
17 | \ingroup qmlmodules |
18 | \brief Provides QML types for applying one or more simple graphical effects to Qt Quick items. |
19 | |
20 | To use the types in this module, import the module with the following line: |
21 | |
22 | \qml |
23 | import QtQuick.Effects |
24 | \endqml |
25 | */ |
26 | |
27 | /*! |
28 | \qmltype MultiEffect |
29 | \instantiates QQuickMultiEffect |
30 | \inqmlmodule QtQuick.Effects |
31 | \inherits Item |
32 | \ingroup qtquick-effects |
33 | \brief Applies post-processing effect to an item. |
34 | |
35 | The MultiEffect type, the successor to the deprecated Qt Graphical Effects |
36 | from Qt 5, applies a post-processing effect to the \l source item. Compared |
37 | to the Qt Graphical Effects module, MultiEffect combines multiple |
38 | effects (blur, shadow, colorization etc.) into a single item and shader |
39 | which makes it better for multiple effects. There are several shader |
40 | variations and the most optimal one gets selected based on the features |
41 | used. |
42 | |
43 | MultiEffect is designed specifically for most common effects and can be easily animated. |
44 | If the MultiEffect doesn't contain the effect you need, consider implementing a custom |
45 | effect using \l {Qt Quick Effect Maker}. For more information about shader effects, |
46 | see the \l ShaderEffect reference documentation. |
47 | |
48 | \section1 Example Usage |
49 | |
50 | The following simple example shows how to apply a saturation effect on an item: |
51 | |
52 | \table 70% |
53 | \row |
54 | \li \image multieffect-example1.png |
55 | \li \qml |
56 | import QtQuick |
57 | import QtQuick.Effects |
58 | |
59 | ... |
60 | Image { |
61 | id: sourceItem |
62 | source: "qt_logo_green_rgb.png" |
63 | visible: false |
64 | } |
65 | MultiEffect { |
66 | source: sourceItem |
67 | anchors.fill: sourceItem |
68 | saturation: -1.0 |
69 | } |
70 | \endqml |
71 | \endtable |
72 | |
73 | Here is a bit more complex example, applying multiple effects at the same time: |
74 | |
75 | \table 70% |
76 | \row |
77 | \li \image multieffect-example2.png |
78 | \li \qml |
79 | import QtQuick |
80 | import QtQuick.Effects |
81 | |
82 | ... |
83 | MultiEffect { |
84 | source: sourceItem |
85 | anchors.fill: sourceItem |
86 | brightness: 0.4 |
87 | saturation: 0.2 |
88 | blurEnabled: true |
89 | blurMax: 64 |
90 | blur: 1.0 |
91 | } |
92 | \endqml |
93 | \endtable |
94 | |
95 | Below is an example of how to use the mask, colorization and brightness effects together to |
96 | fade away an element. This kind of hiding/showing of elements can be, for example, bind |
97 | to a slider value or animations like NumberAnimation. Note how the \c visible property is |
98 | false when the item is totally faded away, to avoid unnecessary rendering of the effect. |
99 | |
100 | \table 70% |
101 | \row |
102 | \li \image multieffect-example3.png |
103 | \li \qml |
104 | import QtQuick |
105 | import QtQuick.Effects |
106 | import QtQuick.Controls.Material |
107 | |
108 | ... |
109 | MultiEffect { |
110 | property real effectAmount: effectSlider.value |
111 | source: sourceItem |
112 | anchors.fill: sourceItem |
113 | brightness: effectAmount |
114 | colorizationColor: "#ff20d0" |
115 | colorization: effectAmount |
116 | maskEnabled: true |
117 | maskSource: Image { |
118 | source: "mask.png" |
119 | } |
120 | maskSpreadAtMin: 0.2 |
121 | maskThresholdMin: effectAmount |
122 | visible: effectAmount < 1.0 |
123 | } |
124 | Slider { |
125 | id: effectSlider |
126 | anchors.bottom: parent.bottom |
127 | anchors.horizontalCenter: parent.horizontalCenter |
128 | } |
129 | \endqml |
130 | \endtable |
131 | |
132 | \section1 Performance |
133 | There are a few things to consider for optimal performance: |
134 | \list |
135 | \li To get the most optimal shader, enable only the effects which you actually use |
136 | (see \l blurEnabled, \l shadowEnabled, \l maskEnabled). Simple color effects (\l brightness, |
137 | \l contrast, \l saturation, \l colorization) are always enabled, so using them doesn't |
138 | add extra overhead. |
139 | \li See the \b {Performance notes} of the properties which may change the shader or the effect |
140 | item size and don't modify these during animations. |
141 | \li When the MultiEffect isn't used, remember to set its \c visible property to be false to |
142 | avoid rendering the effects in the background. |
143 | \li Blur and shadow are the heaviest effects. With these, prefer increasing \l blurMultiplier |
144 | over \l blurMax and avoid using \l source items which animate, so blurring doesn't need |
145 | to be regenerated in every frame. |
146 | \li Apply effects to optimally sized QML elements as more pixels means more work for |
147 | the GPU. When applying the blur effect to the whole background, remember to set |
148 | \l autoPaddingEnabled false or the effect grows "outside" the window / screen. |
149 | \endlist |
150 | */ |
151 | |
152 | /*! |
153 | \qmlsignal QtQuick.Effects::MultiEffect::shaderChanged() |
154 | |
155 | This signal is emitted when the used shader changes. |
156 | \sa fragmentShader, vertexShader |
157 | */ |
158 | |
159 | QQuickMultiEffect::QQuickMultiEffect(QQuickItem *parent) |
160 | : QQuickItem(*new QQuickMultiEffectPrivate, parent) |
161 | { |
162 | setFlag(flag: ItemHasContents); |
163 | } |
164 | |
165 | QQuickMultiEffect::~QQuickMultiEffect() |
166 | { |
167 | } |
168 | |
169 | /*! |
170 | \qmlproperty Item QtQuick.Effects::MultiEffect::source |
171 | |
172 | This property holds the item to be used as a source for the effect. |
173 | If needed, MultiEffect will internally generate a \l ShaderEffectSource |
174 | as the texture source. |
175 | |
176 | \note It is not supported to let the effect include itself, for instance |
177 | by setting source to the effect's parent. |
178 | |
179 | \note If the source item has \l {QtQuick::Item::layer.enabled} {layer.enabled} set to true, |
180 | it will be used directly. This is good for the performance and often desired, when the source |
181 | is hidden. But if the source remains visible and the effect adds padding (autoPaddingEnabled, |
182 | paddingRect), that padding can affect the appearance of the source item. |
183 | |
184 | \sa hasProxySource |
185 | */ |
186 | QQuickItem *QQuickMultiEffect::source() const |
187 | { |
188 | Q_D(const QQuickMultiEffect); |
189 | return d->source(); |
190 | } |
191 | |
192 | void QQuickMultiEffect::setSource(QQuickItem *item) |
193 | { |
194 | Q_D(QQuickMultiEffect); |
195 | d->setSource(item); |
196 | } |
197 | |
198 | /*! |
199 | \qmlproperty bool QtQuick.Effects::MultiEffect::autoPaddingEnabled |
200 | |
201 | When blur or shadow effects are enabled and this is set to true (default), |
202 | the item size is padded automatically based on blurMax and blurMultiplier. |
203 | Note that \l paddingRect is always added to the size. |
204 | |
205 | \image multieffect-example4.png |
206 | |
207 | \sa paddingRect |
208 | |
209 | \include notes.qdocinc performance item size |
210 | |
211 | \include notes.qdocinc performance item resize |
212 | */ |
213 | bool QQuickMultiEffect::autoPaddingEnabled() const |
214 | { |
215 | Q_D(const QQuickMultiEffect); |
216 | return d->autoPaddingEnabled(); |
217 | } |
218 | |
219 | void QQuickMultiEffect::setAutoPaddingEnabled(bool enabled) |
220 | { |
221 | Q_D(QQuickMultiEffect); |
222 | d->setAutoPaddingEnabled(enabled); |
223 | } |
224 | |
225 | /*! |
226 | \qmlproperty rect QtQuick.Effects::MultiEffect::paddingRect |
227 | |
228 | Set this to increase item size manually so that blur and/or shadows will fit. |
229 | If autoPaddingEnabled is true and paddingRect is not set, the item is padded |
230 | to fit maximally blurred item based on blurMax and blurMultiplier. When |
231 | enabling the shadow, you typically need to take \l shadowHorizontalOffset and |
232 | \l shadowVerticalOffset into account and adjust this paddingRect accordingly. |
233 | |
234 | Below is an example of adjusting paddingRect with autoPaddingEnabled set to |
235 | false so that the shadow fits inside the MultiEffect item. |
236 | |
237 | \image multieffect-example5.png |
238 | |
239 | \sa autoPaddingEnabled |
240 | |
241 | \include notes.qdocinc performance item size |
242 | |
243 | \include notes.qdocinc performance item resize |
244 | */ |
245 | QRectF QQuickMultiEffect::paddingRect() const |
246 | { |
247 | Q_D(const QQuickMultiEffect); |
248 | return d->paddingRect(); |
249 | } |
250 | |
251 | void QQuickMultiEffect::setPaddingRect(const QRectF &rect) |
252 | { |
253 | Q_D(QQuickMultiEffect); |
254 | d->setPaddingRect(rect); |
255 | } |
256 | |
257 | /*! |
258 | \qmlproperty real QtQuick.Effects::MultiEffect::brightness |
259 | |
260 | This property defines how much the source brightness is increased or |
261 | decreased. |
262 | |
263 | The value ranges from -1.0 to 1.0. By default, the property is set to \c |
264 | 0.0 (no change). |
265 | */ |
266 | qreal QQuickMultiEffect::brightness() const |
267 | { |
268 | Q_D(const QQuickMultiEffect); |
269 | return d->brightness(); |
270 | } |
271 | |
272 | void QQuickMultiEffect::setBrightness(qreal brightness) |
273 | { |
274 | Q_D(QQuickMultiEffect); |
275 | d->setBrightness(brightness); |
276 | } |
277 | |
278 | /*! |
279 | \qmlproperty real QtQuick.Effects::MultiEffect::contrast |
280 | |
281 | This property defines how much the source contrast is increased or |
282 | decreased. |
283 | |
284 | The value ranges from -1.0 to 1.0. By default, the property is set to \c |
285 | 0.0 (no change). |
286 | */ |
287 | qreal QQuickMultiEffect::contrast() const |
288 | { |
289 | Q_D(const QQuickMultiEffect); |
290 | return d->contrast(); |
291 | } |
292 | |
293 | void QQuickMultiEffect::setContrast(qreal contrast) |
294 | { |
295 | Q_D(QQuickMultiEffect); |
296 | d->setContrast(contrast); |
297 | } |
298 | |
299 | /*! |
300 | \qmlproperty real QtQuick.Effects::MultiEffect::saturation |
301 | |
302 | This property defines how much the source saturation is increased or |
303 | decreased. |
304 | |
305 | The value ranges from -1.0 (totally desaturated) to inf. By default, |
306 | the property is set to \c 0.0 (no change). |
307 | */ |
308 | qreal QQuickMultiEffect::saturation() const |
309 | { |
310 | Q_D(const QQuickMultiEffect); |
311 | return d->saturation(); |
312 | } |
313 | |
314 | void QQuickMultiEffect::setSaturation(qreal saturation) |
315 | { |
316 | Q_D(QQuickMultiEffect); |
317 | d->setSaturation(saturation); |
318 | } |
319 | |
320 | /*! |
321 | \qmlproperty real QtQuick.Effects::MultiEffect::colorization |
322 | |
323 | This property defines how much the source is colorized with the |
324 | colorizationColor. |
325 | |
326 | The value ranges from 0.0 (not colorized) to 1.0 (fully colorized). |
327 | By default, the property is set to \c 0.0 (no change). |
328 | */ |
329 | qreal QQuickMultiEffect::colorization() const |
330 | { |
331 | Q_D(const QQuickMultiEffect); |
332 | return d->colorization(); |
333 | } |
334 | |
335 | void QQuickMultiEffect::setColorization(qreal colorization) |
336 | { |
337 | Q_D(QQuickMultiEffect); |
338 | d->setColorization(colorization); |
339 | } |
340 | |
341 | /*! |
342 | \qmlproperty color QtQuick.Effects::MultiEffect::colorizationColor |
343 | |
344 | This property defines the RGBA color value which is used to |
345 | colorize the source. |
346 | |
347 | By default, the property is set to \c {Qt.rgba(1.0, 0.0, 0.0, 1.0)} (red). |
348 | |
349 | \sa colorization |
350 | */ |
351 | QColor QQuickMultiEffect::colorizationColor() const |
352 | { |
353 | Q_D(const QQuickMultiEffect); |
354 | return d->colorizationColor(); |
355 | } |
356 | |
357 | void QQuickMultiEffect::setColorizationColor(const QColor &color) |
358 | { |
359 | Q_D(QQuickMultiEffect); |
360 | d->setColorizationColor(color); |
361 | } |
362 | |
363 | /*! |
364 | \qmlproperty bool QtQuick.Effects::MultiEffect::blurEnabled |
365 | |
366 | Enables the blur effect. |
367 | |
368 | \include notes.qdocinc performance shader regen |
369 | */ |
370 | bool QQuickMultiEffect::blurEnabled() const |
371 | { |
372 | Q_D(const QQuickMultiEffect); |
373 | return d->blurEnabled(); |
374 | } |
375 | |
376 | void QQuickMultiEffect::setBlurEnabled(bool enabled) |
377 | { |
378 | Q_D(QQuickMultiEffect); |
379 | d->setBlurEnabled(enabled); |
380 | } |
381 | |
382 | /*! |
383 | \qmlproperty real QtQuick.Effects::MultiEffect::blur |
384 | |
385 | This property defines how much blur (radius) is applied to the source. |
386 | |
387 | The value ranges from 0.0 (no blur) to 1.0 (full blur). By default, |
388 | the property is set to \c 0.0 (no change). The amount of full blur |
389 | is affected by blurMax and blurMultiplier. |
390 | |
391 | \b {Performance note:} If you don't need to go close to 1.0 at any point |
392 | of blur animations, consider reducing blurMax or blurMultiplier for |
393 | optimal performance. |
394 | */ |
395 | qreal QQuickMultiEffect::blur() const |
396 | { |
397 | Q_D(const QQuickMultiEffect); |
398 | return d->blur(); |
399 | } |
400 | |
401 | void QQuickMultiEffect::setBlur(qreal blur) |
402 | { |
403 | Q_D(QQuickMultiEffect); |
404 | d->setBlur(blur); |
405 | } |
406 | |
407 | /*! |
408 | \qmlproperty int QtQuick.Effects::MultiEffect::blurMax |
409 | |
410 | This property defines the maximum pixel radius that blur with value |
411 | 1.0 will reach. |
412 | |
413 | Meaningful range of this value is from 2 (subtle blur) to 64 (high |
414 | blur). By default, the property is set to \c 32. For the most optimal |
415 | performance, select as small value as you need. |
416 | |
417 | \note This affects to both blur and shadow effects. |
418 | |
419 | \include notes.qdocinc performance shader regen |
420 | |
421 | \include notes.qdocinc performance item resize |
422 | */ |
423 | int QQuickMultiEffect::blurMax() const |
424 | { |
425 | Q_D(const QQuickMultiEffect); |
426 | return d->blurMax(); |
427 | } |
428 | |
429 | void QQuickMultiEffect::setBlurMax(int blurMax) |
430 | { |
431 | Q_D(QQuickMultiEffect); |
432 | d->setBlurMax(blurMax); |
433 | } |
434 | |
435 | /*! |
436 | \qmlproperty real QtQuick.Effects::MultiEffect::blurMultiplier |
437 | |
438 | This property defines a multiplier for extending the blur radius. |
439 | |
440 | The value ranges from 0.0 (not multiplied) to inf. By default, |
441 | the property is set to \c 0.0. Incresing the multiplier extends the |
442 | blur radius, but decreases the blur quality. This is more performant |
443 | option for a bigger blur radius than blurMax as it doesn't increase |
444 | the amount of texture lookups. |
445 | |
446 | \note This affects to both blur and shadow effects. |
447 | |
448 | \include notes.qdocinc performance item resize |
449 | */ |
450 | qreal QQuickMultiEffect::blurMultiplier() const |
451 | { |
452 | Q_D(const QQuickMultiEffect); |
453 | return d->blurMultiplier(); |
454 | } |
455 | |
456 | void QQuickMultiEffect::setBlurMultiplier(qreal blurMultiplier) |
457 | { |
458 | Q_D(QQuickMultiEffect); |
459 | d->setBlurMultiplier(blurMultiplier); |
460 | } |
461 | |
462 | /*! |
463 | \qmlproperty bool QtQuick.Effects::MultiEffect::shadowEnabled |
464 | |
465 | Enables the shadow effect. |
466 | |
467 | \include notes.qdocinc performance shader regen |
468 | */ |
469 | bool QQuickMultiEffect::shadowEnabled() const |
470 | { |
471 | Q_D(const QQuickMultiEffect); |
472 | return d->shadowEnabled(); |
473 | } |
474 | |
475 | void QQuickMultiEffect::setShadowEnabled(bool enabled) |
476 | { |
477 | Q_D(QQuickMultiEffect); |
478 | d->setShadowEnabled(enabled); |
479 | } |
480 | |
481 | /*! |
482 | \qmlproperty real QtQuick.Effects::MultiEffect::shadowOpacity |
483 | |
484 | This property defines the opacity of the drop shadow. This value |
485 | is multiplied with the \c shadowColor alpha value. |
486 | |
487 | The value ranges from 0.0 (fully transparent) to 1.0 (fully opaque). |
488 | By default, the property is set to \c 1.0. |
489 | */ |
490 | qreal QQuickMultiEffect::shadowOpacity() const |
491 | { |
492 | Q_D(const QQuickMultiEffect); |
493 | return d->shadowOpacity(); |
494 | } |
495 | |
496 | void QQuickMultiEffect::setShadowOpacity(qreal shadowOpacity) |
497 | { |
498 | Q_D(QQuickMultiEffect); |
499 | d->setShadowOpacity(shadowOpacity); |
500 | } |
501 | |
502 | /*! |
503 | \qmlproperty real QtQuick.Effects::MultiEffect::shadowBlur |
504 | |
505 | This property defines how much blur (radius) is applied to the shadow. |
506 | |
507 | The value ranges from 0.0 (no blur) to 1.0 (full blur). By default, |
508 | the property is set to \c 1.0. The amount of full blur |
509 | is affected by blurMax and blurMultiplier. |
510 | |
511 | \b {Performance note:} The most optimal way to reduce shadow blurring is |
512 | to make blurMax smaller (if it isn't needed for item blur). Just remember |
513 | to not adjust blurMax during animations. |
514 | */ |
515 | qreal QQuickMultiEffect::shadowBlur() const |
516 | { |
517 | Q_D(const QQuickMultiEffect); |
518 | return d->shadowBlur(); |
519 | } |
520 | |
521 | void QQuickMultiEffect::setShadowBlur(qreal shadowBlur) |
522 | { |
523 | Q_D(QQuickMultiEffect); |
524 | d->setShadowBlur(shadowBlur); |
525 | } |
526 | |
527 | /*! |
528 | \qmlproperty real QtQuick.Effects::MultiEffect::shadowHorizontalOffset |
529 | |
530 | This property defines the horizontal offset of the shadow from the |
531 | item center. |
532 | |
533 | The value ranges from -inf to inf. By default, the property is set |
534 | to \c 0.0. |
535 | |
536 | \note When moving shadow position away from center and adding |
537 | shadowBlur, you possibly also need to increase the paddingRect |
538 | accordingly if you want the shadow to not be clipped. |
539 | */ |
540 | qreal QQuickMultiEffect::shadowHorizontalOffset() const |
541 | { |
542 | Q_D(const QQuickMultiEffect); |
543 | return d->shadowHorizontalOffset(); |
544 | } |
545 | |
546 | void QQuickMultiEffect::setShadowHorizontalOffset(qreal offset) |
547 | { |
548 | Q_D(QQuickMultiEffect); |
549 | d->setShadowHorizontalOffset(offset); |
550 | } |
551 | |
552 | /*! |
553 | \qmlproperty real QtQuick.Effects::MultiEffect::shadowVerticalOffset |
554 | |
555 | This property defines the vertical offset of the shadow from the |
556 | item center. |
557 | |
558 | The value ranges from -inf to inf. By default, the property is set |
559 | to \c 0.0. |
560 | |
561 | \note When moving shadow position away from center and adding |
562 | shadowBlur, you possibly also need to increase the paddingRect |
563 | accordingly if you want the shadow to not be clipped. |
564 | */ |
565 | qreal QQuickMultiEffect::shadowVerticalOffset() const |
566 | { |
567 | Q_D(const QQuickMultiEffect); |
568 | return d->shadowVerticalOffset(); |
569 | } |
570 | |
571 | void QQuickMultiEffect::setShadowVerticalOffset(qreal offset) |
572 | { |
573 | Q_D(QQuickMultiEffect); |
574 | d->setShadowVerticalOffset(offset); |
575 | } |
576 | |
577 | /*! |
578 | \qmlproperty color QtQuick.Effects::MultiEffect::shadowColor |
579 | |
580 | This property defines the RGBA color value which is used for |
581 | the shadow. It is useful for example when a shadow is used for |
582 | simulating a glow effect. |
583 | |
584 | By default, the property is set to \c {Qt.rgba(0.0, 0.0, 0.0, 1.0)} |
585 | (black). |
586 | */ |
587 | QColor QQuickMultiEffect::shadowColor() const |
588 | { |
589 | Q_D(const QQuickMultiEffect); |
590 | return d->shadowColor(); |
591 | } |
592 | |
593 | void QQuickMultiEffect::setShadowColor(const QColor &color) |
594 | { |
595 | Q_D(QQuickMultiEffect); |
596 | d->setShadowColor(color); |
597 | } |
598 | |
599 | /*! |
600 | \qmlproperty real QtQuick.Effects::MultiEffect::shadowScale |
601 | |
602 | This property defines the scale of the shadow. Scaling is applied from |
603 | the center of the item. |
604 | |
605 | The value ranges from 0 to inf. By default, the property is set to |
606 | \c 1.0. |
607 | |
608 | \note When increasing the shadowScale, you possibly also need to |
609 | increase the paddingRect accordingly to avoid the shadow from being |
610 | clipped. |
611 | */ |
612 | qreal QQuickMultiEffect::shadowScale() const |
613 | { |
614 | Q_D(const QQuickMultiEffect); |
615 | return d->shadowScale(); |
616 | } |
617 | |
618 | void QQuickMultiEffect::setShadowScale(qreal shadowScale) |
619 | { |
620 | Q_D(QQuickMultiEffect); |
621 | d->setShadowScale(shadowScale); |
622 | } |
623 | |
624 | /*! |
625 | \qmlproperty bool QtQuick.Effects::MultiEffect::maskEnabled |
626 | |
627 | Enables the mask effect. |
628 | |
629 | \include notes.qdocinc performance shader regen |
630 | */ |
631 | bool QQuickMultiEffect::maskEnabled() const |
632 | { |
633 | Q_D(const QQuickMultiEffect); |
634 | return d->maskEnabled(); |
635 | } |
636 | |
637 | void QQuickMultiEffect::setMaskEnabled(bool enabled) |
638 | { |
639 | Q_D(QQuickMultiEffect); |
640 | d->setMaskEnabled(enabled); |
641 | } |
642 | |
643 | /*! |
644 | \qmlproperty Item QtQuick.Effects::MultiEffect::maskSource |
645 | |
646 | Source item for the mask effect. Should point to ShaderEffectSource, |
647 | item with \l {QtQuick::Item::layer.enabled} {layer.enabled} set to \c true, |
648 | or to an item that can be directly used as a texture source (e.g. |
649 | \l [QML] Image). The alpha channel of the source item is used for masking. |
650 | */ |
651 | QQuickItem *QQuickMultiEffect::maskSource() const |
652 | { |
653 | Q_D(const QQuickMultiEffect); |
654 | return d->maskSource(); |
655 | } |
656 | |
657 | void QQuickMultiEffect::setMaskSource(QQuickItem *item) |
658 | { |
659 | Q_D(QQuickMultiEffect); |
660 | d->setMaskSource(item); |
661 | } |
662 | |
663 | /*! |
664 | \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMin |
665 | |
666 | This property defines a lower threshold value for the mask pixels. |
667 | The mask pixels that have an alpha value below this property are used |
668 | to completely mask away the corresponding pixels from the source item. |
669 | The mask pixels that have a higher alpha value are used to alphablend |
670 | the source item to the display. |
671 | |
672 | The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By |
673 | default, the property is set to \c 0.0. |
674 | */ |
675 | qreal QQuickMultiEffect::maskThresholdMin() const |
676 | { |
677 | Q_D(const QQuickMultiEffect); |
678 | return d->maskThresholdMin(); |
679 | } |
680 | |
681 | void QQuickMultiEffect::setMaskThresholdMin(qreal threshold) |
682 | { |
683 | Q_D(QQuickMultiEffect); |
684 | d->setMaskThresholdMin(threshold); |
685 | } |
686 | |
687 | /*! |
688 | \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMin |
689 | |
690 | This property defines the smoothness of the mask edges near the |
691 | maskThresholdMin. Setting higher spread values softens the transition |
692 | from the transparent mask pixels towards opaque mask pixels by adding |
693 | interpolated values between them. |
694 | |
695 | The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). |
696 | By default, the property is set to \c 0.0. |
697 | */ |
698 | qreal QQuickMultiEffect::maskSpreadAtMin() const |
699 | { |
700 | Q_D(const QQuickMultiEffect); |
701 | return d->maskSpreadAtMin(); |
702 | } |
703 | |
704 | void QQuickMultiEffect::setMaskSpreadAtMin(qreal spread) |
705 | { |
706 | Q_D(QQuickMultiEffect); |
707 | d->setMaskSpreadAtMin(spread); |
708 | } |
709 | |
710 | /*! |
711 | \qmlproperty real QtQuick.Effects::MultiEffect::maskThresholdMax |
712 | |
713 | This property defines an upper threshold value for the mask pixels. |
714 | The mask pixels that have an alpha value below this property are used |
715 | to completely mask away the corresponding pixels from the source item. |
716 | The mask pixels that have a higher alpha value are used to alphablend |
717 | the source item to the display. |
718 | |
719 | The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By |
720 | default, the property is set to \c 1.0. |
721 | */ |
722 | qreal QQuickMultiEffect::maskThresholdMax() const |
723 | { |
724 | Q_D(const QQuickMultiEffect); |
725 | return d->maskThresholdMax(); |
726 | } |
727 | |
728 | void QQuickMultiEffect::setMaskThresholdMax(qreal threshold) |
729 | { |
730 | Q_D(QQuickMultiEffect); |
731 | d->setMaskThresholdMax(threshold); |
732 | } |
733 | |
734 | /*! |
735 | \qmlproperty real QtQuick.Effects::MultiEffect::maskSpreadAtMax |
736 | |
737 | This property defines the smoothness of the mask edges near the |
738 | maskThresholdMax. Using higher spread values softens the transition |
739 | from the transparent mask pixels towards opaque mask pixels by adding |
740 | interpolated values between them. |
741 | |
742 | The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). |
743 | By default, the property is set to \c 0.0. |
744 | */ |
745 | qreal QQuickMultiEffect::maskSpreadAtMax() const |
746 | { |
747 | Q_D(const QQuickMultiEffect); |
748 | return d->maskSpreadAtMax(); |
749 | } |
750 | |
751 | void QQuickMultiEffect::setMaskSpreadAtMax(qreal spread) |
752 | { |
753 | Q_D(QQuickMultiEffect); |
754 | d->setMaskSpreadAtMax(spread); |
755 | } |
756 | |
757 | /*! |
758 | \qmlproperty bool QtQuick.Effects::MultiEffect::maskInverted |
759 | |
760 | This property switches the mask to the opposite side; instead of |
761 | masking away the content outside maskThresholdMin and maskThresholdMax, |
762 | content between them will get masked away. |
763 | |
764 | By default, the property is set to \c false. |
765 | */ |
766 | bool QQuickMultiEffect::maskInverted() const |
767 | { |
768 | Q_D(const QQuickMultiEffect); |
769 | return d->maskInverted(); |
770 | } |
771 | |
772 | void QQuickMultiEffect::setMaskInverted(bool inverted) |
773 | { |
774 | Q_D(QQuickMultiEffect); |
775 | d->setMaskInverted(inverted); |
776 | } |
777 | |
778 | /*! |
779 | \qmlproperty rect QtQuick.Effects::MultiEffect::itemRect |
780 | |
781 | Read-only access to effect item rectangle. This can be used e.g. to see |
782 | the area item covers. |
783 | |
784 | \sa paddingRect, autoPaddingEnabled |
785 | */ |
786 | QRectF QQuickMultiEffect::itemRect() const |
787 | { |
788 | Q_D(const QQuickMultiEffect); |
789 | return d->itemRect(); |
790 | } |
791 | |
792 | /*! |
793 | \qmlproperty string QtQuick.Effects::MultiEffect::fragmentShader |
794 | \readonly |
795 | |
796 | Read-only access to filename of the currently used fragment shader. |
797 | */ |
798 | QString QQuickMultiEffect::fragmentShader() const |
799 | { |
800 | Q_D(const QQuickMultiEffect); |
801 | return d->fragmentShader(); |
802 | } |
803 | |
804 | /*! |
805 | \qmlproperty string QtQuick.Effects::MultiEffect::vertexShader |
806 | \readonly |
807 | |
808 | Read-only access to filename of the currently used vertex shader. |
809 | */ |
810 | QString QQuickMultiEffect::vertexShader() const |
811 | { |
812 | Q_D(const QQuickMultiEffect); |
813 | return d->vertexShader(); |
814 | } |
815 | |
816 | /*! |
817 | \qmlproperty bool QtQuick.Effects::MultiEffect::hasProxySource |
818 | \readonly |
819 | |
820 | Returns true when the MultiEffect internally creates \l ShaderEffectSource |
821 | for the \l source item and false when \l source item is used as-is. |
822 | For example when source is \l Image element or \l Item with |
823 | \l {QtQuick::Item::layer.enabled} {layer.enabled} set to \c true, |
824 | this additional proxy source is not needed. |
825 | */ |
826 | bool QQuickMultiEffect::hasProxySource() const |
827 | { |
828 | Q_D(const QQuickMultiEffect); |
829 | return d->hasProxySource(); |
830 | } |
831 | |
832 | // *** protected *** |
833 | |
834 | void QQuickMultiEffect::componentComplete() |
835 | { |
836 | Q_D(QQuickMultiEffect); |
837 | QQuickItem::componentComplete(); |
838 | d->initialize(); |
839 | } |
840 | |
841 | void QQuickMultiEffect::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) |
842 | { |
843 | Q_D(QQuickMultiEffect); |
844 | QQuickItem::geometryChange(newGeometry, oldGeometry); |
845 | if (width() > 0 && height() > 0) |
846 | d->handleGeometryChange(newGeometry, oldGeometry); |
847 | } |
848 | |
849 | void QQuickMultiEffect::itemChange(ItemChange change, const ItemChangeData &value) |
850 | { |
851 | Q_D(QQuickMultiEffect); |
852 | d->handleItemChange(change, value); |
853 | QQuickItem::itemChange(change, value); |
854 | } |
855 | |
856 | // *** private *** |
857 | |
858 | QQuickMultiEffectPrivate::QQuickMultiEffectPrivate() |
859 | { |
860 | } |
861 | |
862 | QQuickMultiEffectPrivate::~QQuickMultiEffectPrivate() |
863 | { |
864 | } |
865 | |
866 | void QQuickMultiEffectPrivate::handleGeometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) |
867 | { |
868 | Q_UNUSED(oldGeometry); |
869 | Q_UNUSED(newGeometry); |
870 | initialize(); |
871 | if (!m_shaderEffect) |
872 | return; |
873 | updateBlurItemSizes(); |
874 | updateSourcePadding(); |
875 | } |
876 | |
877 | void QQuickMultiEffectPrivate::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) |
878 | { |
879 | Q_UNUSED(value); |
880 | if (change == QQuickItem::ItemSceneChange) |
881 | initialize(); |
882 | } |
883 | |
884 | |
885 | QQuickItem *QQuickMultiEffectPrivate::source() const |
886 | { |
887 | return m_sourceItem; |
888 | } |
889 | |
890 | void QQuickMultiEffectPrivate::setSource(QQuickItem *item) |
891 | { |
892 | Q_Q(QQuickMultiEffect); |
893 | if (item == m_sourceItem) |
894 | return; |
895 | |
896 | m_sourceItem = item; |
897 | if (m_shaderSource) |
898 | m_shaderSource->setInput(m_sourceItem); |
899 | |
900 | updateSourcePadding(); |
901 | q->update(); |
902 | Q_EMIT q->sourceChanged(); |
903 | } |
904 | |
905 | bool QQuickMultiEffectPrivate::autoPaddingEnabled() const |
906 | { |
907 | return m_autoPaddingEnabled; |
908 | } |
909 | |
910 | void QQuickMultiEffectPrivate::setAutoPaddingEnabled(bool enabled) |
911 | { |
912 | Q_Q(QQuickMultiEffect); |
913 | if (enabled == m_autoPaddingEnabled) |
914 | return; |
915 | |
916 | m_autoPaddingEnabled = enabled; |
917 | updateSourcePadding(); |
918 | q->update(); |
919 | Q_EMIT q->autoPaddingEnabledChanged(); |
920 | } |
921 | |
922 | QRectF QQuickMultiEffectPrivate::paddingRect() const |
923 | { |
924 | return m_paddingRect; |
925 | } |
926 | |
927 | void QQuickMultiEffectPrivate::setPaddingRect(const QRectF &rect) |
928 | { |
929 | Q_Q(QQuickMultiEffect); |
930 | if (rect == m_paddingRect) |
931 | return; |
932 | m_paddingRect = rect; |
933 | updateCenterOffset(); |
934 | updateSourcePadding(); |
935 | q->update(); |
936 | emit q->paddingRectChanged(); |
937 | } |
938 | |
939 | qreal QQuickMultiEffectPrivate::brightness() const |
940 | { |
941 | return m_brightness; |
942 | } |
943 | |
944 | void QQuickMultiEffectPrivate::setBrightness(qreal brightness) |
945 | { |
946 | Q_Q(QQuickMultiEffect); |
947 | if (brightness == m_brightness) |
948 | return; |
949 | |
950 | m_brightness = brightness; |
951 | if (m_shaderEffect) |
952 | m_shaderEffect->setProperty(name: "brightness" , value: m_brightness); |
953 | |
954 | q->update(); |
955 | Q_EMIT q->brightnessChanged(); |
956 | } |
957 | |
958 | qreal QQuickMultiEffectPrivate::contrast() const |
959 | { |
960 | return m_contrast; |
961 | } |
962 | |
963 | void QQuickMultiEffectPrivate::setContrast(qreal contrast) |
964 | { |
965 | Q_Q(QQuickMultiEffect); |
966 | if (contrast == m_contrast) |
967 | return; |
968 | |
969 | m_contrast = contrast; |
970 | if (m_shaderEffect) |
971 | m_shaderEffect->setProperty(name: "contrast" , value: m_contrast); |
972 | |
973 | q->update(); |
974 | Q_EMIT q->contrastChanged(); |
975 | } |
976 | |
977 | qreal QQuickMultiEffectPrivate::saturation() const |
978 | { |
979 | return m_saturation; |
980 | } |
981 | |
982 | void QQuickMultiEffectPrivate::setSaturation(qreal saturation) |
983 | { |
984 | Q_Q(QQuickMultiEffect); |
985 | if (saturation == m_saturation) |
986 | return; |
987 | |
988 | m_saturation = saturation; |
989 | if (m_shaderEffect) |
990 | m_shaderEffect->setProperty(name: "saturation" , value: m_saturation); |
991 | |
992 | q->update(); |
993 | Q_EMIT q->saturationChanged(); |
994 | } |
995 | |
996 | qreal QQuickMultiEffectPrivate::colorization() const |
997 | { |
998 | return m_colorization; |
999 | } |
1000 | |
1001 | void QQuickMultiEffectPrivate::setColorization(qreal colorization) |
1002 | { |
1003 | Q_Q(QQuickMultiEffect); |
1004 | if (colorization == m_colorization) |
1005 | return; |
1006 | |
1007 | m_colorization = colorization; |
1008 | updateColorizationColor(); |
1009 | |
1010 | q->update(); |
1011 | Q_EMIT q->colorizationChanged(); |
1012 | } |
1013 | |
1014 | QColor QQuickMultiEffectPrivate::colorizationColor() const |
1015 | { |
1016 | return m_colorizationColor; |
1017 | } |
1018 | |
1019 | void QQuickMultiEffectPrivate::setColorizationColor(const QColor &color) |
1020 | { |
1021 | Q_Q(QQuickMultiEffect); |
1022 | if (color == m_colorizationColor) |
1023 | return; |
1024 | |
1025 | m_colorizationColor = color; |
1026 | updateColorizationColor(); |
1027 | |
1028 | q->update(); |
1029 | Q_EMIT q->colorizationColorChanged(); |
1030 | } |
1031 | |
1032 | bool QQuickMultiEffectPrivate::blurEnabled() const |
1033 | { |
1034 | return m_blurEnabled; |
1035 | } |
1036 | |
1037 | void QQuickMultiEffectPrivate::setBlurEnabled(bool enabled) |
1038 | { |
1039 | Q_Q(QQuickMultiEffect); |
1040 | if (enabled == m_blurEnabled) |
1041 | return; |
1042 | |
1043 | m_blurEnabled = enabled; |
1044 | updateSourcePadding(); |
1045 | updateBlurLevel(); |
1046 | updateEffectShaders(); |
1047 | |
1048 | q->update(); |
1049 | Q_EMIT q->blurEnabledChanged(); |
1050 | } |
1051 | |
1052 | qreal QQuickMultiEffectPrivate::blur() const |
1053 | { |
1054 | return m_blur; |
1055 | } |
1056 | |
1057 | void QQuickMultiEffectPrivate::setBlur(qreal blur) |
1058 | { |
1059 | Q_Q(QQuickMultiEffect); |
1060 | if (blur == m_blur) |
1061 | return; |
1062 | |
1063 | m_blur = blur; |
1064 | updateBlurWeights(); |
1065 | |
1066 | q->update(); |
1067 | Q_EMIT q->blurChanged(); |
1068 | } |
1069 | |
1070 | int QQuickMultiEffectPrivate::blurMax() const |
1071 | { |
1072 | return m_blurMax; |
1073 | } |
1074 | |
1075 | void QQuickMultiEffectPrivate::setBlurMax(int blurMax) |
1076 | { |
1077 | Q_Q(QQuickMultiEffect); |
1078 | if (blurMax == m_blurMax) |
1079 | return; |
1080 | |
1081 | m_blurMax = blurMax; |
1082 | updateSourcePadding(); |
1083 | updateBlurLevel(); |
1084 | updateBlurItemSizes(); |
1085 | updateBlurWeights(); |
1086 | updateShadowBlurWeights(); |
1087 | updateEffectShaders(); |
1088 | |
1089 | q->update(); |
1090 | Q_EMIT q->blurMaxChanged(); |
1091 | } |
1092 | |
1093 | qreal QQuickMultiEffectPrivate::blurMultiplier() const |
1094 | { |
1095 | return m_blurMultiplier; |
1096 | } |
1097 | |
1098 | void QQuickMultiEffectPrivate::setBlurMultiplier(qreal blurMultiplier) |
1099 | { |
1100 | Q_Q(QQuickMultiEffect); |
1101 | if (blurMultiplier == m_blurMultiplier) |
1102 | return; |
1103 | |
1104 | m_blurMultiplier = blurMultiplier; |
1105 | updateSourcePadding(); |
1106 | updateBlurItemSizes(forceUpdate: true); |
1107 | updateBlurWeights(); |
1108 | updateShadowBlurWeights(); |
1109 | |
1110 | q->update(); |
1111 | Q_EMIT q->blurMultiplierChanged(); |
1112 | } |
1113 | |
1114 | bool QQuickMultiEffectPrivate::shadowEnabled() const |
1115 | { |
1116 | return m_shadowEnabled; |
1117 | } |
1118 | |
1119 | void QQuickMultiEffectPrivate::setShadowEnabled(bool enabled) |
1120 | { |
1121 | Q_Q(QQuickMultiEffect); |
1122 | if (enabled == m_shadowEnabled) |
1123 | return; |
1124 | |
1125 | m_shadowEnabled = enabled; |
1126 | updateSourcePadding(); |
1127 | updateBlurLevel(); |
1128 | updateEffectShaders(); |
1129 | |
1130 | q->update(); |
1131 | Q_EMIT q->shadowEnabledChanged(); |
1132 | } |
1133 | |
1134 | qreal QQuickMultiEffectPrivate::shadowOpacity() const |
1135 | { |
1136 | return m_shadowOpacity; |
1137 | } |
1138 | |
1139 | void QQuickMultiEffectPrivate::setShadowOpacity(qreal shadowOpacity) |
1140 | { |
1141 | Q_Q(QQuickMultiEffect); |
1142 | if (shadowOpacity == m_shadowOpacity) |
1143 | return; |
1144 | |
1145 | m_shadowOpacity = shadowOpacity; |
1146 | updateShadowColor(); |
1147 | |
1148 | q->update(); |
1149 | Q_EMIT q->shadowOpacityChanged(); |
1150 | } |
1151 | |
1152 | qreal QQuickMultiEffectPrivate::shadowBlur() const |
1153 | { |
1154 | return m_shadowBlur; |
1155 | } |
1156 | |
1157 | void QQuickMultiEffectPrivate::setShadowBlur(qreal shadowBlur) |
1158 | { |
1159 | Q_Q(QQuickMultiEffect); |
1160 | if (shadowBlur == m_shadowBlur) |
1161 | return; |
1162 | |
1163 | m_shadowBlur = shadowBlur; |
1164 | updateShadowBlurWeights(); |
1165 | |
1166 | q->update(); |
1167 | Q_EMIT q->shadowBlurChanged(); |
1168 | } |
1169 | |
1170 | qreal QQuickMultiEffectPrivate::shadowHorizontalOffset() const |
1171 | { |
1172 | return m_shadowHorizontalOffset; |
1173 | } |
1174 | |
1175 | void QQuickMultiEffectPrivate::setShadowHorizontalOffset(qreal offset) |
1176 | { |
1177 | Q_Q(QQuickMultiEffect); |
1178 | if (offset == m_shadowHorizontalOffset) |
1179 | return; |
1180 | |
1181 | m_shadowHorizontalOffset = offset; |
1182 | updateShadowOffset(); |
1183 | |
1184 | q->update(); |
1185 | Q_EMIT q->shadowHorizontalOffsetChanged(); |
1186 | } |
1187 | |
1188 | qreal QQuickMultiEffectPrivate::shadowVerticalOffset() const |
1189 | { |
1190 | return m_shadowVerticalOffset; |
1191 | } |
1192 | |
1193 | void QQuickMultiEffectPrivate::setShadowVerticalOffset(qreal offset) |
1194 | { |
1195 | Q_Q(QQuickMultiEffect); |
1196 | if (offset == m_shadowVerticalOffset) |
1197 | return; |
1198 | |
1199 | m_shadowVerticalOffset = offset; |
1200 | updateShadowOffset(); |
1201 | |
1202 | q->update(); |
1203 | Q_EMIT q->shadowVerticalOffsetChanged(); |
1204 | } |
1205 | |
1206 | QColor QQuickMultiEffectPrivate::shadowColor() const |
1207 | { |
1208 | return m_shadowColor; |
1209 | } |
1210 | |
1211 | void QQuickMultiEffectPrivate::setShadowColor(const QColor &color) |
1212 | { |
1213 | Q_Q(QQuickMultiEffect); |
1214 | if (color == m_shadowColor) |
1215 | return; |
1216 | |
1217 | m_shadowColor = color; |
1218 | updateShadowColor(); |
1219 | |
1220 | q->update(); |
1221 | Q_EMIT q->shadowColorChanged(); |
1222 | } |
1223 | |
1224 | qreal QQuickMultiEffectPrivate::shadowScale() const |
1225 | { |
1226 | return m_shadowScale; |
1227 | } |
1228 | |
1229 | void QQuickMultiEffectPrivate::setShadowScale(qreal shadowScale) |
1230 | { |
1231 | Q_Q(QQuickMultiEffect); |
1232 | if (shadowScale == m_shadowScale) |
1233 | return; |
1234 | |
1235 | m_shadowScale = shadowScale; |
1236 | updateCenterOffset(); |
1237 | if (m_shaderEffect) |
1238 | m_shaderEffect->setProperty(name: "shadowScale" , value: 1.0 / m_shadowScale); |
1239 | |
1240 | q->update(); |
1241 | Q_EMIT q->shadowScaleChanged(); |
1242 | } |
1243 | |
1244 | bool QQuickMultiEffectPrivate::maskEnabled() const |
1245 | { |
1246 | return m_maskEnabled; |
1247 | } |
1248 | |
1249 | void QQuickMultiEffectPrivate::setMaskEnabled(bool enabled) |
1250 | { |
1251 | Q_Q(QQuickMultiEffect); |
1252 | if (enabled == m_maskEnabled) |
1253 | return; |
1254 | |
1255 | m_maskEnabled = enabled; |
1256 | updateEffectShaders(); |
1257 | |
1258 | q->update(); |
1259 | Q_EMIT q->maskEnabledChanged(); |
1260 | } |
1261 | |
1262 | QQuickItem *QQuickMultiEffectPrivate::maskSource() const |
1263 | { |
1264 | return m_maskSourceItem; |
1265 | } |
1266 | |
1267 | void QQuickMultiEffectPrivate::setMaskSource(QQuickItem *item) |
1268 | { |
1269 | Q_Q(QQuickMultiEffect); |
1270 | if (item == m_maskSourceItem) |
1271 | return; |
1272 | |
1273 | m_maskSourceItem = item; |
1274 | if (m_shaderEffect) { |
1275 | auto maskSourceVariant = QVariant::fromValue<QQuickItem*>(value: m_maskSourceItem); |
1276 | m_shaderEffect->setProperty(name: "maskSrc" , value: maskSourceVariant); |
1277 | } |
1278 | |
1279 | q->update(); |
1280 | Q_EMIT q->maskSourceChanged(); |
1281 | } |
1282 | |
1283 | qreal QQuickMultiEffectPrivate::maskThresholdMin() const |
1284 | { |
1285 | return m_maskThresholdMin; |
1286 | } |
1287 | |
1288 | void QQuickMultiEffectPrivate::setMaskThresholdMin(qreal threshold) |
1289 | { |
1290 | Q_Q(QQuickMultiEffect); |
1291 | if (threshold == m_maskThresholdMin) |
1292 | return; |
1293 | |
1294 | m_maskThresholdMin = threshold; |
1295 | updateMaskThresholdSpread(); |
1296 | |
1297 | q->update(); |
1298 | Q_EMIT q->maskThresholdMinChanged(); |
1299 | } |
1300 | |
1301 | qreal QQuickMultiEffectPrivate::maskSpreadAtMin() const |
1302 | { |
1303 | return m_maskSpreadAtMin; |
1304 | } |
1305 | |
1306 | void QQuickMultiEffectPrivate::setMaskSpreadAtMin(qreal spread) |
1307 | { |
1308 | Q_Q(QQuickMultiEffect); |
1309 | if (spread == m_maskSpreadAtMin) |
1310 | return; |
1311 | |
1312 | m_maskSpreadAtMin = spread; |
1313 | updateMaskThresholdSpread(); |
1314 | |
1315 | q->update(); |
1316 | Q_EMIT q->maskSpreadAtMinChanged(); |
1317 | } |
1318 | |
1319 | qreal QQuickMultiEffectPrivate::maskThresholdMax() const |
1320 | { |
1321 | return m_maskThresholdMax; |
1322 | } |
1323 | |
1324 | void QQuickMultiEffectPrivate::setMaskThresholdMax(qreal threshold) |
1325 | { |
1326 | Q_Q(QQuickMultiEffect); |
1327 | if (threshold == m_maskThresholdMax) |
1328 | return; |
1329 | |
1330 | m_maskThresholdMax = threshold; |
1331 | updateMaskThresholdSpread(); |
1332 | |
1333 | q->update(); |
1334 | Q_EMIT q->maskThresholdMaxChanged(); |
1335 | } |
1336 | |
1337 | qreal QQuickMultiEffectPrivate::maskSpreadAtMax() const |
1338 | { |
1339 | return m_maskSpreadAtMax; |
1340 | } |
1341 | |
1342 | void QQuickMultiEffectPrivate::setMaskSpreadAtMax(qreal spread) |
1343 | { |
1344 | Q_Q(QQuickMultiEffect); |
1345 | if (spread == m_maskSpreadAtMax) |
1346 | return; |
1347 | |
1348 | m_maskSpreadAtMax = spread; |
1349 | updateMaskThresholdSpread(); |
1350 | |
1351 | q->update(); |
1352 | Q_EMIT q->maskSpreadAtMaxChanged(); |
1353 | } |
1354 | |
1355 | bool QQuickMultiEffectPrivate::maskInverted() const |
1356 | { |
1357 | return m_maskInverted; |
1358 | } |
1359 | |
1360 | void QQuickMultiEffectPrivate::setMaskInverted(bool inverted) |
1361 | { |
1362 | Q_Q(QQuickMultiEffect); |
1363 | if (inverted == m_maskInverted) |
1364 | return; |
1365 | |
1366 | m_maskInverted = inverted; |
1367 | if (m_shaderEffect) |
1368 | m_shaderEffect->setProperty(name: "maskInverted" , value: float(m_maskInverted)); |
1369 | |
1370 | q->update(); |
1371 | Q_EMIT q->maskInvertedChanged(); |
1372 | } |
1373 | |
1374 | QRectF QQuickMultiEffectPrivate::itemRect() const |
1375 | { |
1376 | if (!m_shaderEffect || !m_shaderSource) |
1377 | return QRectF(); |
1378 | |
1379 | QRectF sourceRect = m_shaderSource->sourceRect(); |
1380 | if (sourceRect.width() > 0 && sourceRect.height() > 0) |
1381 | return sourceRect; |
1382 | else |
1383 | return m_shaderEffect->boundingRect(); |
1384 | } |
1385 | |
1386 | QString QQuickMultiEffectPrivate::fragmentShader() const |
1387 | { |
1388 | return m_fragShader; |
1389 | } |
1390 | |
1391 | QString QQuickMultiEffectPrivate::vertexShader() const |
1392 | { |
1393 | return m_vertShader; |
1394 | } |
1395 | |
1396 | bool QQuickMultiEffectPrivate::hasProxySource() const |
1397 | { |
1398 | return m_shaderSource && m_shaderSource->isActive(); |
1399 | } |
1400 | |
1401 | // This initializes the component. It will be ran once, when |
1402 | // the component is ready and it has a valid size. |
1403 | void QQuickMultiEffectPrivate::initialize() |
1404 | { |
1405 | Q_Q(QQuickMultiEffect); |
1406 | if (m_initialized) |
1407 | return; |
1408 | if (!q->isComponentComplete()) |
1409 | return; |
1410 | if (!q->window()) |
1411 | return; |
1412 | if (q->width() <= 0 || q->height() <= 0) |
1413 | return; |
1414 | |
1415 | m_shaderEffect = new QQuickShaderEffect(q); |
1416 | m_shaderSource = new QGfxSourceProxy(q); |
1417 | QObject::connect(sender: m_shaderSource, signal: &QGfxSourceProxy::outputChanged, context: q, slot: [this] { proxyOutputChanged(); }); |
1418 | QObject::connect(sender: m_shaderSource, signal: &QGfxSourceProxy::activeChanged, context: q, slot: &QQuickMultiEffect::hasProxySourceChanged); |
1419 | |
1420 | m_shaderEffect->setParentItem(q); |
1421 | m_shaderEffect->setSize(q->size()); |
1422 | |
1423 | m_shaderSource->setParentItem(q); |
1424 | m_shaderSource->setSize(q->size()); |
1425 | m_shaderSource->setInput(m_sourceItem); |
1426 | |
1427 | updateCenterOffset(); |
1428 | updateMaskThresholdSpread(); |
1429 | updateBlurWeights(); |
1430 | updateShadowBlurWeights(); |
1431 | updateColorizationColor(); |
1432 | updateShadowColor(); |
1433 | updateShadowOffset(); |
1434 | |
1435 | // Create properties |
1436 | auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: m_shaderSource->output()); |
1437 | m_shaderEffect->setProperty(name: "src" , value: sourceVariant); |
1438 | m_shaderEffect->setProperty(name: "brightness" , value: m_brightness); |
1439 | m_shaderEffect->setProperty(name: "contrast" , value: m_contrast); |
1440 | m_shaderEffect->setProperty(name: "saturation" , value: m_saturation); |
1441 | m_shaderEffect->setProperty(name: "shadowScale" , value: 1.0 / m_shadowScale); |
1442 | auto maskSourceVariant = QVariant::fromValue<QQuickItem*>(value: m_maskSourceItem); |
1443 | m_shaderEffect->setProperty(name: "maskSrc" , value: maskSourceVariant); |
1444 | m_shaderEffect->setProperty(name: "maskInverted" , value: float(m_maskInverted)); |
1445 | |
1446 | updateBlurLevel(); |
1447 | updateBlurItemSizes(); |
1448 | updateSourcePadding(); |
1449 | |
1450 | updateEffectShaders(); |
1451 | |
1452 | m_initialized = true; |
1453 | } |
1454 | |
1455 | void QQuickMultiEffectPrivate::updateMaskThresholdSpread() |
1456 | { |
1457 | if (!m_shaderEffect) |
1458 | return; |
1459 | |
1460 | // Calculate threshold and spread values for mask |
1461 | // smoothstep, keeping always edge0 < edge1. |
1462 | const qreal c0 = 0.0001; |
1463 | const qreal c1 = 1.0 - c0; |
1464 | const qreal mt1 = m_maskThresholdMin + c0; |
1465 | const qreal ms1 = m_maskSpreadAtMin + 1.0; |
1466 | const qreal mt2 = c1 - m_maskThresholdMax; |
1467 | const qreal ms2 = m_maskSpreadAtMax + 1.0; |
1468 | const QVector4D maskThresholdSpread = QVector4D( |
1469 | mt1 * ms1 - (ms1 - c1), |
1470 | mt1 * ms1, |
1471 | mt2 * ms2 - (ms2 - c1), |
1472 | mt2 * ms2); |
1473 | m_shaderEffect->setProperty(name: "mask" , value: maskThresholdSpread); |
1474 | } |
1475 | |
1476 | void QQuickMultiEffectPrivate::updateCenterOffset() |
1477 | { |
1478 | if (!m_shaderEffect) |
1479 | return; |
1480 | |
1481 | const qreal scale = 1.0 / m_shadowScale; |
1482 | QVector2D centerOffset((1.0 - scale) * (0.5 + 0.5 * (m_paddingRect.x() - m_paddingRect.width()) / m_shaderEffect->width()), |
1483 | (1.0 - scale) * (0.5 + 0.5 * (m_paddingRect.y() - m_paddingRect.height()) / m_shaderEffect->height())); |
1484 | m_shaderEffect->setProperty(name: "centerOffset" , value: centerOffset); |
1485 | } |
1486 | |
1487 | void QQuickMultiEffectPrivate::updateShadowOffset() |
1488 | { |
1489 | if (!m_shaderEffect) |
1490 | return; |
1491 | |
1492 | QVector2D shadowOffset = QVector2D(m_shadowHorizontalOffset / m_shaderEffect->width(), m_shadowVerticalOffset / m_shaderEffect->height()); |
1493 | m_shaderEffect->setProperty(name: "shadowOffset" , value: shadowOffset); |
1494 | } |
1495 | |
1496 | void QQuickMultiEffectPrivate::updateColorizationColor() |
1497 | { |
1498 | if (!m_shaderEffect) |
1499 | return; |
1500 | |
1501 | float alpha = std::clamp(val: float(m_colorizationColor.alphaF() * m_colorization), lo: 0.0f, hi: 1.0f); |
1502 | QVector4D colorizationColor(m_colorizationColor.redF(), |
1503 | m_colorizationColor.greenF(), |
1504 | m_colorizationColor.blueF(), |
1505 | alpha); |
1506 | m_shaderEffect->setProperty(name: "colorizationColor" , value: colorizationColor); |
1507 | } |
1508 | |
1509 | void QQuickMultiEffectPrivate::updateShadowColor() |
1510 | { |
1511 | if (!m_shaderEffect) |
1512 | return; |
1513 | |
1514 | float alpha = std::clamp(val: float(m_shadowColor.alphaF() * m_shadowOpacity), lo: 0.0f, hi: 1.0f); |
1515 | QVector4D shadowColor(m_shadowColor.redF(), |
1516 | m_shadowColor.greenF(), |
1517 | m_shadowColor.blueF(), |
1518 | alpha); |
1519 | |
1520 | m_shaderEffect->setProperty(name: "shadowColor" , value: shadowColor); |
1521 | } |
1522 | |
1523 | float QQuickMultiEffectPrivate::calculateLod(float blurAmount) |
1524 | { |
1525 | return qSqrt(v: blurAmount * float(m_blurMax) / 64.0f) * 1.2f - 0.2f; |
1526 | } |
1527 | |
1528 | float QQuickMultiEffectPrivate::blurWeight(float v) |
1529 | { |
1530 | return std::max(a: 0.0f, b: std::min(a: 1.0f, b: 1.0f - v * 2.0f)); |
1531 | } |
1532 | |
1533 | void QQuickMultiEffectPrivate::getBlurWeights(float blurLod, QVector4D &blurWeight1, QVector2D &blurWeight2) |
1534 | { |
1535 | float bw1 = blurWeight(v: std::fabs(x: blurLod - 0.1f)); |
1536 | float bw2 = blurWeight(v: std::fabs(x: blurLod - 0.3f)); |
1537 | float bw3 = blurWeight(v: std::fabs(x: blurLod - 0.5f)); |
1538 | float bw4 = blurWeight(v: std::fabs(x: blurLod - 0.7f)); |
1539 | float bw5 = blurWeight(v: std::fabs(x: blurLod - 0.9f)); |
1540 | float bw6 = blurWeight(v: std::fabs(x: blurLod - 1.1f)); |
1541 | float bsum = bw1 + bw2 + bw3 + bw4 + bw5 + bw6; |
1542 | blurWeight1 = QVector4D(bw1 / bsum, bw2 / bsum, bw3 / bsum, bw4 / bsum); |
1543 | blurWeight2 = QVector2D(bw5 / bsum, bw6 / bsum); |
1544 | } |
1545 | |
1546 | void QQuickMultiEffectPrivate::updateBlurWeights() |
1547 | { |
1548 | if (!m_shaderEffect) |
1549 | return; |
1550 | float blurLod = calculateLod(blurAmount: m_blur); |
1551 | getBlurWeights(blurLod, blurWeight1&: m_blurWeight1, blurWeight2&: m_blurWeight2); |
1552 | m_shaderEffect->setProperty(name: "blurWeight1" , value: m_blurWeight1); |
1553 | m_shaderEffect->setProperty(name: "blurWeight2" , value: m_blurWeight2); |
1554 | } |
1555 | |
1556 | void QQuickMultiEffectPrivate::updateShadowBlurWeights() |
1557 | { |
1558 | if (!m_shaderEffect) |
1559 | return; |
1560 | float blurLod = calculateLod(blurAmount: m_shadowBlur); |
1561 | getBlurWeights(blurLod, blurWeight1&: m_shadowBlurWeight1, blurWeight2&: m_shadowBlurWeight2); |
1562 | m_shaderEffect->setProperty(name: "shadowBlurWeight1" , value: m_shadowBlurWeight1); |
1563 | m_shaderEffect->setProperty(name: "shadowBlurWeight2" , value: m_shadowBlurWeight2); |
1564 | } |
1565 | |
1566 | void QQuickMultiEffectPrivate::updateBlurItemSizes(bool forceUpdate) |
1567 | { |
1568 | if (m_blurEffects.isEmpty() || !m_shaderSource || !m_sourceItem) |
1569 | return; |
1570 | |
1571 | // First blur item size to be half of th source item |
1572 | // extended size, rounded to next divisible by 16. |
1573 | QSizeF sourceSize = itemRect().size(); |
1574 | QSizeF firstItemSize(std::ceil(x: sourceSize.width() / 16) * 8, |
1575 | std::ceil(x: sourceSize.height() / 16) * 8); |
1576 | |
1577 | if (!forceUpdate && m_firstBlurItemSize == firstItemSize) |
1578 | return; |
1579 | |
1580 | qCDebug(lcQuickEffect) << "Source size:" << sourceSize; |
1581 | m_firstBlurItemSize = firstItemSize; |
1582 | |
1583 | for (int i = 0; i < m_blurEffects.size(); i++) { |
1584 | auto *blurEffect = m_blurEffects[i]; |
1585 | QSizeF itemSize = (i == 0) ? firstItemSize : m_blurEffects[i - 1]->size() * 0.5; |
1586 | qCDebug(lcQuickEffect) << "Blur item" << i << ":" << itemSize; |
1587 | blurEffect->setSize(itemSize); |
1588 | |
1589 | const QVector2D offset((1.0 + m_blurMultiplier) / itemSize.width(), |
1590 | (1.0 + m_blurMultiplier) / itemSize.height()); |
1591 | blurEffect->setProperty(name: "offset" , value: offset); |
1592 | } |
1593 | } |
1594 | |
1595 | void QQuickMultiEffectPrivate::updateEffectShaders() |
1596 | { |
1597 | Q_Q(QQuickMultiEffect); |
1598 | if (!q->isComponentComplete()) |
1599 | return; |
1600 | |
1601 | QString vShader = QStringLiteral("multieffect_c" ); |
1602 | if (m_shadowEnabled) |
1603 | vShader += QStringLiteral("s" ); |
1604 | |
1605 | QString fShader = QStringLiteral("multieffect_c" ); |
1606 | if (m_maskEnabled) |
1607 | fShader += QStringLiteral("m" ); |
1608 | if (m_blurEnabled && m_blurMax > 0) |
1609 | fShader += QStringLiteral("b" ); |
1610 | if (m_shadowEnabled) |
1611 | fShader += QStringLiteral("s" ); |
1612 | |
1613 | fShader += QString::number(m_blurLevel); |
1614 | |
1615 | bool shaderChanged = false; |
1616 | if (fShader != m_fragShader) { |
1617 | shaderChanged = true; |
1618 | m_fragShader = fShader; |
1619 | QUrl fs = QUrl(QStringLiteral("qrc:/data/shaders/%1.frag.qsb" ).arg(a: m_fragShader)); |
1620 | m_shaderEffect->setFragmentShader(fs); |
1621 | Q_EMIT q->fragmentShaderChanged(); |
1622 | } |
1623 | if (vShader != m_vertShader) { |
1624 | shaderChanged = true; |
1625 | m_vertShader = vShader; |
1626 | QUrl vs = QUrl(QStringLiteral("qrc:/data/shaders/%1.vert.qsb" ).arg(a: m_vertShader)); |
1627 | m_shaderEffect->setVertexShader(vs); |
1628 | Q_EMIT q->vertexShaderChanged(); |
1629 | } |
1630 | if (shaderChanged) { |
1631 | qCDebug(lcQuickEffect) << this << "Shaders: " << m_fragShader << m_vertShader; |
1632 | Q_EMIT q->shaderChanged(); |
1633 | } |
1634 | } |
1635 | |
1636 | void QQuickMultiEffectPrivate::updateBlurLevel(bool forceUpdate) |
1637 | { |
1638 | int blurLevel = 0; |
1639 | if ((m_blurEnabled || m_shadowEnabled) && m_blurMax > 0) { |
1640 | if (m_blurMax > 32) |
1641 | blurLevel = 3; |
1642 | else if (m_blurMax > 16) |
1643 | blurLevel = 2; |
1644 | else |
1645 | blurLevel = 1; |
1646 | } |
1647 | |
1648 | if (blurLevel != m_blurLevel || (blurLevel > 0 && m_blurEffects.isEmpty()) || forceUpdate) { |
1649 | // Blur level has changed or blur items need to be |
1650 | // initially created. |
1651 | updateBlurItemsAmount(blurLevel); |
1652 | // When the level grows, new items must be resized |
1653 | if (blurLevel > m_blurLevel) |
1654 | updateBlurItemSizes(forceUpdate: true); |
1655 | } |
1656 | m_blurLevel = blurLevel; |
1657 | } |
1658 | |
1659 | void QQuickMultiEffectPrivate::updateBlurItemsAmount(int blurLevel) |
1660 | { |
1661 | Q_Q(QQuickMultiEffect); |
1662 | if (!m_shaderEffect) |
1663 | return; |
1664 | |
1665 | // Lowest blur level uses 3 items, highest 5 items. |
1666 | int itemsAmount = blurLevel == 0 ? 0 : blurLevel + 2; |
1667 | |
1668 | if (m_blurEffects.size() < itemsAmount) { |
1669 | // Add more blur items. |
1670 | // Note that by design blur items are only added and never reduced |
1671 | // during the lifetime of the effect component. |
1672 | const auto engine = qmlEngine(q); |
1673 | QUrl blurVs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.vert.qsb" )); |
1674 | QUrl blurFs = QUrl(QStringLiteral("qrc:/data/shaders/bluritems.frag.qsb" )); |
1675 | QQmlComponent blurComponent(engine, QUrl(QStringLiteral("qrc:/data/BlurItem.qml" ))); |
1676 | for (int i = m_blurEffects.size(); i < itemsAmount; i++) { |
1677 | auto blurEffect = qobject_cast<QQuickShaderEffect*>(object: blurComponent.create()); |
1678 | blurEffect->setParent(q); |
1679 | blurEffect->setParentItem(q); |
1680 | auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: blurEffect); |
1681 | QString sourceProperty = QStringLiteral("blurSrc%1" ).arg(a: i + 1); |
1682 | m_shaderEffect->setProperty(name: sourceProperty.toUtf8(), value: sourceVariant); |
1683 | // Initial value to avoid "'source' does not have a matching property" warning. |
1684 | // Will be updated with the correct one few lines forward. |
1685 | blurEffect->setProperty(name: "source" , value: sourceVariant); |
1686 | QQuickItemPrivate *priv = QQuickItemPrivate::get(item: blurEffect); |
1687 | priv->layer()->setEnabled(true); |
1688 | priv->layer()->setSmooth(true); |
1689 | blurEffect->setVertexShader(blurVs); |
1690 | blurEffect->setFragmentShader(blurFs); |
1691 | m_blurEffects << blurEffect; |
1692 | } |
1693 | } |
1694 | |
1695 | // Set the blur items source components |
1696 | static const auto dummyShaderSource = new QQuickShaderEffectSource(q); |
1697 | for (int i = 0; i < m_blurEffects.size(); i++) { |
1698 | auto *blurEffect = m_blurEffects[i]; |
1699 | auto sourceItem = (i >= itemsAmount) ? |
1700 | static_cast<QQuickItem *>(dummyShaderSource) : (i == 0) ? |
1701 | static_cast<QQuickItem *>(m_shaderSource->output()) : |
1702 | static_cast<QQuickItem *>(m_blurEffects[i - 1]); |
1703 | auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: sourceItem); |
1704 | blurEffect->setProperty(name: "source" , value: sourceVariant); |
1705 | } |
1706 | } |
1707 | |
1708 | void QQuickMultiEffectPrivate::updateSourcePadding() |
1709 | { |
1710 | Q_Q(QQuickMultiEffect); |
1711 | if (!m_shaderEffect || !m_shaderSource) |
1712 | return; |
1713 | |
1714 | const bool blurItemsNeeded = (m_blurEnabled || m_shadowEnabled) && (m_blurMax > 0); |
1715 | const int itemPadding = m_autoPaddingEnabled && blurItemsNeeded ? m_blurMax * (1.0 + m_blurMultiplier) : 0; |
1716 | |
1717 | // Set the shader effect size |
1718 | if (m_paddingRect != QRectF() || itemPadding > 0) { |
1719 | QRectF effectRect(-m_paddingRect.x() - itemPadding, |
1720 | -m_paddingRect.y() - itemPadding, |
1721 | q->width() + m_paddingRect.x() + m_paddingRect.width() + (itemPadding * 2), |
1722 | q->height() + m_paddingRect.y() + m_paddingRect.height() + (itemPadding * 2)); |
1723 | m_shaderEffect->setX(effectRect.x()); |
1724 | m_shaderEffect->setY(effectRect.y()); |
1725 | m_shaderEffect->setWidth(effectRect.width()); |
1726 | m_shaderEffect->setHeight(effectRect.height()); |
1727 | |
1728 | // Set the source size |
1729 | m_shaderSource->setSize(m_shaderEffect->size()); |
1730 | |
1731 | // When m_sourceItem is set and has size, use that as the base size. |
1732 | // When effect is used as a component in Item "layer.effect", source |
1733 | // doesn't have a size and then we follow the effect item size. |
1734 | const qreal baseWidth = m_sourceItem && m_sourceItem->width() > 0 ? m_sourceItem->width() : q->width(); |
1735 | const qreal baseHeight = m_sourceItem && m_sourceItem->height() > 0 ? m_sourceItem->height() : q->height(); |
1736 | |
1737 | // Set the source rect |
1738 | const qreal widthMultiplier = q->width() > 0 ? baseWidth / q->width() : 1.0; |
1739 | const qreal heightMultiplier = q->height() > 0 ? baseHeight / q->height() : 1.0; |
1740 | const qreal xPadding = itemPadding * widthMultiplier; |
1741 | const qreal yPadding = itemPadding * heightMultiplier; |
1742 | QRectF rect = QRectF(m_paddingRect.x() * widthMultiplier, |
1743 | m_paddingRect.y() * heightMultiplier, |
1744 | m_paddingRect.width() * widthMultiplier, |
1745 | m_paddingRect.height() * heightMultiplier); |
1746 | QRectF sourceRect = QRectF(-rect.x() - xPadding, |
1747 | -rect.y() - yPadding, |
1748 | baseWidth + rect.x() + rect.width() + xPadding * 2, |
1749 | baseHeight + rect.y() + rect.height() + yPadding * 2); |
1750 | m_shaderSource->setSourceRect(sourceRect); |
1751 | } else { |
1752 | m_shaderEffect->setX(0); |
1753 | m_shaderEffect->setY(0); |
1754 | m_shaderEffect->setSize(q->size()); |
1755 | m_shaderSource->setSize(q->size()); |
1756 | m_shaderSource->setSourceRect(QRectF()); |
1757 | } |
1758 | |
1759 | updateShadowOffset(); |
1760 | updateProxyActiveCheck(); |
1761 | updateBlurItemSizes(); |
1762 | Q_EMIT q->paddingRectChanged(); |
1763 | Q_EMIT q->itemRectChanged(); |
1764 | Q_EMIT q->itemSizeChanged(); |
1765 | } |
1766 | |
1767 | void QQuickMultiEffectPrivate::proxyOutputChanged() |
1768 | { |
1769 | if (!m_shaderSource) |
1770 | return; |
1771 | |
1772 | auto sourceVariant = QVariant::fromValue<QQuickItem*>(value: m_shaderSource->output()); |
1773 | m_shaderEffect->setProperty(name: "src" , value: sourceVariant); |
1774 | |
1775 | // Force updating the blur items since the source output has changed |
1776 | updateBlurLevel(forceUpdate: true); |
1777 | updateBlurItemSizes(); |
1778 | updateSourcePadding(); |
1779 | } |
1780 | |
1781 | void QQuickMultiEffectPrivate::updateProxyActiveCheck() |
1782 | { |
1783 | if (!m_shaderSource) |
1784 | return; |
1785 | |
1786 | m_shaderSource->polish(); |
1787 | } |
1788 | |
1789 | QT_END_NAMESPACE |
1790 | |
1791 | #include "moc_qquickmultieffect_p.cpp" |
1792 | |