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 QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickshape_p.h"
41#include "qquickshape_p_p.h"
42#include "qquickshapegenericrenderer_p.h"
43#include "qquickshapenvprrenderer_p.h"
44#include "qquickshapesoftwarerenderer_p.h"
45#include <private/qsgplaintexture_p.h>
46#include <private/qquicksvgparser_p.h>
47#include <QtGui/private/qdrawhelper_p.h>
48#include <QOpenGLFunctions>
49#include <QLoggingCategory>
50#include <QtGui/private/qrhi_p.h>
51
52static void initResources()
53{
54#if defined(QT_STATIC)
55 Q_INIT_RESOURCE(qtquickshapes);
56#endif
57}
58
59QT_BEGIN_NAMESPACE
60
61Q_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync")
62
63/*!
64 \qmlmodule QtQuick.Shapes 1.\QtMinorVersion
65 \title Qt Quick Shapes QML Types
66 \ingroup qmlmodules
67 \brief Provides QML types for drawing stroked and filled shapes.
68
69 To use the types in this module, import the module with the following line:
70
71 \qml \QtMinorVersion
72 import QtQuick.Shapes 1.\1
73 \endqml
74*/
75
76void QQuickShapesModule::defineModule()
77{
78 initResources();
79}
80
81QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams()
82 : strokeColor(Qt::white),
83 strokeWidth(1),
84 fillColor(Qt::white),
85 fillRule(QQuickShapePath::OddEvenFill),
86 joinStyle(QQuickShapePath::BevelJoin),
87 miterLimit(2),
88 capStyle(QQuickShapePath::SquareCap),
89 strokeStyle(QQuickShapePath::SolidLine),
90 dashOffset(0),
91 fillGradient(nullptr)
92{
93 dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space
94}
95
96/*!
97 \qmltype ShapePath
98 //! \instantiates QQuickShapePath
99 \inqmlmodule QtQuick.Shapes
100 \ingroup qtquick-paths
101 \ingroup qtquick-views
102 \inherits Path
103 \brief Describes a Path and associated properties for stroking and filling.
104 \since 5.10
105
106 A \l Shape contains one or more ShapePath elements. At least one ShapePath is
107 necessary in order to have a Shape output anything visible. A ShapePath
108 itself is a \l Path with additional properties describing the stroking and
109 filling parameters, such as the stroke width and color, the fill color or
110 gradient, join and cap styles, and so on. As with ordinary \l Path objects,
111 ShapePath also contains a list of path elements like \l PathMove, \l PathLine,
112 \l PathCubic, \l PathQuad, \l PathArc, together with a starting position.
113
114 Any property changes in these data sets will be bubble up and change the
115 output of the Shape. This means that it is simple and easy to change, or
116 even animate, the starting and ending position, control points, or any
117 stroke or fill parameters using the usual QML bindings and animation types
118 like NumberAnimation.
119
120 In the following example the line join style changes automatically based on
121 the value of joinStyleIndex:
122
123 \qml
124 ShapePath {
125 strokeColor: "black"
126 strokeWidth: 16
127 fillColor: "transparent"
128 capStyle: ShapePath.RoundCap
129
130 property int joinStyleIndex: 0
131
132 property variant styles: [
133 ShapePath.BevelJoin,
134 ShapePath.MiterJoin,
135 ShapePath.RoundJoin
136 ]
137
138 joinStyle: styles[joinStyleIndex]
139
140 startX: 30
141 startY: 30
142 PathLine { x: 100; y: 100 }
143 PathLine { x: 30; y: 100 }
144 }
145 \endqml
146
147 Once associated with a Shape, here is the output with a joinStyleIndex
148 of 2 (ShapePath.RoundJoin):
149
150 \image visualpath-code-example.png
151
152 \sa {Qt Quick Examples - Shapes}, Shape
153 */
154
155QQuickShapePathPrivate::QQuickShapePathPrivate()
156 : dirty(DirtyAll)
157{
158 // Set this QQuickPath to be a ShapePath
159 isShapePath = true;
160}
161
162QQuickShapePath::QQuickShapePath(QObject *parent)
163 : QQuickPath(*(new QQuickShapePathPrivate), parent)
164{
165 // The inherited changed() and the shapePathChanged() signals remain
166 // distinct, and this is intentional. Combining the two is not possible due
167 // to the difference in semantics and the need to act (see dirty flag
168 // below) differently on QQuickPath-related changes.
169
170 connect(sender: this, signal: &QQuickPath::changed, slot: [this]() {
171 Q_D(QQuickShapePath);
172 d->dirty |= QQuickShapePathPrivate::DirtyPath;
173 emit shapePathChanged();
174 });
175}
176
177QQuickShapePath::~QQuickShapePath()
178{
179}
180
181/*!
182 \qmlproperty color QtQuick.Shapes::ShapePath::strokeColor
183
184 This property holds the stroking color.
185
186 When set to \c transparent, no stroking occurs.
187
188 The default value is \c white.
189 */
190
191QColor QQuickShapePath::strokeColor() const
192{
193 Q_D(const QQuickShapePath);
194 return d->sfp.strokeColor;
195}
196
197void QQuickShapePath::setStrokeColor(const QColor &color)
198{
199 Q_D(QQuickShapePath);
200 if (d->sfp.strokeColor != color) {
201 d->sfp.strokeColor = color;
202 d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor;
203 emit strokeColorChanged();
204 emit shapePathChanged();
205 }
206}
207
208/*!
209 \qmlproperty real QtQuick.Shapes::ShapePath::strokeWidth
210
211 This property holds the stroke width.
212
213 When set to a negative value, no stroking occurs.
214
215 The default value is 1.
216 */
217
218qreal QQuickShapePath::strokeWidth() const
219{
220 Q_D(const QQuickShapePath);
221 return d->sfp.strokeWidth;
222}
223
224void QQuickShapePath::setStrokeWidth(qreal w)
225{
226 Q_D(QQuickShapePath);
227 if (d->sfp.strokeWidth != w) {
228 d->sfp.strokeWidth = w;
229 d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth;
230 emit strokeWidthChanged();
231 emit shapePathChanged();
232 }
233}
234
235/*!
236 \qmlproperty color QtQuick.Shapes::ShapePath::fillColor
237
238 This property holds the fill color.
239
240 When set to \c transparent, no filling occurs.
241
242 The default value is \c white.
243 */
244
245QColor QQuickShapePath::fillColor() const
246{
247 Q_D(const QQuickShapePath);
248 return d->sfp.fillColor;
249}
250
251void QQuickShapePath::setFillColor(const QColor &color)
252{
253 Q_D(QQuickShapePath);
254 if (d->sfp.fillColor != color) {
255 d->sfp.fillColor = color;
256 d->dirty |= QQuickShapePathPrivate::DirtyFillColor;
257 emit fillColorChanged();
258 emit shapePathChanged();
259 }
260}
261
262/*!
263 \qmlproperty enumeration QtQuick.Shapes::ShapePath::fillRule
264
265 This property holds the fill rule. The default value is
266 \c ShapePath.OddEvenFill. For an explanation on fill rules, see
267 QPainterPath::setFillRule().
268
269 \value ShapePath.OddEvenFill
270 Odd-even fill rule.
271
272 \value ShapePath.WindingFill
273 Non-zero winding fill rule.
274 */
275
276QQuickShapePath::FillRule QQuickShapePath::fillRule() const
277{
278 Q_D(const QQuickShapePath);
279 return d->sfp.fillRule;
280}
281
282void QQuickShapePath::setFillRule(FillRule fillRule)
283{
284 Q_D(QQuickShapePath);
285 if (d->sfp.fillRule != fillRule) {
286 d->sfp.fillRule = fillRule;
287 d->dirty |= QQuickShapePathPrivate::DirtyFillRule;
288 emit fillRuleChanged();
289 emit shapePathChanged();
290 }
291}
292
293/*!
294 \qmlproperty enumeration QtQuick.Shapes::ShapePath::joinStyle
295
296 This property defines how joins between two connected lines are drawn. The
297 default value is \c ShapePath.BevelJoin.
298
299 \value ShapePath.MiterJoin
300 The outer edges of the lines are extended to meet at an angle, and
301 this area is filled.
302
303 \value ShapePath.BevelJoin
304 The triangular notch between the two lines is filled.
305
306 \value ShapePath.RoundJoin
307 A circular arc between the two lines is filled.
308 */
309
310QQuickShapePath::JoinStyle QQuickShapePath::joinStyle() const
311{
312 Q_D(const QQuickShapePath);
313 return d->sfp.joinStyle;
314}
315
316void QQuickShapePath::setJoinStyle(JoinStyle style)
317{
318 Q_D(QQuickShapePath);
319 if (d->sfp.joinStyle != style) {
320 d->sfp.joinStyle = style;
321 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
322 emit joinStyleChanged();
323 emit shapePathChanged();
324 }
325}
326
327/*!
328 \qmlproperty int QtQuick.Shapes::ShapePath::miterLimit
329
330 When joinStyle is set to \c ShapePath.MiterJoin, this property
331 specifies how far the miter join can extend from the join point.
332
333 The default value is 2.
334 */
335
336int QQuickShapePath::miterLimit() const
337{
338 Q_D(const QQuickShapePath);
339 return d->sfp.miterLimit;
340}
341
342void QQuickShapePath::setMiterLimit(int limit)
343{
344 Q_D(QQuickShapePath);
345 if (d->sfp.miterLimit != limit) {
346 d->sfp.miterLimit = limit;
347 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
348 emit miterLimitChanged();
349 emit shapePathChanged();
350 }
351}
352
353/*!
354 \qmlproperty enumeration QtQuick.Shapes::ShapePath::capStyle
355
356 This property defines how the end points of lines are drawn. The
357 default value is \c ShapePath.SquareCap.
358
359 \value ShapePath.FlatCap
360 A square line end that does not cover the end point of the line.
361
362 \value ShapePath.SquareCap
363 A square line end that covers the end point and extends beyond it
364 by half the line width.
365
366 \value ShapePath.RoundCap
367 A rounded line end.
368 */
369
370QQuickShapePath::CapStyle QQuickShapePath::capStyle() const
371{
372 Q_D(const QQuickShapePath);
373 return d->sfp.capStyle;
374}
375
376void QQuickShapePath::setCapStyle(CapStyle style)
377{
378 Q_D(QQuickShapePath);
379 if (d->sfp.capStyle != style) {
380 d->sfp.capStyle = style;
381 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
382 emit capStyleChanged();
383 emit shapePathChanged();
384 }
385}
386
387/*!
388 \qmlproperty enumeration QtQuick.Shapes::ShapePath::strokeStyle
389
390 This property defines the style of stroking. The default value is
391 ShapePath.SolidLine.
392
393 \list
394 \li ShapePath.SolidLine - A plain line.
395 \li ShapePath.DashLine - Dashes separated by a few pixels.
396 \endlist
397 */
398
399QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const
400{
401 Q_D(const QQuickShapePath);
402 return d->sfp.strokeStyle;
403}
404
405void QQuickShapePath::setStrokeStyle(StrokeStyle style)
406{
407 Q_D(QQuickShapePath);
408 if (d->sfp.strokeStyle != style) {
409 d->sfp.strokeStyle = style;
410 d->dirty |= QQuickShapePathPrivate::DirtyDash;
411 emit strokeStyleChanged();
412 emit shapePathChanged();
413 }
414}
415
416/*!
417 \qmlproperty real QtQuick.Shapes::ShapePath::dashOffset
418
419 This property defines the starting point on the dash pattern, measured in
420 units used to specify the dash pattern.
421
422 The default value is 0.
423
424 \sa QPen::setDashOffset()
425 */
426
427qreal QQuickShapePath::dashOffset() const
428{
429 Q_D(const QQuickShapePath);
430 return d->sfp.dashOffset;
431}
432
433void QQuickShapePath::setDashOffset(qreal offset)
434{
435 Q_D(QQuickShapePath);
436 if (d->sfp.dashOffset != offset) {
437 d->sfp.dashOffset = offset;
438 d->dirty |= QQuickShapePathPrivate::DirtyDash;
439 emit dashOffsetChanged();
440 emit shapePathChanged();
441 }
442}
443
444/*!
445 \qmlproperty list<real> QtQuick.Shapes::ShapePath::dashPattern
446
447 This property defines the dash pattern when ShapePath.strokeStyle is set
448 to ShapePath.DashLine. The pattern must be specified as an even number of
449 positive entries where the entries 1, 3, 5... are the dashes and 2, 4,
450 6... are the spaces. The pattern is specified in units of the pen's width.
451
452 The default value is (4, 2), meaning a dash of 4 * ShapePath.strokeWidth
453 pixels followed by a space of 2 * ShapePath.strokeWidth pixels.
454
455 \sa QPen::setDashPattern()
456 */
457
458QVector<qreal> QQuickShapePath::dashPattern() const
459{
460 Q_D(const QQuickShapePath);
461 return d->sfp.dashPattern;
462}
463
464void QQuickShapePath::setDashPattern(const QVector<qreal> &array)
465{
466 Q_D(QQuickShapePath);
467 if (d->sfp.dashPattern != array) {
468 d->sfp.dashPattern = array;
469 d->dirty |= QQuickShapePathPrivate::DirtyDash;
470 emit dashPatternChanged();
471 emit shapePathChanged();
472 }
473}
474
475/*!
476 \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::fillGradient
477
478 This property defines the fill gradient. By default no gradient is enabled
479 and the value is \c null. In this case the fill uses a solid color based
480 on the value of ShapePath.fillColor.
481
482 When set, ShapePath.fillColor is ignored and filling is done using one of
483 the ShapeGradient subtypes.
484
485 \note The Gradient type cannot be used here. Rather, prefer using one of
486 the advanced subtypes, like LinearGradient.
487 */
488
489QQuickShapeGradient *QQuickShapePath::fillGradient() const
490{
491 Q_D(const QQuickShapePath);
492 return d->sfp.fillGradient;
493}
494
495void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
496{
497 Q_D(QQuickShapePath);
498 if (d->sfp.fillGradient != gradient) {
499 if (d->sfp.fillGradient)
500 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
501 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
502 d->sfp.fillGradient = gradient;
503 if (d->sfp.fillGradient)
504 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
505 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
506 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
507 emit shapePathChanged();
508 }
509}
510
511void QQuickShapePathPrivate::_q_fillGradientChanged()
512{
513 Q_Q(QQuickShapePath);
514 dirty |= DirtyFillGradient;
515 emit q->shapePathChanged();
516}
517
518void QQuickShapePath::resetFillGradient()
519{
520 setFillGradient(nullptr);
521}
522
523/*!
524 \qmltype Shape
525 //! \instantiates QQuickShape
526 \inqmlmodule QtQuick.Shapes
527 \ingroup qtquick-paths
528 \ingroup qtquick-views
529 \inherits Item
530 \brief Renders a path.
531 \since 5.10
532
533 Renders a path either by generating geometry via QPainterPath and manual
534 triangulation or by using a GPU vendor extension like
535 \c{GL_NV_path_rendering}.
536
537 This approach is different from rendering shapes via QQuickPaintedItem or
538 the 2D Canvas because the path never gets rasterized in software.
539 Therefore Shape is suitable for creating shapes spreading over larger
540 areas of the screen, avoiding the performance penalty for texture uploads
541 or framebuffer blits. In addition, the declarative API allows manipulating,
542 binding to, and even animating the path element properties like starting
543 and ending position, the control points, and so on.
544
545 The types for specifying path elements are shared between \l PathView and
546 Shape. However, not all Shape implementations support all path
547 element types, while some may not make sense for PathView. Shape's
548 currently supported subset is: PathMove, PathLine, PathQuad, PathCubic,
549 PathArc, and PathSvg.
550
551 See \l Path for a detailed overview of the supported path elements.
552
553 \qml
554 Shape {
555 width: 200
556 height: 150
557 anchors.centerIn: parent
558 ShapePath {
559 strokeWidth: 4
560 strokeColor: "red"
561 fillGradient: LinearGradient {
562 x1: 20; y1: 20
563 x2: 180; y2: 130
564 GradientStop { position: 0; color: "blue" }
565 GradientStop { position: 0.2; color: "green" }
566 GradientStop { position: 0.4; color: "red" }
567 GradientStop { position: 0.6; color: "yellow" }
568 GradientStop { position: 1; color: "cyan" }
569 }
570 strokeStyle: ShapePath.DashLine
571 dashPattern: [ 1, 4 ]
572 startX: 20; startY: 20
573 PathLine { x: 180; y: 130 }
574 PathLine { x: 20; y: 130 }
575 PathLine { x: 20; y: 20 }
576 }
577 }
578 \endqml
579
580 \image pathitem-code-example.png
581
582 Like \l Item, Shape also allows any visual or non-visual objects to be
583 declared as children. ShapePath objects are handled specially. This is
584 useful since it allows adding visual items, like \l Rectangle or \l Image,
585 and non-visual objects, like \l Timer directly as children of Shape.
586
587 The following list summarizes the available Shape rendering approaches:
588
589 \list
590
591 \li When running with the OpenGL backend of Qt Quick, both the generic,
592 triangulation-based and the NVIDIA-specific \c{GL_NV_path_rendering}
593 methods are available. By default only the generic approach is used.
594 Setting Shape.vendorExtensionsEnabled property to \c true leads to using
595 NV_path_rendering on NVIDIA systems when running directly on OpenGL, and
596 the generic method on others. When OpenGL is not used directly by the scene
597 graph, for example because it is using the graphics abstraction layer
598 (QRhi), only the generic shape renderer is available.
599
600 \li The \c software backend is fully supported. The path is rendered via
601 QPainter::strokePath() and QPainter::fillPath() in this case.
602
603 \li The Direct 3D 12 backend is not currently supported.
604
605 \li The OpenVG backend is not currently supported.
606
607 \endlist
608
609 When using Shape, it is important to be aware of potential performance
610 implications:
611
612 \list
613
614 \li When the application is running with the generic, triangulation-based
615 Shape implementation, the geometry generation happens entirely on the
616 CPU. This is potentially expensive. Changing the set of path elements,
617 changing the properties of these elements, or changing certain properties
618 of the Shape itself all lead to retriangulation of the affected paths on
619 every change. Therefore, applying animation to such properties can affect
620 performance on less powerful systems.
621
622 \li However, the data-driven, declarative nature of the Shape API often
623 means better cacheability for the underlying CPU and GPU resources. A
624 property change in one ShapePath will only lead to reprocessing the
625 affected ShapePath, leaving other parts of the Shape unchanged. Therefore,
626 a frequently changing property can still result in a lower overall system
627 load than with imperative painting approaches (for example, QPainter).
628
629 \li If animating properties other than stroke and fill colors is a must,
630 it is recommended to target systems providing \c{GL_NV_path_rendering}
631 where the cost of property changes is smaller.
632
633 \li At the same time, attention must be paid to the number of Shape
634 elements in the scene, in particular when using this special accelerated
635 approach for \c{GL_NV_path_rendering}. The way such a Shape item is
636 represented in the scene graph is different from an ordinary
637 geometry-based item, and incurs a certain cost when it comes to OpenGL
638 state changes.
639
640 \li As a general rule, scenes should avoid using separate Shape items when
641 it is not absolutely necessary. Prefer using one Shape item with multiple
642 ShapePath elements over multiple Shape items.
643
644 \endlist
645
646 \sa {Qt Quick Examples - Shapes}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
647*/
648
649QQuickShapePrivate::QQuickShapePrivate()
650 : effectRefCount(0)
651{
652}
653
654QQuickShapePrivate::~QQuickShapePrivate()
655{
656 delete renderer;
657}
658
659void QQuickShapePrivate::_q_shapePathChanged()
660{
661 Q_Q(QQuickShape);
662 spChanged = true;
663 q->polish();
664}
665
666void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
667{
668 Q_Q(QQuickShape);
669 if (status != newStatus) {
670 status = newStatus;
671 emit q->statusChanged();
672 }
673}
674
675QQuickShape::QQuickShape(QQuickItem *parent)
676 : QQuickItem(*(new QQuickShapePrivate), parent)
677{
678 setFlag(flag: ItemHasContents);
679}
680
681QQuickShape::~QQuickShape()
682{
683}
684
685/*!
686 \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType
687
688 This property determines which path rendering backend is active.
689
690 \value Shape.UnknownRenderer
691 The renderer is unknown.
692
693 \value Shape.GeometryRenderer
694 The generic, driver independent solution for OpenGL. Uses the same
695 CPU-based triangulation approach as QPainter's OpenGL 2 paint
696 engine. This is the default on non-NVIDIA hardware when the default,
697 OpenGL Qt Quick scenegraph backend is in use.
698
699 \value Shape.NvprRenderer
700 Path items are rendered by performing OpenGL calls using the
701 \c{GL_NV_path_rendering} extension. This is the default on NVIDIA
702 hardware when the default, OpenGL Qt Quick scenegraph backend is in
703 use.
704
705 \value Shape.SoftwareRenderer
706 Pure QPainter drawing using the raster paint engine. This is the
707 default, and only, option when the Qt Quick scenegraph is running
708 with the \c software backend.
709*/
710
711QQuickShape::RendererType QQuickShape::rendererType() const
712{
713 Q_D(const QQuickShape);
714 return d->rendererType;
715}
716
717/*!
718 \qmlproperty bool QtQuick.Shapes::Shape::asynchronous
719
720 When rendererType is \c Shape.GeometryRenderer, the input path is
721 triangulated on the CPU during the polishing phase of the Shape. This is
722 potentially expensive. To offload this work to separate worker threads,
723 set this property to \c true.
724
725 When enabled, making a Shape visible will not wait for the content to
726 become available. Instead, the GUI/main thread is not blocked and the
727 results of the path rendering are shown only when all the asynchronous
728 work has been finished.
729
730 The default value is \c false.
731 */
732
733bool QQuickShape::asynchronous() const
734{
735 Q_D(const QQuickShape);
736 return d->async;
737}
738
739void QQuickShape::setAsynchronous(bool async)
740{
741 Q_D(QQuickShape);
742 if (d->async != async) {
743 d->async = async;
744 emit asynchronousChanged();
745 if (d->componentComplete)
746 d->_q_shapePathChanged();
747 }
748}
749
750/*!
751 \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
752
753 This property controls the usage of non-standard OpenGL extensions like
754 \c GL_NV_path_rendering.
755
756 The default value is \c false.
757
758 As of Qt 5.12 Shape.NvprRenderer is disabled by default and a uniform
759 behavior, based on triangulating the path and generating QSGGeometryNode
760 instances, is used regardless of the graphics card and drivers. To enable
761 using vendor-specific path rendering approaches set the value to \c true.
762 Depending on the platform and content, this can lead to improved
763 performance. Setting the value to \c true is safe in any case since
764 rendering falls back to the default method when the vendor-specific
765 approach, such as \c GL_NV_path_rendering, is not supported at run time.
766
767 \deprecated
768
769 Changing the default value (false) is not recommended. In particular,
770 support for Shape.NvprRenderer will not be available in future Qt versions.
771 Applications experiencing rendering problems with the property set to true
772 are advised to set it to false.
773 */
774
775bool QQuickShape::vendorExtensionsEnabled() const
776{
777 Q_D(const QQuickShape);
778 return d->enableVendorExts;
779}
780
781void QQuickShape::setVendorExtensionsEnabled(bool enable)
782{
783 Q_D(QQuickShape);
784 if (d->enableVendorExts != enable) {
785 d->enableVendorExts = enable;
786 emit vendorExtensionsEnabledChanged();
787 }
788}
789
790/*!
791 \qmlproperty enumeration QtQuick.Shapes::Shape::status
792
793 This property determines the status of the Shape and is relevant when
794 Shape.asynchronous is set to \c true.
795
796 \value Shape.Null
797 Not yet initialized.
798
799 \value Shape.Ready
800 The Shape has finished processing.
801
802 \value Shape.Processing
803 The path is being processed.
804 */
805
806QQuickShape::Status QQuickShape::status() const
807{
808 Q_D(const QQuickShape);
809 return d->status;
810}
811
812/*!
813 \qmlproperty enumeration QtQuick.Shapes::Shape::containsMode
814 \since QtQuick.Shapes 1.11
815
816 This property determines the definition of \l {QQuickItem::contains()}{contains()}
817 for the Shape. It is useful in case you add \l {Qt Quick Input Handlers} and you want to
818 react only when the mouse or touchpoint is fully inside the Shape.
819
820 \value Shape.BoundingRectContains
821 The default implementation of \l QQuickItem::contains() checks only
822 whether the given point is inside the rectangular bounding box. This is
823 the most efficient implementation, which is why it's the default.
824
825 \value Shape.FillContains
826 Check whether the interior (the part that would be filled if you are
827 rendering it with fill) of any \l ShapePath that makes up this Shape
828 contains the given point. The more complex and numerous ShapePaths you
829 add, the less efficient this is to check, which can potentially slow
830 down event delivery in your application. So it should be used with care.
831
832 One way to speed up the \c FillContains check is to generate an approximate
833 outline with as few points as possible, place that in a transparent Shape
834 on top, and add your Pointer Handlers to that, so that the containment
835 check is cheaper during event delivery.
836*/
837QQuickShape::ContainsMode QQuickShape::containsMode() const
838{
839 Q_D(const QQuickShape);
840 return d->containsMode;
841}
842
843void QQuickShape::setContainsMode(QQuickShape::ContainsMode containsMode)
844{
845 Q_D(QQuickShape);
846 if (d->containsMode == containsMode)
847 return;
848
849 d->containsMode = containsMode;
850 emit containsModeChanged();
851}
852
853bool QQuickShape::contains(const QPointF &point) const
854{
855 Q_D(const QQuickShape);
856 switch (d->containsMode) {
857 case BoundingRectContains:
858 return QQuickItem::contains(point);
859 case FillContains:
860 for (QQuickShapePath *path : d->sp) {
861 if (path->path().contains(pt: point))
862 return true;
863 }
864 }
865 return false;
866}
867
868static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
869{
870 QQuickShape *item = static_cast<QQuickShape *>(property->object);
871 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
872 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(object: obj);
873 if (path)
874 d->sp.append(t: path);
875
876 QQuickItemPrivate::data_append(property, obj);
877
878 if (path && d->componentComplete) {
879 QObject::connect(sender: path, SIGNAL(shapePathChanged()), receiver: item, SLOT(_q_shapePathChanged()));
880 d->_q_shapePathChanged();
881 }
882}
883
884static void vpe_clear(QQmlListProperty<QObject> *property)
885{
886 QQuickShape *item = static_cast<QQuickShape *>(property->object);
887 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
888
889 for (QQuickShapePath *p : d->sp)
890 QObject::disconnect(sender: p, SIGNAL(shapePathChanged()), receiver: item, SLOT(_q_shapePathChanged()));
891
892 d->sp.clear();
893
894 QQuickItemPrivate::data_clear(property);
895
896 if (d->componentComplete)
897 d->_q_shapePathChanged();
898}
899
900/*!
901 \qmlproperty list<Object> QtQuick.Shapes::Shape::data
902
903 This property holds the ShapePath objects that define the contents of the
904 Shape. It can also contain any other type of objects, since Shape, like
905 Item, allows adding any visual or non-visual objects as children.
906
907 \default
908 */
909
910QQmlListProperty<QObject> QQuickShape::data()
911{
912 return QQmlListProperty<QObject>(this,
913 nullptr,
914 vpe_append,
915 QQuickItemPrivate::data_count,
916 QQuickItemPrivate::data_at,
917 vpe_clear);
918}
919
920void QQuickShape::classBegin()
921{
922 QQuickItem::classBegin();
923}
924
925void QQuickShape::componentComplete()
926{
927 Q_D(QQuickShape);
928
929 QQuickItem::componentComplete();
930
931 for (QQuickShapePath *p : d->sp)
932 connect(sender: p, SIGNAL(shapePathChanged()), receiver: this, SLOT(_q_shapePathChanged()));
933
934 d->_q_shapePathChanged();
935}
936
937void QQuickShape::updatePolish()
938{
939 Q_D(QQuickShape);
940
941 const int currentEffectRefCount = d->extra.isAllocated() ? d->extra->recursiveEffectRefCount : 0;
942 if (!d->spChanged && currentEffectRefCount <= d->effectRefCount)
943 return;
944
945 d->spChanged = false;
946 d->effectRefCount = currentEffectRefCount;
947
948 if (!d->renderer) {
949 d->createRenderer();
950 if (!d->renderer)
951 return;
952 emit rendererChanged();
953 }
954
955 // endSync() is where expensive calculations may happen (or get kicked off
956 // on worker threads), depending on the backend. Therefore do this only
957 // when the item is visible.
958 if (isVisible() || d->effectRefCount > 0)
959 d->sync();
960
961 update();
962}
963
964void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data)
965{
966 Q_D(QQuickShape);
967
968 // sync may have been deferred; do it now if the item became visible
969 if (change == ItemVisibleHasChanged && data.boolValue)
970 d->_q_shapePathChanged();
971 else if (change == QQuickItem::ItemSceneChange) {
972 for (int i = 0; i < d->sp.count(); ++i)
973 QQuickShapePathPrivate::get(p: d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
974 d->_q_shapePathChanged();
975 }
976
977 QQuickItem::itemChange(change, data);
978}
979
980QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
981{
982 // Called on the render thread, with the gui thread blocked. We can now
983 // safely access gui thread data.
984
985 Q_D(QQuickShape);
986 if (d->renderer) {
987 if (!node)
988 node = d->createNode();
989 d->renderer->updateNode();
990 }
991 return node;
992}
993
994// the renderer object lives on the gui thread
995void QQuickShapePrivate::createRenderer()
996{
997 Q_Q(QQuickShape);
998 QSGRendererInterface *ri = q->window()->rendererInterface();
999 if (!ri)
1000 return;
1001
1002 switch (ri->graphicsApi()) {
1003#if QT_CONFIG(opengl)
1004 case QSGRendererInterface::OpenGL:
1005 if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) {
1006 rendererType = QQuickShape::NvprRenderer;
1007 renderer = new QQuickShapeNvprRenderer;
1008 } else {
1009 rendererType = QQuickShape::GeometryRenderer;
1010 renderer = new QQuickShapeGenericRenderer(q);
1011 }
1012 break;
1013#endif
1014 case QSGRendererInterface::Software:
1015 rendererType = QQuickShape::SoftwareRenderer;
1016 renderer = new QQuickShapeSoftwareRenderer;
1017 break;
1018 default:
1019 if (QSGRendererInterface::isApiRhiBased(api: ri->graphicsApi())) {
1020 rendererType = QQuickShape::GeometryRenderer;
1021 renderer = new QQuickShapeGenericRenderer(q);
1022 } else {
1023 qWarning(msg: "No path backend for this graphics API yet");
1024 }
1025 break;
1026 }
1027}
1028
1029// the node lives on the render thread
1030QSGNode *QQuickShapePrivate::createNode()
1031{
1032 Q_Q(QQuickShape);
1033 QSGNode *node = nullptr;
1034 if (!q->window())
1035 return node;
1036 QSGRendererInterface *ri = q->window()->rendererInterface();
1037 if (!ri)
1038 return node;
1039
1040 switch (ri->graphicsApi()) {
1041#if QT_CONFIG(opengl)
1042 case QSGRendererInterface::OpenGL:
1043 if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) {
1044 node = new QQuickShapeNvprRenderNode;
1045 static_cast<QQuickShapeNvprRenderer *>(renderer)->setNode(
1046 static_cast<QQuickShapeNvprRenderNode *>(node));
1047 } else {
1048 node = new QQuickShapeGenericNode;
1049 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1050 static_cast<QQuickShapeGenericNode *>(node));
1051 }
1052 break;
1053#endif
1054 case QSGRendererInterface::Software:
1055 node = new QQuickShapeSoftwareRenderNode(q);
1056 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1057 static_cast<QQuickShapeSoftwareRenderNode *>(node));
1058 break;
1059 default:
1060 if (QSGRendererInterface::isApiRhiBased(api: ri->graphicsApi())) {
1061 node = new QQuickShapeGenericNode;
1062 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1063 static_cast<QQuickShapeGenericNode *>(node));
1064 } else {
1065 qWarning(msg: "No path backend for this graphics API yet");
1066 }
1067 break;
1068 }
1069
1070 return node;
1071}
1072
1073void QQuickShapePrivate::asyncShapeReady(void *data)
1074{
1075 QQuickShapePrivate *self = static_cast<QQuickShapePrivate *>(data);
1076 self->setStatus(QQuickShape::Ready);
1077 if (self->syncTimingActive)
1078 qDebug(msg: "[Shape %p] [%d] [dirty=0x%x] async update took %lld ms",
1079 self->q_func(), self->syncTimeCounter, self->syncTimingTotalDirty, self->syncTimer.elapsed());
1080}
1081
1082void QQuickShapePrivate::sync()
1083{
1084 syncTimingTotalDirty = 0;
1085 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1086 if (syncTimingActive)
1087 syncTimer.start();
1088
1089 const bool useAsync = async && renderer->flags().testFlag(flag: QQuickAbstractPathRenderer::SupportsAsync);
1090 if (useAsync) {
1091 setStatus(QQuickShape::Processing);
1092 renderer->setAsyncCallback(asyncShapeReady, this);
1093 }
1094
1095 const int count = sp.count();
1096 renderer->beginSync(totalCount: count);
1097
1098 for (int i = 0; i < count; ++i) {
1099 QQuickShapePath *p = sp[i];
1100 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1101 syncTimingTotalDirty |= dirty;
1102
1103 if (dirty & QQuickShapePathPrivate::DirtyPath)
1104 renderer->setPath(index: i, path: p);
1105 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor)
1106 renderer->setStrokeColor(index: i, color: p->strokeColor());
1107 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth)
1108 renderer->setStrokeWidth(index: i, w: p->strokeWidth());
1109 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1110 renderer->setFillColor(index: i, color: p->fillColor());
1111 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1112 renderer->setFillRule(index: i, fillRule: p->fillRule());
1113 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1114 renderer->setJoinStyle(index: i, joinStyle: p->joinStyle(), miterLimit: p->miterLimit());
1115 renderer->setCapStyle(index: i, capStyle: p->capStyle());
1116 }
1117 if (dirty & QQuickShapePathPrivate::DirtyDash)
1118 renderer->setStrokeStyle(index: i, strokeStyle: p->strokeStyle(), dashOffset: p->dashOffset(), dashPattern: p->dashPattern());
1119 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1120 renderer->setFillGradient(index: i, gradient: p->fillGradient());
1121
1122 dirty = 0;
1123 }
1124
1125 if (syncTimingTotalDirty)
1126 ++syncTimeCounter;
1127 else
1128 syncTimingActive = false;
1129
1130 renderer->endSync(async: useAsync);
1131
1132 if (!useAsync) {
1133 setStatus(QQuickShape::Ready);
1134 if (syncTimingActive)
1135 qDebug(msg: "[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1136 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1137 }
1138}
1139
1140// ***** gradient support *****
1141
1142/*!
1143 \qmltype ShapeGradient
1144 //! \instantiates QQuickShapeGradient
1145 \inqmlmodule QtQuick.Shapes
1146 \ingroup qtquick-paths
1147 \ingroup qtquick-views
1148 \inherits Gradient
1149 \brief Base type of Shape fill gradients.
1150 \since 5.10
1151
1152 This is an abstract base class for gradients like LinearGradient and
1153 cannot be created directly. It extends \l Gradient with properties like the
1154 spread mode.
1155 */
1156
1157QQuickShapeGradient::QQuickShapeGradient(QObject *parent)
1158 : QQuickGradient(parent),
1159 m_spread(PadSpread)
1160{
1161}
1162
1163/*!
1164 \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spread
1165
1166 Specifies how the area outside the gradient area should be filled. The
1167 default value is \c ShapeGradient.PadSpread.
1168
1169 \value ShapeGradient.PadSpread
1170 The area is filled with the closest stop color.
1171
1172 \value ShapeGradient.RepeatSpread
1173 The gradient is repeated outside the gradient area.
1174
1175 \value ShapeGradient.ReflectSpread
1176 The gradient is reflected outside the gradient area.
1177 */
1178
1179QQuickShapeGradient::SpreadMode QQuickShapeGradient::spread() const
1180{
1181 return m_spread;
1182}
1183
1184void QQuickShapeGradient::setSpread(SpreadMode mode)
1185{
1186 if (m_spread != mode) {
1187 m_spread = mode;
1188 emit spreadChanged();
1189 emit updated();
1190 }
1191}
1192
1193/*!
1194 \qmltype LinearGradient
1195 //! \instantiates QQuickShapeLinearGradient
1196 \inqmlmodule QtQuick.Shapes
1197 \ingroup qtquick-paths
1198 \ingroup qtquick-views
1199 \inherits ShapeGradient
1200 \brief Linear gradient.
1201 \since 5.10
1202
1203 Linear gradients interpolate colors between start and end points in Shape
1204 items. Outside these points the gradient is either padded, reflected or
1205 repeated depending on the spread type.
1206
1207 \note LinearGradient is only supported in combination with Shape items. It
1208 is not compatible with \l Rectangle, as that only supports \l Gradient.
1209
1210 \sa QLinearGradient
1211 */
1212
1213QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent)
1214 : QQuickShapeGradient(parent)
1215{
1216}
1217
1218/*!
1219 \qmlproperty real QtQuick.Shapes::LinearGradient::x1
1220 \qmlproperty real QtQuick.Shapes::LinearGradient::y1
1221 \qmlproperty real QtQuick.Shapes::LinearGradient::x2
1222 \qmlproperty real QtQuick.Shapes::LinearGradient::y2
1223
1224 These properties define the start and end points between which color
1225 interpolation occurs. By default both points are set to (0, 0).
1226 */
1227
1228qreal QQuickShapeLinearGradient::x1() const
1229{
1230 return m_start.x();
1231}
1232
1233void QQuickShapeLinearGradient::setX1(qreal v)
1234{
1235 if (m_start.x() != v) {
1236 m_start.setX(v);
1237 emit x1Changed();
1238 emit updated();
1239 }
1240}
1241
1242qreal QQuickShapeLinearGradient::y1() const
1243{
1244 return m_start.y();
1245}
1246
1247void QQuickShapeLinearGradient::setY1(qreal v)
1248{
1249 if (m_start.y() != v) {
1250 m_start.setY(v);
1251 emit y1Changed();
1252 emit updated();
1253 }
1254}
1255
1256qreal QQuickShapeLinearGradient::x2() const
1257{
1258 return m_end.x();
1259}
1260
1261void QQuickShapeLinearGradient::setX2(qreal v)
1262{
1263 if (m_end.x() != v) {
1264 m_end.setX(v);
1265 emit x2Changed();
1266 emit updated();
1267 }
1268}
1269
1270qreal QQuickShapeLinearGradient::y2() const
1271{
1272 return m_end.y();
1273}
1274
1275void QQuickShapeLinearGradient::setY2(qreal v)
1276{
1277 if (m_end.y() != v) {
1278 m_end.setY(v);
1279 emit y2Changed();
1280 emit updated();
1281 }
1282}
1283
1284/*!
1285 \qmltype RadialGradient
1286 //! \instantiates QQuickShapeRadialGradient
1287 \inqmlmodule QtQuick.Shapes
1288 \ingroup qtquick-paths
1289 \ingroup qtquick-views
1290 \inherits ShapeGradient
1291 \brief Radial gradient.
1292 \since 5.10
1293
1294 Radial gradients interpolate colors between a focal circle and a center
1295 circle in Shape items. Points outside the cone defined by the two circles
1296 will be transparent.
1297
1298 Outside the end points the gradient is either padded, reflected or repeated
1299 depending on the spread type.
1300
1301 Below is an example of a simple radial gradient. Here the colors are
1302 interpolated between the specified point and the end points on a circle
1303 specified by the radius:
1304
1305 \code
1306 fillGradient: RadialGradient {
1307 centerX: 50; centerY: 50
1308 centerRadius: 100
1309 focalX: centerX; focalY: centerY
1310 GradientStop { position: 0; color: "blue" }
1311 GradientStop { position: 0.2; color: "green" }
1312 GradientStop { position: 0.4; color: "red" }
1313 GradientStop { position: 0.6; color: "yellow" }
1314 GradientStop { position: 1; color: "cyan" }
1315 }
1316 \endcode
1317
1318 \image shape-radial-gradient.png
1319
1320 Extended radial gradients, where a separate focal circle is specified, are
1321 also supported.
1322
1323 \note RadialGradient is only supported in combination with Shape items. It
1324 is not compatible with \l Rectangle, as that only supports \l Gradient.
1325
1326 \sa QRadialGradient
1327 */
1328
1329QQuickShapeRadialGradient::QQuickShapeRadialGradient(QObject *parent)
1330 : QQuickShapeGradient(parent)
1331{
1332}
1333
1334/*!
1335 \qmlproperty real QtQuick.Shapes::RadialGradient::centerX
1336 \qmlproperty real QtQuick.Shapes::RadialGradient::centerY
1337 \qmlproperty real QtQuick.Shapes::RadialGradient::focalX
1338 \qmlproperty real QtQuick.Shapes::RadialGradient::focalY
1339
1340 These properties define the center and focal points. To specify a simple
1341 radial gradient, set focalX and focalY to the value of centerX and
1342 centerY, respectively.
1343 */
1344
1345qreal QQuickShapeRadialGradient::centerX() const
1346{
1347 return m_centerPoint.x();
1348}
1349
1350void QQuickShapeRadialGradient::setCenterX(qreal v)
1351{
1352 if (m_centerPoint.x() != v) {
1353 m_centerPoint.setX(v);
1354 emit centerXChanged();
1355 emit updated();
1356 }
1357}
1358
1359qreal QQuickShapeRadialGradient::centerY() const
1360{
1361 return m_centerPoint.y();
1362}
1363
1364void QQuickShapeRadialGradient::setCenterY(qreal v)
1365{
1366 if (m_centerPoint.y() != v) {
1367 m_centerPoint.setY(v);
1368 emit centerYChanged();
1369 emit updated();
1370 }
1371}
1372
1373/*!
1374 \qmlproperty real QtQuick.Shapes::RadialGradient::centerRadius
1375 \qmlproperty real QtQuick.Shapes::RadialGradient::focalRadius
1376
1377 These properties define the center and focal radius. For simple radial
1378 gradients, focalRadius should be set to \c 0 (the default value).
1379 */
1380
1381qreal QQuickShapeRadialGradient::centerRadius() const
1382{
1383 return m_centerRadius;
1384}
1385
1386void QQuickShapeRadialGradient::setCenterRadius(qreal v)
1387{
1388 if (m_centerRadius != v) {
1389 m_centerRadius = v;
1390 emit centerRadiusChanged();
1391 emit updated();
1392 }
1393}
1394
1395qreal QQuickShapeRadialGradient::focalX() const
1396{
1397 return m_focalPoint.x();
1398}
1399
1400void QQuickShapeRadialGradient::setFocalX(qreal v)
1401{
1402 if (m_focalPoint.x() != v) {
1403 m_focalPoint.setX(v);
1404 emit focalXChanged();
1405 emit updated();
1406 }
1407}
1408
1409qreal QQuickShapeRadialGradient::focalY() const
1410{
1411 return m_focalPoint.y();
1412}
1413
1414void QQuickShapeRadialGradient::setFocalY(qreal v)
1415{
1416 if (m_focalPoint.y() != v) {
1417 m_focalPoint.setY(v);
1418 emit focalYChanged();
1419 emit updated();
1420 }
1421}
1422
1423qreal QQuickShapeRadialGradient::focalRadius() const
1424{
1425 return m_focalRadius;
1426}
1427
1428void QQuickShapeRadialGradient::setFocalRadius(qreal v)
1429{
1430 if (m_focalRadius != v) {
1431 m_focalRadius = v;
1432 emit focalRadiusChanged();
1433 emit updated();
1434 }
1435}
1436
1437/*!
1438 \qmltype ConicalGradient
1439 //! \instantiates QQuickShapeConicalGradient
1440 \inqmlmodule QtQuick.Shapes
1441 \ingroup qtquick-paths
1442 \ingroup qtquick-views
1443 \inherits ShapeGradient
1444 \brief Conical gradient.
1445 \since 5.10
1446
1447 Conical gradients interpolate colors counter-clockwise around a center
1448 point in Shape items.
1449
1450 \note The \l{ShapeGradient::spread}{spread mode} setting has no effect for
1451 conical gradients.
1452
1453 \note ConicalGradient is only supported in combination with Shape items. It
1454 is not compatible with \l Rectangle, as that only supports \l Gradient.
1455
1456 \sa QConicalGradient
1457 */
1458
1459QQuickShapeConicalGradient::QQuickShapeConicalGradient(QObject *parent)
1460 : QQuickShapeGradient(parent)
1461{
1462}
1463
1464/*!
1465 \qmlproperty real QtQuick.Shapes::ConicalGradient::centerX
1466 \qmlproperty real QtQuick.Shapes::ConicalGradient::centerY
1467
1468 These properties define the center point of the conical gradient.
1469 */
1470
1471qreal QQuickShapeConicalGradient::centerX() const
1472{
1473 return m_centerPoint.x();
1474}
1475
1476void QQuickShapeConicalGradient::setCenterX(qreal v)
1477{
1478 if (m_centerPoint.x() != v) {
1479 m_centerPoint.setX(v);
1480 emit centerXChanged();
1481 emit updated();
1482 }
1483}
1484
1485qreal QQuickShapeConicalGradient::centerY() const
1486{
1487 return m_centerPoint.y();
1488}
1489
1490void QQuickShapeConicalGradient::setCenterY(qreal v)
1491{
1492 if (m_centerPoint.y() != v) {
1493 m_centerPoint.setY(v);
1494 emit centerYChanged();
1495 emit updated();
1496 }
1497}
1498
1499/*!
1500 \qmlproperty real QtQuick.Shapes::ConicalGradient::angle
1501
1502 This property defines the start angle for the conical gradient. The value
1503 is in degrees (0-360).
1504 */
1505
1506qreal QQuickShapeConicalGradient::angle() const
1507{
1508 return m_angle;
1509}
1510
1511void QQuickShapeConicalGradient::setAngle(qreal v)
1512{
1513 if (m_angle != v) {
1514 m_angle = v;
1515 emit angleChanged();
1516 emit updated();
1517 }
1518}
1519
1520static void generateGradientColorTable(const QQuickShapeGradientCacheKey &gradient,
1521 uint *colorTable, int size, float opacity)
1522{
1523 int pos = 0;
1524 const QGradientStops &s = gradient.stops;
1525 const bool colorInterpolation = true;
1526
1527 uint alpha = qRound(d: opacity * 256);
1528 uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha);
1529 qreal incr = 1.0 / qreal(size);
1530 qreal fpos = 1.5 * incr;
1531 colorTable[pos++] = ARGB2RGBA(x: qPremultiply(x: current_color));
1532
1533 while (fpos <= s.first().first) {
1534 colorTable[pos] = colorTable[pos - 1];
1535 pos++;
1536 fpos += incr;
1537 }
1538
1539 if (colorInterpolation)
1540 current_color = qPremultiply(x: current_color);
1541
1542 const int sLast = s.size() - 1;
1543 for (int i = 0; i < sLast; ++i) {
1544 qreal delta = 1/(s[i+1].first - s[i].first);
1545 uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha);
1546 if (colorInterpolation)
1547 next_color = qPremultiply(x: next_color);
1548
1549 while (fpos < s[i+1].first && pos < size) {
1550 int dist = int(256 * ((fpos - s[i].first) * delta));
1551 int idist = 256 - dist;
1552 if (colorInterpolation)
1553 colorTable[pos] = ARGB2RGBA(x: INTERPOLATE_PIXEL_256(x: current_color, a: idist, y: next_color, b: dist));
1554 else
1555 colorTable[pos] = ARGB2RGBA(x: qPremultiply(x: INTERPOLATE_PIXEL_256(x: current_color, a: idist, y: next_color, b: dist)));
1556 ++pos;
1557 fpos += incr;
1558 }
1559 current_color = next_color;
1560 }
1561
1562 Q_ASSERT(s.size() > 0);
1563
1564 uint last_color = ARGB2RGBA(x: qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));
1565 for ( ; pos < size; ++pos)
1566 colorTable[pos] = last_color;
1567
1568 colorTable[size-1] = last_color;
1569}
1570
1571QQuickShapeGradientCache::~QQuickShapeGradientCache()
1572{
1573 qDeleteAll(c: m_textures);
1574}
1575
1576QQuickShapeGradientCache *QQuickShapeGradientCache::cacheForRhi(QRhi *rhi)
1577{
1578 static QHash<QRhi *, QQuickShapeGradientCache *> caches;
1579 auto it = caches.constFind(key: rhi);
1580 if (it != caches.constEnd())
1581 return *it;
1582
1583 QQuickShapeGradientCache *cache = new QQuickShapeGradientCache;
1584 rhi->addCleanupCallback(callback: [cache](QRhi *rhi) {
1585 caches.remove(key: rhi);
1586 delete cache;
1587 });
1588 caches.insert(key: rhi, value: cache);
1589 return cache;
1590}
1591
1592QSGTexture *QQuickShapeGradientCache::get(const QQuickShapeGradientCacheKey &grad)
1593{
1594 QSGPlainTexture *tx = m_textures[grad];
1595 if (!tx) {
1596 static const int W = 1024; // texture size is 1024x1
1597 QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied);
1598 generateGradientColorTable(gradient: grad, colorTable: reinterpret_cast<uint *>(gradTab.bits()), size: W, opacity: 1.0f);
1599 tx = new QSGPlainTexture;
1600 tx->setImage(gradTab);
1601 switch (grad.spread) {
1602 case QQuickShapeGradient::PadSpread:
1603 tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1604 tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
1605 break;
1606 case QQuickShapeGradient::RepeatSpread:
1607 tx->setHorizontalWrapMode(QSGTexture::Repeat);
1608 tx->setVerticalWrapMode(QSGTexture::Repeat);
1609 break;
1610 case QQuickShapeGradient::ReflectSpread:
1611 tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
1612 tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
1613 break;
1614 default:
1615 qWarning(msg: "Unknown gradient spread mode %d", grad.spread);
1616 break;
1617 }
1618 tx->setFiltering(QSGTexture::Linear);
1619 m_textures[grad] = tx;
1620 }
1621 return tx;
1622}
1623
1624#if QT_CONFIG(opengl)
1625
1626// contexts sharing with each other get the same cache instance
1627class QQuickShapeGradientCacheWrapper
1628{
1629public:
1630 QQuickShapeGradientOpenGLCache *get(QOpenGLContext *context)
1631 {
1632 return m_resource.value<QQuickShapeGradientOpenGLCache>(context);
1633 }
1634
1635private:
1636 QOpenGLMultiGroupSharedResource m_resource;
1637};
1638
1639QQuickShapeGradientOpenGLCache *QQuickShapeGradientOpenGLCache::currentCache()
1640{
1641 static QQuickShapeGradientCacheWrapper qt_path_gradient_caches;
1642 return qt_path_gradient_caches.get(context: QOpenGLContext::currentContext());
1643}
1644
1645// let QOpenGLContext manage the lifetime of the cached textures
1646QQuickShapeGradientOpenGLCache::~QQuickShapeGradientOpenGLCache()
1647{
1648 m_cache.clear();
1649}
1650
1651void QQuickShapeGradientOpenGLCache::invalidateResource()
1652{
1653 m_cache.clear();
1654}
1655
1656void QQuickShapeGradientOpenGLCache::freeResource(QOpenGLContext *)
1657{
1658 qDeleteAll(c: m_cache);
1659 m_cache.clear();
1660}
1661
1662QSGTexture *QQuickShapeGradientOpenGLCache::get(const QQuickShapeGradientCacheKey &grad)
1663{
1664 QSGPlainTexture *tx = m_cache[grad];
1665 if (!tx) {
1666 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
1667 GLuint id;
1668 f->glGenTextures(n: 1, textures: &id);
1669 f->glBindTexture(GL_TEXTURE_2D, texture: id);
1670 static const uint W = 1024; // texture size is 1024x1
1671 uint buf[W];
1672 generateGradientColorTable(gradient: grad, colorTable: buf, size: W, opacity: 1.0f);
1673 f->glTexImage2D(GL_TEXTURE_2D, level: 0, GL_RGBA, width: W, height: 1, border: 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels: buf);
1674 tx = new QSGPlainTexture;
1675 tx->setTextureId(id);
1676 switch (grad.spread) {
1677 case QQuickShapeGradient::PadSpread:
1678 tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1679 tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
1680 break;
1681 case QQuickShapeGradient::RepeatSpread:
1682 tx->setHorizontalWrapMode(QSGTexture::Repeat);
1683 tx->setVerticalWrapMode(QSGTexture::Repeat);
1684 break;
1685 case QQuickShapeGradient::ReflectSpread:
1686 tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
1687 tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
1688 break;
1689 default:
1690 qWarning(msg: "Unknown gradient spread mode %d", grad.spread);
1691 break;
1692 }
1693 tx->setFiltering(QSGTexture::Linear);
1694 m_cache[grad] = tx;
1695 }
1696 return tx;
1697}
1698
1699#endif // QT_CONFIG(opengl)
1700
1701QT_END_NAMESPACE
1702
1703#include "moc_qquickshape_p.cpp"
1704

source code of qtdeclarative/src/quickshapes/qquickshape.cpp