1// Copyright (C) 2016 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 "qquickshape_p.h"
5#include "qquickshape_p_p.h"
6#include "qquickshapegenericrenderer_p.h"
7#include "qquickshapesoftwarerenderer_p.h"
8#include "qquickshapecurverenderer_p.h"
9#include <private/qsgplaintexture_p.h>
10#include <private/qquicksvgparser_p.h>
11#include <QtGui/private/qdrawhelper_p.h>
12#include <QOpenGLFunctions>
13#include <QLoggingCategory>
14#include <rhi/qrhi.h>
15
16static void initResources()
17{
18#if defined(QT_STATIC)
19 Q_INIT_RESOURCE(qtquickshapes_shaders);
20#endif
21}
22
23QT_BEGIN_NAMESPACE
24
25Q_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync")
26
27/*!
28 \keyword Qt Quick Shapes
29 \qmlmodule QtQuick.Shapes 1.\QtMinorVersion
30 \title Qt Quick Shapes QML Types
31 \ingroup qmlmodules
32 \brief Provides QML types for drawing stroked and filled shapes.
33
34 To use the types in this module, import the module with the following line:
35
36 \qml
37 import QtQuick.Shapes
38 \endqml
39
40 Qt Quick Shapes provides tools for drawing arbitrary shapes in a Qt Quick scene.
41 \l{Shape}{Shapes} can be constructed from basic building blocks like \l{PathLine}{lines} and
42 \l{PathCubic}{curves} that define sub-shapes. The sub-shapes can then be filled with solid
43 colors or gradients, and an outline stroke can be defined.
44
45 Qt Quick Shapes also supports higher level path element types, such as \l{PathText}{text} and
46 \l{PathSvg}{SVG path descriptions}. The currently supported element types is: PathMove,
47 PathLine, PathQuad, PathCubic, PathArc, PathText and PathSvg.
48
49 Qt Quick Shapes triangulates the shapes and renders the corresponding triangles on the GPU.
50 Therefore, altering the control points of elements will lead to re-triangulation of the
51 affected paths, at some performance cost. In addition, curves are flattened before they are
52 rendered, so applying a very high scale to the shape may show artifacts where it is visible
53 that the curves are represented by a sequence of smaller, straight lines.
54
55 \note By default, Qt Quick Shapes relies on multi-sampling for anti-aliasing. This can be
56 enabled for the entire application or window using the corresponding settings in QSurfaceFormat.
57 It can also be enabled for only the shape, by setting its \l{Item::layer.enabled}{layer.enabled}
58 property to true and then adjusting the \l{Item::layer.samples}{layer.samples} property. In the
59 latter case, multi-sampling will not be applied to the entire scene, but the shape will be
60 rendered via an intermediate off-screen buffer. Alternatively, the
61 \l{QtQuick.Shapes::Shape::preferredRendererType}{preferredRendererType} property can be set
62 to \c{Shape.CurveRenderer}. This has anti-aliasing built in and generally renders the shapes
63 at a higher quality, but at some additional performance cost.
64
65 For further information, the \l{Qt Quick Examples - Shapes}{Shapes example} shows how to
66 implement different types of shapes, fills and strokes, and the \l{Weather Forecast Example}
67 shows examples of different ways shapes might be useful in a user interface.
68*/
69
70void QQuickShapes_initializeModule()
71{
72 QQuickShapesModule::defineModule();
73}
74
75Q_CONSTRUCTOR_FUNCTION(QQuickShapes_initializeModule)
76
77void QQuickShapesModule::defineModule()
78{
79 initResources();
80}
81
82QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams()
83 : strokeColor(Qt::white),
84 strokeWidth(1),
85 fillColor(Qt::white),
86 fillRule(QQuickShapePath::OddEvenFill),
87 joinStyle(QQuickShapePath::BevelJoin),
88 miterLimit(2),
89 capStyle(QQuickShapePath::SquareCap),
90 strokeStyle(QQuickShapePath::SolidLine),
91 dashOffset(0),
92 fillGradient(nullptr),
93 fillItem(nullptr)
94{
95 dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space
96}
97
98/*!
99 \qmltype ShapePath
100 //! \nativetype QQuickShapePath
101 \inqmlmodule QtQuick.Shapes
102 \ingroup qtquick-paths
103 \ingroup qtquick-views
104 \inherits Path
105 \brief Describes a Path and associated properties for stroking and filling.
106 \since 5.10
107
108 A \l Shape contains one or more ShapePath elements. At least one ShapePath is
109 necessary in order to have a Shape output anything visible. A ShapePath
110 itself is a \l Path with additional properties describing the stroking and
111 filling parameters, such as the stroke width and color, the fill color or
112 gradient, join and cap styles, and so on. As with ordinary \l Path objects,
113 ShapePath also contains a list of path elements like \l PathMove, \l PathLine,
114 \l PathCubic, \l PathQuad, \l PathArc, together with a starting position.
115
116 Any property changes in these data sets will be bubble up and change the
117 output of the Shape. This means that it is simple and easy to change, or
118 even animate, the starting and ending position, control points, or any
119 stroke or fill parameters using the usual QML bindings and animation types
120 like NumberAnimation.
121
122 In the following example the line join style changes automatically based on
123 the value of joinStyleIndex:
124
125 \qml
126 ShapePath {
127 strokeColor: "black"
128 strokeWidth: 16
129 fillColor: "transparent"
130 capStyle: ShapePath.RoundCap
131
132 property int joinStyleIndex: 0
133
134 property variant styles: [
135 ShapePath.BevelJoin,
136 ShapePath.MiterJoin,
137 ShapePath.RoundJoin
138 ]
139
140 joinStyle: styles[joinStyleIndex]
141
142 startX: 30
143 startY: 30
144 PathLine { x: 100; y: 100 }
145 PathLine { x: 30; y: 100 }
146 }
147 \endqml
148
149 Once associated with a Shape, here is the output with a joinStyleIndex
150 of 2 (ShapePath.RoundJoin):
151
152 \image visualpath-code-example.png
153
154 \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Shape
155 */
156
157QQuickShapePathPrivate::QQuickShapePathPrivate()
158 : dirty(DirtyAll)
159{
160 // Set this QQuickPath to be a ShapePath
161 isShapePath = true;
162}
163
164QQuickShapePath::QQuickShapePath(QObject *parent)
165 : QQuickPath(*(new QQuickShapePathPrivate), parent)
166{
167 // The inherited changed() and the shapePathChanged() signals remain
168 // distinct, and this is intentional. Combining the two is not possible due
169 // to the difference in semantics and the need to act (see dirty flag
170 // below) differently on QQuickPath-related changes.
171
172 connect(sender: this, signal: &QQuickPath::changed, slot: [this]() {
173 Q_D(QQuickShapePath);
174 d->dirty |= QQuickShapePathPrivate::DirtyPath;
175 emit shapePathChanged();
176 });
177}
178
179QQuickShapePath::~QQuickShapePath()
180{
181}
182
183/*!
184 \qmlproperty color QtQuick.Shapes::ShapePath::strokeColor
185
186 This property holds the stroking color.
187
188 When set to \c transparent, no stroking occurs.
189
190 The default value is \c white.
191 */
192
193QColor QQuickShapePath::strokeColor() const
194{
195 Q_D(const QQuickShapePath);
196 return d->sfp.strokeColor;
197}
198
199void QQuickShapePath::setStrokeColor(const QColor &color)
200{
201 Q_D(QQuickShapePath);
202 if (d->sfp.strokeColor != color) {
203 d->sfp.strokeColor = color;
204 d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor;
205 emit strokeColorChanged();
206 emit shapePathChanged();
207 }
208}
209
210/*!
211 \qmlproperty real QtQuick.Shapes::ShapePath::strokeWidth
212
213 This property holds the stroke width.
214
215 When set to a negative value, no stroking occurs.
216
217 The default value is 1.
218 */
219
220qreal QQuickShapePath::strokeWidth() const
221{
222 Q_D(const QQuickShapePath);
223 return d->sfp.strokeWidth;
224}
225
226void QQuickShapePath::setStrokeWidth(qreal w)
227{
228 Q_D(QQuickShapePath);
229 if (d->sfp.strokeWidth != w) {
230 d->sfp.strokeWidth = w;
231 d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth;
232 emit strokeWidthChanged();
233 emit shapePathChanged();
234 }
235}
236
237/*!
238 \qmlproperty color QtQuick.Shapes::ShapePath::fillColor
239
240 This property holds the fill color.
241
242 When set to \c transparent, no filling occurs.
243
244 The default value is \c white.
245
246 \note If either \l fillGradient or \l fillItem are set to something other than \c null, these
247 will take precedence over \c fillColor. The \c fillColor will be ignored in this case.
248 */
249
250QColor QQuickShapePath::fillColor() const
251{
252 Q_D(const QQuickShapePath);
253 return d->sfp.fillColor;
254}
255
256void QQuickShapePath::setFillColor(const QColor &color)
257{
258 Q_D(QQuickShapePath);
259 if (d->sfp.fillColor != color) {
260 d->sfp.fillColor = color;
261 d->dirty |= QQuickShapePathPrivate::DirtyFillColor;
262 emit fillColorChanged();
263 emit shapePathChanged();
264 }
265}
266
267/*!
268 \qmlproperty enumeration QtQuick.Shapes::ShapePath::fillRule
269
270 This property holds the fill rule. The default value is
271 \c ShapePath.OddEvenFill. For an explanation on fill rules, see
272 QPainterPath::setFillRule().
273
274 \value ShapePath.OddEvenFill
275 Odd-even fill rule.
276
277 \value ShapePath.WindingFill
278 Non-zero winding fill rule.
279 */
280
281QQuickShapePath::FillRule QQuickShapePath::fillRule() const
282{
283 Q_D(const QQuickShapePath);
284 return d->sfp.fillRule;
285}
286
287void QQuickShapePath::setFillRule(FillRule fillRule)
288{
289 Q_D(QQuickShapePath);
290 if (d->sfp.fillRule != fillRule) {
291 d->sfp.fillRule = fillRule;
292 d->dirty |= QQuickShapePathPrivate::DirtyFillRule;
293 emit fillRuleChanged();
294 emit shapePathChanged();
295 }
296}
297
298/*!
299 \qmlproperty enumeration QtQuick.Shapes::ShapePath::joinStyle
300
301 This property defines how joins between two connected lines are drawn. The
302 default value is \c ShapePath.BevelJoin.
303
304 \value ShapePath.MiterJoin
305 The outer edges of the lines are extended to meet at an angle, and
306 this area is filled.
307
308 \value ShapePath.BevelJoin
309 The triangular notch between the two lines is filled.
310
311 \value ShapePath.RoundJoin
312 A circular arc between the two lines is filled.
313 */
314
315QQuickShapePath::JoinStyle QQuickShapePath::joinStyle() const
316{
317 Q_D(const QQuickShapePath);
318 return d->sfp.joinStyle;
319}
320
321void QQuickShapePath::setJoinStyle(JoinStyle style)
322{
323 Q_D(QQuickShapePath);
324 if (d->sfp.joinStyle != style) {
325 d->sfp.joinStyle = style;
326 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
327 emit joinStyleChanged();
328 emit shapePathChanged();
329 }
330}
331
332/*!
333 \qmlproperty int QtQuick.Shapes::ShapePath::miterLimit
334
335 When joinStyle is set to \c ShapePath.MiterJoin, this property
336 specifies how far the miter join can extend from the join point.
337
338 The default value is 2.
339 */
340
341int QQuickShapePath::miterLimit() const
342{
343 Q_D(const QQuickShapePath);
344 return d->sfp.miterLimit;
345}
346
347void QQuickShapePath::setMiterLimit(int limit)
348{
349 Q_D(QQuickShapePath);
350 if (d->sfp.miterLimit != limit) {
351 d->sfp.miterLimit = limit;
352 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
353 emit miterLimitChanged();
354 emit shapePathChanged();
355 }
356}
357
358/*!
359 \qmlproperty enumeration QtQuick.Shapes::ShapePath::capStyle
360
361 This property defines how the end points of lines are drawn. The
362 default value is \c ShapePath.SquareCap.
363
364 \value ShapePath.FlatCap
365 A square line end that does not cover the end point of the line.
366
367 \value ShapePath.SquareCap
368 A square line end that covers the end point and extends beyond it
369 by half the line width.
370
371 \value ShapePath.RoundCap
372 A rounded line end.
373 */
374
375QQuickShapePath::CapStyle QQuickShapePath::capStyle() const
376{
377 Q_D(const QQuickShapePath);
378 return d->sfp.capStyle;
379}
380
381void QQuickShapePath::setCapStyle(CapStyle style)
382{
383 Q_D(QQuickShapePath);
384 if (d->sfp.capStyle != style) {
385 d->sfp.capStyle = style;
386 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
387 emit capStyleChanged();
388 emit shapePathChanged();
389 }
390}
391
392/*!
393 \qmlproperty enumeration QtQuick.Shapes::ShapePath::strokeStyle
394
395 This property defines the style of stroking. The default value is
396 ShapePath.SolidLine.
397
398 \value ShapePath.SolidLine A plain line.
399 \value ShapePath.DashLine Dashes separated by a few pixels.
400 */
401
402QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const
403{
404 Q_D(const QQuickShapePath);
405 return d->sfp.strokeStyle;
406}
407
408void QQuickShapePath::setStrokeStyle(StrokeStyle style)
409{
410 Q_D(QQuickShapePath);
411 if (d->sfp.strokeStyle != style) {
412 d->sfp.strokeStyle = style;
413 d->dirty |= QQuickShapePathPrivate::DirtyDash;
414 emit strokeStyleChanged();
415 emit shapePathChanged();
416 }
417}
418
419/*!
420 \qmlproperty real QtQuick.Shapes::ShapePath::dashOffset
421
422 This property defines the starting point on the dash pattern, measured in
423 units used to specify the dash pattern.
424
425 The default value is 0.
426
427 \sa QPen::setDashOffset()
428 */
429
430qreal QQuickShapePath::dashOffset() const
431{
432 Q_D(const QQuickShapePath);
433 return d->sfp.dashOffset;
434}
435
436void QQuickShapePath::setDashOffset(qreal offset)
437{
438 Q_D(QQuickShapePath);
439 if (d->sfp.dashOffset != offset) {
440 d->sfp.dashOffset = offset;
441 d->dirty |= QQuickShapePathPrivate::DirtyDash;
442 emit dashOffsetChanged();
443 emit shapePathChanged();
444 }
445}
446
447/*!
448 \qmlproperty list<real> QtQuick.Shapes::ShapePath::dashPattern
449
450 This property defines the dash pattern when ShapePath.strokeStyle is set
451 to ShapePath.DashLine. The pattern must be specified as an even number of
452 positive entries where the entries 1, 3, 5... are the dashes and 2, 4,
453 6... are the spaces. The pattern is specified in units of the pen's width.
454
455 The default value is (4, 2), meaning a dash of 4 * ShapePath.strokeWidth
456 pixels followed by a space of 2 * ShapePath.strokeWidth pixels.
457
458 \sa QPen::setDashPattern()
459 */
460
461QVector<qreal> QQuickShapePath::dashPattern() const
462{
463 Q_D(const QQuickShapePath);
464 return d->sfp.dashPattern;
465}
466
467void QQuickShapePath::setDashPattern(const QVector<qreal> &array)
468{
469 Q_D(QQuickShapePath);
470 if (d->sfp.dashPattern != array) {
471 d->sfp.dashPattern = array;
472 d->dirty |= QQuickShapePathPrivate::DirtyDash;
473 emit dashPatternChanged();
474 emit shapePathChanged();
475 }
476}
477
478/*!
479 \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::fillGradient
480
481 This property defines the fill gradient. By default no gradient is enabled
482 and the value is \c null. In this case the fill will either be based on the \l fillItem
483 property if it is set, and otherwise the \l{fillColor} property will be used.
484
485 \note The Gradient type cannot be used here. Rather, prefer using one of
486 the advanced subtypes, like LinearGradient.
487
488 \note If set to something other than \c{null}, the \c fillGradient will take precedence over
489 both \l fillItem and \l fillColor.
490 */
491
492QQuickShapeGradient *QQuickShapePath::fillGradient() const
493{
494 Q_D(const QQuickShapePath);
495 return d->sfp.fillGradient;
496}
497
498void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
499{
500 Q_D(QQuickShapePath);
501 if (d->sfp.fillGradient != gradient) {
502 if (d->sfp.fillGradient)
503 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
504 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
505 d->sfp.fillGradient = gradient;
506 if (d->sfp.fillGradient)
507 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
508 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
509 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
510 emit shapePathChanged();
511 }
512}
513
514void QQuickShapePath::resetFillGradient()
515{
516 setFillGradient(nullptr);
517}
518
519void QQuickShapePathPrivate::_q_fillGradientChanged()
520{
521 Q_Q(QQuickShapePath);
522 dirty |= DirtyFillGradient;
523 emit q->shapePathChanged();
524}
525
526/*!
527 \qmlproperty Item QtQuick.Shapes::ShapePath::fillItem
528 \since 6.8
529
530 This property defines another Qt Quick Item to use as fill by the shape. The item must be
531 texture provider (such as a \l {Item Layers} {layered item}, a \l{ShaderEffectSource} or an
532 \l{Image}). If it is not a valid texture provider, this property will be ignored.
533
534 \note When using a layered item as a \c fillItem, you may see pixelation effects when
535 transforming the fill. Setting the \l {QtQuick::Item::}{layer.smooth} property to true will
536 give better visual results in this case.
537
538 By default no fill item is set and the value is \c null.
539
540 \note If set to something other than \c null, the \c fillItem property takes precedence over
541 \l fillColor. The \l fillGradient property in turn takes precedence over both \c fillItem and
542 \l{fillColor}.
543 */
544
545QQuickItem *QQuickShapePath::fillItem() const
546{
547 Q_D(const QQuickShapePath);
548 return d->sfp.fillItem;
549}
550
551void QQuickShapePath::setFillItem(QQuickItem *fillItem)
552{
553 Q_D(QQuickShapePath);
554 if (d->sfp.fillItem != fillItem) {
555 if (d->sfp.fillItem != nullptr) {
556 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
557 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
558 }
559 d->sfp.fillItem = fillItem;
560 if (d->sfp.fillItem != nullptr) {
561 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
562 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
563 }
564 emit fillItemChanged();
565
566 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
567 emit shapePathChanged();
568 }
569}
570
571void QQuickShapePathPrivate::_q_fillItemDestroyed()
572{
573 Q_Q(QQuickShapePath);
574 sfp.fillItem = nullptr;
575 dirty |= DirtyFillItem;
576 emit q->fillItemChanged();
577 emit q->shapePathChanged();
578}
579
580/*!
581 \qmlproperty PathHints QtQuick.Shapes::ShapePath::pathHints
582 \since 6.7
583
584 This property describes characteristics of the shape. If set, these hints may allow
585 optimized rendering. By default, no hints are set. It can be a combination of the following
586 values:
587
588 \value ShapePath.PathLinear
589 The path only has straight lines, no curves.
590 \value ShapePath.PathQuadratic
591 The path does not have any cubic curves: only lines and quadratic Bezier curves.
592 \value ShapePath.PathConvex
593 The path does not have any dents or holes. All straight lines between two points
594 inside the shape will be completely inside the shape.
595 \value ShapePath.PathFillOnRight
596 The path follows the TrueType convention where outlines around solid fill have their
597 control points ordered clockwise, and outlines around holes in the shape have their
598 control points ordered counter-clockwise.
599 \value ShapePath.PathSolid
600 The path has no holes, or mathematically speaking it is \e{simply connected}.
601 \value ShapePath.PathNonIntersecting
602 The path outline does not cross itself.
603 \value ShapePath.PathNonOverlappingControlPointTriangles
604 The triangles defined by the curve control points do not overlap with each other,
605 or with any of the line segments. Also, no line segments intersect.
606 This implies \c PathNonIntersecting.
607
608 Not all hints are logically independent, but the dependencies are not enforced.
609 For example, \c PathLinear implies \c PathQuadratic, but it is valid to have \c PathLinear
610 without \c PathQuadratic.
611
612 The pathHints property describes a set of statements known to be true; the absence of a hint
613 does not necessarily mean that the corresponding statement is false.
614*/
615
616QQuickShapePath::PathHints QQuickShapePath::pathHints() const
617{
618 Q_D(const QQuickShapePath);
619 return d->pathHints;
620}
621
622void QQuickShapePath::setPathHints(PathHints newPathHints)
623{
624 Q_D(QQuickShapePath);
625 if (d->pathHints == newPathHints)
626 return;
627 d->pathHints = newPathHints;
628 emit pathHintsChanged();
629}
630
631/*!
632 \qmlproperty matrix4x4 QtQuick.Shapes::ShapePath::fillTransform
633 \since 6.8
634
635 This property defines a transform to be applied to the path's fill pattern (\l fillGradient or
636 \l fillItem). It has no effect if the fill is a solid color or transparent. By default no fill
637 transform is enabled and the value of this property is the \c identity matrix.
638
639 This example displays a rectangle filled with the contents of \c myImageItem rotated 45 degrees
640 around the center point of \c myShape:
641
642 \qml
643 ShapePath {
644 fillItem: myImageItem
645 fillTransform: PlanarTransform.fromRotate(45, myShape.width / 2, myShape.height / 2)
646 PathRectangle { x: 10; y: 10; width: myShape.width - 20; height: myShape.height - 20 }
647 }
648 \endqml
649*/
650
651QMatrix4x4 QQuickShapePath::fillTransform() const
652{
653 Q_D(const QQuickShapePath);
654 return d->sfp.fillTransform.matrix();
655}
656
657void QQuickShapePath::setFillTransform(const QMatrix4x4 &matrix)
658{
659 Q_D(QQuickShapePath);
660 if (d->sfp.fillTransform != matrix) {
661 d->sfp.fillTransform.setMatrix(matrix);
662 d->dirty |= QQuickShapePathPrivate::DirtyFillTransform;
663 emit fillTransformChanged();
664 emit shapePathChanged();
665 }
666}
667
668/*!
669 \qmltype Shape
670 //! \nativetype QQuickShape
671 \inqmlmodule QtQuick.Shapes
672 \ingroup qtquick-paths
673 \ingroup qtquick-views
674 \inherits Item
675 \brief Renders a path.
676 \since 5.10
677
678 Renders a path by triangulating geometry from a QPainterPath.
679
680 This approach is different from rendering shapes via QQuickPaintedItem or
681 the 2D Canvas because the path never gets rasterized in software.
682 Therefore Shape is suitable for creating shapes spreading over larger
683 areas of the screen, avoiding the performance penalty for texture uploads
684 or framebuffer blits. In addition, the declarative API allows manipulating,
685 binding to, and even animating the path element properties like starting
686 and ending position, the control points, and so on.
687
688 The types for specifying path elements are shared between \l PathView and
689 Shape. However, not all Shape implementations support all path
690 element types, while some may not make sense for PathView. Shape's
691 currently supported subset is: PathMove, PathLine, PathQuad, PathCubic,
692 PathArc, PathText and PathSvg.
693
694 See \l Path for a detailed overview of the supported path elements.
695
696 \qml
697 Shape {
698 width: 200
699 height: 150
700 anchors.centerIn: parent
701 ShapePath {
702 strokeWidth: 4
703 strokeColor: "red"
704 fillGradient: LinearGradient {
705 x1: 20; y1: 20
706 x2: 180; y2: 130
707 GradientStop { position: 0; color: "blue" }
708 GradientStop { position: 0.2; color: "green" }
709 GradientStop { position: 0.4; color: "red" }
710 GradientStop { position: 0.6; color: "yellow" }
711 GradientStop { position: 1; color: "cyan" }
712 }
713 strokeStyle: ShapePath.DashLine
714 dashPattern: [ 1, 4 ]
715 startX: 20; startY: 20
716 PathLine { x: 180; y: 130 }
717 PathLine { x: 20; y: 130 }
718 PathLine { x: 20; y: 20 }
719 }
720 }
721 \endqml
722
723 \image pathitem-code-example.png
724
725 Like \l Item, Shape also allows any visual or non-visual objects to be
726 declared as children. ShapePath objects are handled specially. This is
727 useful since it allows adding visual items, like \l Rectangle or \l Image,
728 and non-visual objects, like \l Timer directly as children of Shape.
729
730 The following list summarizes the available Shape rendering approaches:
731
732 \list
733
734 \li When Qt Quick is running with the default, hardware-accelerated backend (RHI),
735 the generic shape renderer will be used. This converts the shapes into triangles
736 which are passed to the renderer.
737
738 \li The \c software backend is fully supported. The path is rendered via
739 QPainter::strokePath() and QPainter::fillPath() in this case.
740
741 \li The OpenVG backend is not currently supported.
742
743 \endlist
744
745 When using Shape, it is important to be aware of potential performance
746 implications:
747
748 \list
749
750 \li When the application is running with the generic, triangulation-based
751 Shape implementation, the geometry generation happens entirely on the
752 CPU. This is potentially expensive. Changing the set of path elements,
753 changing the properties of these elements, or changing certain properties
754 of the Shape itself all lead to retriangulation of the affected paths on
755 every change. Therefore, applying animation to such properties can affect
756 performance on less powerful systems.
757
758 \li However, the data-driven, declarative nature of the Shape API often
759 means better cacheability for the underlying CPU and GPU resources. A
760 property change in one ShapePath will only lead to reprocessing the
761 affected ShapePath, leaving other parts of the Shape unchanged. Therefore,
762 a frequently changing property can still result in a lower overall system
763 load than with imperative painting approaches (for example, QPainter).
764
765 \li At the same time, attention must be paid to the number of Shape
766 elements in the scene. The way such a Shape item is represented in
767 the scene graph is different from an ordinary geometry-based item,
768 and incurs a certain cost when it comes to OpenGL state changes.
769
770 \li As a general rule, scenes should avoid using separate Shape items when
771 it is not absolutely necessary. Prefer using one Shape item with multiple
772 ShapePath elements over multiple Shape items.
773
774 \endlist
775
776 \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
777*/
778
779QQuickShapePrivate::QQuickShapePrivate()
780 : effectRefCount(0)
781{
782}
783
784QQuickShapePrivate::~QQuickShapePrivate()
785{
786 delete renderer;
787}
788
789void QQuickShapePrivate::_q_shapePathChanged()
790{
791 Q_Q(QQuickShape);
792 spChanged = true;
793 q->polish();
794 emit q->boundingRectChanged();
795 auto br = q->boundingRect();
796 q->setImplicitSize(br.right(), br.bottom());
797}
798
799void QQuickShapePrivate::handleSceneChange(QQuickWindow *w)
800{
801 if (renderer != nullptr)
802 renderer->handleSceneChange(window: w);
803}
804
805void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
806{
807 Q_Q(QQuickShape);
808 if (status != newStatus) {
809 status = newStatus;
810 emit q->statusChanged();
811 }
812}
813
814qreal QQuickShapePrivate::getImplicitWidth() const
815{
816 Q_Q(const QQuickShape);
817 return q->boundingRect().right();
818}
819
820qreal QQuickShapePrivate::getImplicitHeight() const
821{
822 Q_Q(const QQuickShape);
823 return q->boundingRect().bottom();
824}
825
826QQuickShape::QQuickShape(QQuickItem *parent)
827 : QQuickItem(*(new QQuickShapePrivate), parent)
828{
829 setFlag(flag: ItemHasContents);
830}
831
832QQuickShape::~QQuickShape()
833{
834}
835
836/*!
837 \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType
838 \readonly
839
840 This property determines which path rendering backend is active.
841
842 \value Shape.UnknownRenderer
843 The renderer is unknown.
844
845 \value Shape.GeometryRenderer
846 The generic, driver independent solution for GPU rendering. Uses the same
847 CPU-based triangulation approach as QPainter's OpenGL 2 paint
848 engine. This is the default when the RHI-based Qt Quick scenegraph
849 backend is in use.
850
851 \value Shape.SoftwareRenderer
852 Pure QPainter drawing using the raster paint engine. This is the
853 default, and only, option when the Qt Quick scenegraph is running
854 with the \c software backend.
855
856 \value Shape.CurveRenderer
857 GPU-based renderer that aims to preserve curvature at any scale.
858 In contrast to \c Shape.GeometryRenderer, curves are not approximated by short straight
859 lines. Instead, curves are rendered using a specialized fragment shader. This improves
860 visual quality and avoids re-tesselation performance hit when zooming. Also,
861 \c Shape.CurveRenderer provides native, high-quality anti-aliasing, without the
862 performance cost of multi- or supersampling.
863
864 By default, \c Shape.GeometryRenderer will be selected unless the Qt Quick scenegraph is running
865 with the \c software backend. In that case, \c Shape.SoftwareRenderer will be used.
866 \c Shape.CurveRenderer may be requested using the \l preferredRendererType property.
867
868 \note The \c Shape.CurveRenderer will approximate cubic curves with quadratic ones and may
869 therefore diverge slightly from the mathematically correct visualization of the shape. In
870 addition, if the shape is being rendered into a Qt Quick 3D scene and the OpenGL backend for
871 RHI is active, the \c GL_OES_standard_derivatives extension to OpenGL is required (this is
872 available by default on OpenGL ES 3 and later, but optional in OpenGL ES 2.)
873*/
874
875QQuickShape::RendererType QQuickShape::rendererType() const
876{
877 Q_D(const QQuickShape);
878 return d->rendererType;
879}
880
881/*!
882 \qmlproperty enumeration QtQuick.Shapes::Shape::preferredRendererType
883 \since 6.6
884
885 Requests a specific backend to use for rendering the shape. The possible values are the same as
886 for \l rendererType. The default is \c Shape.UnknownRenderer, indicating no particular preference.
887
888 If the requested renderer type is not supported for the current Qt Quick backend, the default
889 renderer for that backend will be used instead. This will be reflected in the \l rendererType
890 when the backend is initialized.
891
892 \c Shape.SoftwareRenderer can currently not be selected without running the scenegraph with
893 the \c software backend, in which case it will be selected regardless of the
894 \c preferredRendererType.
895
896 See \l rendererType for more information on the implications.
897*/
898
899QQuickShape::RendererType QQuickShape::preferredRendererType() const
900{
901 Q_D(const QQuickShape);
902 return d->preferredType;
903}
904
905void QQuickShape::setPreferredRendererType(QQuickShape::RendererType preferredType)
906{
907 Q_D(QQuickShape);
908 if (d->preferredType == preferredType)
909 return;
910
911 d->preferredType = preferredType;
912 // (could bail out here if selectRenderType shows no change?)
913
914 for (int i = 0; i < d->sp.size(); ++i) {
915 QQuickShapePath *p = d->sp[i];
916 QQuickShapePathPrivate *pp = QQuickShapePathPrivate::get(p);
917 pp->dirty |= QQuickShapePathPrivate::DirtyAll;
918 }
919 d->spChanged = true;
920 d->_q_shapePathChanged();
921 polish();
922 update();
923
924 emit preferredRendererTypeChanged();
925}
926
927/*!
928 \qmlproperty bool QtQuick.Shapes::Shape::asynchronous
929
930 When rendererType is \c Shape.GeometryRenderer or \c Shape.CurveRenderer, a certain amount of
931 preprocessing of the input path is performed on the CPU during the polishing phase of the
932 Shape. This is potentially expensive. To offload this work to separate worker threads, set this
933 property to \c true.
934
935 When enabled, making a Shape visible will not wait for the content to
936 become available. Instead, the GUI/main thread is not blocked and the
937 results of the path rendering are shown only when all the asynchronous
938 work has been finished.
939
940 The default value is \c false.
941 */
942
943bool QQuickShape::asynchronous() const
944{
945 Q_D(const QQuickShape);
946 return d->async;
947}
948
949void QQuickShape::setAsynchronous(bool async)
950{
951 Q_D(QQuickShape);
952 if (d->async != async) {
953 d->async = async;
954 emit asynchronousChanged();
955 if (d->componentComplete)
956 d->_q_shapePathChanged();
957 }
958}
959
960/*!
961 \qmlproperty rect QtQuick.Shapes::Shape::boundingRect
962 \readonly
963 \since 6.6
964
965 Contains the united bounding rect of all sub paths in the shape.
966 */
967QRectF QQuickShape::boundingRect() const
968{
969 Q_D(const QQuickShape);
970 QRectF brect;
971 for (QQuickShapePath *path : d->sp) {
972 qreal pw = path->strokeColor().alpha() ? path->strokeWidth() : 0;
973 qreal d = path->capStyle() == QQuickShapePath::SquareCap ? pw * M_SQRT1_2 : pw / 2;
974 brect = brect.united(r: path->path().boundingRect().adjusted(xp1: -d, yp1: -d, xp2: d, yp2: d));
975 }
976
977 return brect;
978}
979
980/*!
981 \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
982
983 This property controls the usage of non-standard OpenGL extensions.
984
985 The default value is \c false.
986
987 As of Qt 6.0 there are no vendor-specific rendering paths implemented.
988 */
989
990bool QQuickShape::vendorExtensionsEnabled() const
991{
992 Q_D(const QQuickShape);
993 return d->enableVendorExts;
994}
995
996void QQuickShape::setVendorExtensionsEnabled(bool enable)
997{
998 Q_D(QQuickShape);
999 if (d->enableVendorExts != enable) {
1000 d->enableVendorExts = enable;
1001 emit vendorExtensionsEnabledChanged();
1002 }
1003}
1004
1005/*!
1006 \qmlproperty enumeration QtQuick.Shapes::Shape::status
1007 \readonly
1008
1009 This property determines the status of the Shape and is relevant when
1010 Shape.asynchronous is set to \c true.
1011
1012 \value Shape.Null
1013 Not yet initialized.
1014
1015 \value Shape.Ready
1016 The Shape has finished processing.
1017
1018 \value Shape.Processing
1019 The path is being processed.
1020 */
1021
1022QQuickShape::Status QQuickShape::status() const
1023{
1024 Q_D(const QQuickShape);
1025 return d->status;
1026}
1027
1028/*!
1029 \qmlproperty enumeration QtQuick.Shapes::Shape::containsMode
1030 \since QtQuick.Shapes 1.11
1031
1032 This property determines the definition of \l {QQuickItem::contains()}{contains()}
1033 for the Shape. It is useful in case you add \l {Qt Quick Input Handlers} and you want to
1034 react only when the mouse or touchpoint is fully inside the Shape.
1035
1036 \value Shape.BoundingRectContains
1037 The default implementation of \l QQuickItem::contains() checks only
1038 whether the given point is inside the rectangular bounding box. This is
1039 the most efficient implementation, which is why it's the default.
1040
1041 \value Shape.FillContains
1042 Check whether the interior (the part that would be filled if you are
1043 rendering it with fill) of any \l ShapePath that makes up this Shape
1044 contains the given point. The more complex and numerous ShapePaths you
1045 add, the less efficient this is to check, which can potentially slow
1046 down event delivery in your application. So it should be used with care.
1047
1048 One way to speed up the \c FillContains check is to generate an approximate
1049 outline with as few points as possible, place that in a transparent Shape
1050 on top, and add your Pointer Handlers to that, so that the containment
1051 check is cheaper during event delivery.
1052*/
1053QQuickShape::ContainsMode QQuickShape::containsMode() const
1054{
1055 Q_D(const QQuickShape);
1056 return d->containsMode;
1057}
1058
1059void QQuickShape::setContainsMode(QQuickShape::ContainsMode containsMode)
1060{
1061 Q_D(QQuickShape);
1062 if (d->containsMode == containsMode)
1063 return;
1064
1065 d->containsMode = containsMode;
1066 emit containsModeChanged();
1067}
1068
1069bool QQuickShape::contains(const QPointF &point) const
1070{
1071 Q_D(const QQuickShape);
1072 switch (d->containsMode) {
1073 case BoundingRectContains:
1074 return QQuickItem::contains(point);
1075 case FillContains:
1076 for (QQuickShapePath *path : d->sp) {
1077 if (path->path().contains(pt: point))
1078 return true;
1079 }
1080 }
1081 return false;
1082}
1083
1084/*!
1085 \qmlproperty enumeration QtQuick.Shapes::Shape::fillMode
1086 \since QtQuick.Shapes 6.7
1087
1088 Set this property to define what happens when the path has a different size
1089 than the item.
1090
1091 \value Shape.NoResize the shape is rendered at its native size, independent of the size of the item. This is the default
1092 \value Shape.Stretch the shape is scaled to fit the item, changing the aspect ratio if necessary.
1093 Note that non-uniform scaling may cause reduced quality of anti-aliasing when using the curve renderer
1094 \value Shape.PreserveAspectFit the shape is scaled uniformly to fit inside the item
1095 \value Shape.PreserveAspectCrop the shape is scaled uniformly to fill the item fully, extending outside the item if necessary.
1096 Note that this only actually crops the content if \l clip is true
1097*/
1098
1099QQuickShape::FillMode QQuickShape::fillMode() const
1100{
1101 Q_D(const QQuickShape);
1102 return d->fillMode;
1103}
1104
1105void QQuickShape::setFillMode(FillMode newFillMode)
1106{
1107 Q_D(QQuickShape);
1108 if (d->fillMode == newFillMode)
1109 return;
1110 d->fillMode = newFillMode;
1111 emit fillModeChanged();
1112}
1113
1114/*!
1115 \qmlproperty enumeration QtQuick.Shapes::Shape::horizontalAlignment
1116 \qmlproperty enumeration QtQuick.Shapes::Shape::verticalAlignment
1117 \since 6.7
1118
1119 Sets the horizontal and vertical alignment of the shape within the item.
1120 By default, the shape is aligned with \c{(0,0)} on the top left corner.
1121
1122 The valid values for \c horizontalAlignment are \c Shape.AlignLeft,
1123 \c Shape.AlignRight and \c Shape.AlignHCenter. The valid values for
1124 \c verticalAlignment are \c Shape.AlignTop, \c Shape.AlignBottom and
1125 \c Shape.AlignVCenter.
1126*/
1127
1128QQuickShape::HAlignment QQuickShape::horizontalAlignment() const
1129{
1130 Q_D(const QQuickShape);
1131 return d->horizontalAlignment;
1132}
1133
1134void QQuickShape::setHorizontalAlignment(HAlignment newHorizontalAlignment)
1135{
1136 Q_D(QQuickShape);
1137 if (d->horizontalAlignment == newHorizontalAlignment)
1138 return;
1139 d->horizontalAlignment = newHorizontalAlignment;
1140 emit horizontalAlignmentChanged();
1141}
1142
1143QQuickShape::VAlignment QQuickShape::verticalAlignment() const
1144{
1145 Q_D(const QQuickShape);
1146 return d->verticalAlignment;
1147}
1148
1149void QQuickShape::setVerticalAlignment(VAlignment newVerticalAlignment)
1150{
1151 Q_D(QQuickShape);
1152 if (d->verticalAlignment == newVerticalAlignment)
1153 return;
1154 d->verticalAlignment = newVerticalAlignment;
1155 emit verticalAlignmentChanged();
1156}
1157
1158static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1159{
1160 QQuickShape *item = static_cast<QQuickShape *>(property->object);
1161 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1162 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(object: obj);
1163 if (path)
1164 d->sp.append(t: path);
1165
1166 QQuickItemPrivate::data_append(property, obj);
1167
1168 if (path && d->componentComplete) {
1169 QObject::connect(sender: path, SIGNAL(shapePathChanged()), receiver: item, SLOT(_q_shapePathChanged()));
1170 d->_q_shapePathChanged();
1171 }
1172}
1173
1174static void vpe_clear(QQmlListProperty<QObject> *property)
1175{
1176 QQuickShape *item = static_cast<QQuickShape *>(property->object);
1177 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1178
1179 for (QQuickShapePath *p : d->sp)
1180 QObject::disconnect(sender: p, SIGNAL(shapePathChanged()), receiver: item, SLOT(_q_shapePathChanged()));
1181
1182 d->sp.clear();
1183
1184 QQuickItemPrivate::data_clear(property);
1185
1186 if (d->componentComplete)
1187 d->_q_shapePathChanged();
1188}
1189
1190/*!
1191 \qmlproperty list<Object> QtQuick.Shapes::Shape::data
1192
1193 This property holds the ShapePath objects that define the contents of the
1194 Shape. It can also contain any other type of objects, since Shape, like
1195 Item, allows adding any visual or non-visual objects as children.
1196
1197 \qmldefault
1198 */
1199
1200QQmlListProperty<QObject> QQuickShape::data()
1201{
1202 return QQmlListProperty<QObject>(this,
1203 nullptr,
1204 vpe_append,
1205 QQuickItemPrivate::data_count,
1206 QQuickItemPrivate::data_at,
1207 vpe_clear);
1208}
1209
1210void QQuickShape::classBegin()
1211{
1212 QQuickItem::classBegin();
1213}
1214
1215void QQuickShape::componentComplete()
1216{
1217 Q_D(QQuickShape);
1218
1219 QQuickItem::componentComplete();
1220
1221 for (QQuickShapePath *p : d->sp)
1222 connect(sender: p, SIGNAL(shapePathChanged()), receiver: this, SLOT(_q_shapePathChanged()));
1223
1224 d->_q_shapePathChanged();
1225}
1226
1227void QQuickShape::updatePolish()
1228{
1229 Q_D(QQuickShape);
1230
1231 const int currentEffectRefCount = d->extra.isAllocated() ? d->extra->recursiveEffectRefCount : 0;
1232 if (!d->spChanged && currentEffectRefCount <= d->effectRefCount)
1233 return;
1234
1235 d->spChanged = false;
1236 d->effectRefCount = currentEffectRefCount;
1237
1238 QQuickShape::RendererType expectedRenderer = d->selectRendererType();
1239 if (d->rendererType != expectedRenderer) {
1240 delete d->renderer;
1241 d->renderer = nullptr;
1242 }
1243
1244 if (!d->renderer) {
1245 d->createRenderer();
1246 if (!d->renderer)
1247 return;
1248 emit rendererChanged();
1249 }
1250
1251 // endSync() is where expensive calculations may happen (or get kicked off
1252 // on worker threads), depending on the backend. Therefore do this only
1253 // when the item is visible.
1254 if (isVisible() || d->effectRefCount > 0)
1255 d->sync();
1256}
1257
1258void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data)
1259{
1260 Q_D(QQuickShape);
1261
1262 // sync may have been deferred; do it now if the item became visible
1263 if (change == ItemVisibleHasChanged && data.boolValue)
1264 d->_q_shapePathChanged();
1265 else if (change == QQuickItem::ItemSceneChange) {
1266 for (int i = 0; i < d->sp.size(); ++i)
1267 QQuickShapePathPrivate::get(p: d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1268 d->_q_shapePathChanged();
1269 d->handleSceneChange(w: data.window);
1270 }
1271
1272 QQuickItem::itemChange(change, data);
1273}
1274
1275QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1276{
1277 // Called on the render thread, with the gui thread blocked. We can now
1278 // safely access gui thread data.
1279 Q_D(QQuickShape);
1280
1281 if (d->renderer || d->rendererChanged) {
1282 if (!node || d->rendererChanged) {
1283 d->rendererChanged = false;
1284 delete node;
1285 node = d->createNode();
1286 }
1287 if (d->renderer)
1288 d->renderer->updateNode();
1289
1290 // TODO: only add transform node when needed (and then make sure static_cast is safe)
1291 QMatrix4x4 fillModeTransform;
1292 qreal xScale = 1.0;
1293 qreal yScale = 1.0;
1294
1295 if (d->fillMode != NoResize) {
1296 xScale = width() / implicitWidth();
1297 yScale = height() / implicitHeight();
1298
1299 if (d->fillMode == PreserveAspectFit)
1300 xScale = yScale = qMin(a: xScale, b: yScale);
1301 else if (d->fillMode == PreserveAspectCrop)
1302 xScale = yScale = qMax(a: xScale, b: yScale);
1303 fillModeTransform.scale(x: xScale, y: yScale);
1304 }
1305 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1306 qreal tx = 0;
1307 qreal ty = 0;
1308 qreal w = xScale * implicitWidth();
1309 qreal h = yScale * implicitHeight();
1310 if (d->horizontalAlignment == AlignRight)
1311 tx = width() - w;
1312 else if (d->horizontalAlignment == AlignHCenter)
1313 tx = (width() - w) / 2;
1314 if (d->verticalAlignment == AlignBottom)
1315 ty = height() - h;
1316 else if (d->verticalAlignment == AlignVCenter)
1317 ty = (height() - h) / 2;
1318 fillModeTransform.translate(x: tx / xScale, y: ty / yScale);
1319 }
1320
1321 QSGTransformNode *transformNode = static_cast<QSGTransformNode *>(node);
1322 if (fillModeTransform != transformNode->matrix())
1323 transformNode->setMatrix(fillModeTransform);
1324 }
1325 return node;
1326}
1327
1328QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1329{
1330 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1331 Q_Q(QQuickShape);
1332 QSGRendererInterface *ri = q->window()->rendererInterface();
1333 if (!ri)
1334 return res;
1335
1336 static const bool environmentPreferCurve =
1337 qEnvironmentVariable(varName: "QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String("curverenderer");
1338
1339 switch (ri->graphicsApi()) {
1340 case QSGRendererInterface::Software:
1341 res = QQuickShape::SoftwareRenderer;
1342 break;
1343 default:
1344 if (QSGRendererInterface::isApiRhiBased(api: ri->graphicsApi())) {
1345 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1346 res = QQuickShape::CurveRenderer;
1347 } else {
1348 res = QQuickShape::GeometryRenderer;
1349 }
1350 } else {
1351 qWarning(msg: "No path backend for this graphics API yet");
1352 }
1353 break;
1354 }
1355
1356 return res;
1357}
1358
1359// the renderer object lives on the gui thread
1360void QQuickShapePrivate::createRenderer()
1361{
1362 Q_Q(QQuickShape);
1363 QQuickShape::RendererType selectedType = selectRendererType();
1364 if (selectedType == QQuickShape::UnknownRenderer)
1365 return;
1366
1367 rendererType = selectedType;
1368 rendererChanged = true;
1369
1370 switch (selectedType) {
1371 case QQuickShape::SoftwareRenderer:
1372 renderer = new QQuickShapeSoftwareRenderer;
1373 break;
1374 case QQuickShape::GeometryRenderer:
1375 renderer = new QQuickShapeGenericRenderer(q);
1376 break;
1377 case QQuickShape::CurveRenderer:
1378 renderer = new QQuickShapeCurveRenderer(q);
1379 break;
1380 default:
1381 Q_UNREACHABLE();
1382 break;
1383 }
1384}
1385
1386// the node lives on the render thread
1387QSGNode *QQuickShapePrivate::createNode()
1388{
1389 Q_Q(QQuickShape);
1390 QSGNode *node = nullptr;
1391 if (!q->window() || !renderer)
1392 return node;
1393 QSGRendererInterface *ri = q->window()->rendererInterface();
1394 if (!ri)
1395 return node;
1396
1397 QSGNode *pathNode = nullptr;
1398 switch (ri->graphicsApi()) {
1399 case QSGRendererInterface::Software:
1400 pathNode = new QQuickShapeSoftwareRenderNode(q);
1401 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1402 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1403 break;
1404 default:
1405 if (QSGRendererInterface::isApiRhiBased(api: ri->graphicsApi())) {
1406 if (rendererType == QQuickShape::CurveRenderer) {
1407 pathNode = new QSGNode;
1408 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1409 } else {
1410 pathNode = new QQuickShapeGenericNode;
1411 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1412 static_cast<QQuickShapeGenericNode *>(pathNode));
1413 }
1414 } else {
1415 qWarning(msg: "No path backend for this graphics API yet");
1416 }
1417 break;
1418 }
1419
1420 // TODO: only create transform node when needed
1421 node = new QSGTransformNode;
1422 node->appendChildNode(node: pathNode);
1423
1424 return node;
1425}
1426
1427void QQuickShapePrivate::asyncShapeReady(void *data)
1428{
1429 QQuickShapePrivate *self = static_cast<QQuickShapePrivate *>(data);
1430 self->setStatus(QQuickShape::Ready);
1431 if (self->syncTimingActive)
1432 qDebug(msg: "[Shape %p] [%d] [dirty=0x%x] async update took %lld ms",
1433 self->q_func(), self->syncTimeCounter, self->syncTimingTotalDirty, self->syncTimer.elapsed());
1434}
1435
1436void QQuickShapePrivate::sync()
1437{
1438 int totalDirty = 0;
1439 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1440 if (syncTimingActive)
1441 syncTimer.start();
1442
1443 const bool useAsync = async && renderer->flags().testFlag(flag: QQuickAbstractPathRenderer::SupportsAsync);
1444 if (useAsync) {
1445 setStatus(QQuickShape::Processing);
1446 renderer->setAsyncCallback(asyncShapeReady, this);
1447 }
1448
1449 const int count = sp.size();
1450 bool countChanged = false;
1451 renderer->beginSync(totalCount: count, countChanged: &countChanged);
1452 renderer->setTriangulationScale(triangulationScale);
1453
1454 for (int i = 0; i < count; ++i) {
1455 QQuickShapePath *p = sp[i];
1456 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1457 totalDirty |= dirty;
1458
1459 if (dirty & QQuickShapePathPrivate::DirtyPath)
1460 renderer->setPath(index: i, path: p);
1461 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor)
1462 renderer->setStrokeColor(index: i, color: p->strokeColor());
1463 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth)
1464 renderer->setStrokeWidth(index: i, w: p->strokeWidth());
1465 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1466 renderer->setFillColor(index: i, color: p->fillColor());
1467 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1468 renderer->setFillRule(index: i, fillRule: p->fillRule());
1469 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1470 renderer->setJoinStyle(index: i, joinStyle: p->joinStyle(), miterLimit: p->miterLimit());
1471 renderer->setCapStyle(index: i, capStyle: p->capStyle());
1472 }
1473 if (dirty & QQuickShapePathPrivate::DirtyDash)
1474 renderer->setStrokeStyle(index: i, strokeStyle: p->strokeStyle(), dashOffset: p->dashOffset(), dashPattern: p->dashPattern());
1475 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1476 renderer->setFillGradient(index: i, gradient: p->fillGradient());
1477 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1478 renderer->setFillTransform(index: i, transform: QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1479 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1480 if (p->fillItem() == nullptr) {
1481 renderer->setFillTextureProvider(index: i, textureProviderItem: nullptr);
1482 } else if (p->fillItem()->isTextureProvider()) {
1483 renderer->setFillTextureProvider(index: i, textureProviderItem: p->fillItem());
1484 } else {
1485 renderer->setFillTextureProvider(index: i, textureProviderItem: nullptr);
1486 qWarning() << "QQuickShape: Fill item is not texture provider";
1487 }
1488 }
1489
1490 dirty = 0;
1491 }
1492
1493 syncTimingTotalDirty = totalDirty;
1494 if (syncTimingTotalDirty)
1495 ++syncTimeCounter;
1496 else
1497 syncTimingActive = false;
1498
1499 renderer->endSync(async: useAsync);
1500
1501 if (!useAsync) {
1502 setStatus(QQuickShape::Ready);
1503 if (syncTimingActive)
1504 qDebug(msg: "[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1505 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1506 }
1507
1508 // Must dirty the QQuickItem if something got changed, nothing
1509 // else does this for us.
1510 Q_Q(QQuickShape);
1511 if (totalDirty || countChanged)
1512 q->update();
1513}
1514
1515// ***** gradient support *****
1516
1517/*!
1518 \qmltype ShapeGradient
1519 //! \nativetype QQuickShapeGradient
1520 \inqmlmodule QtQuick.Shapes
1521 \ingroup qtquick-paths
1522 \ingroup qtquick-views
1523 \inherits Gradient
1524 \brief Base type of Shape fill gradients.
1525 \since 5.10
1526
1527 This is an abstract base class for gradients like LinearGradient and
1528 cannot be created directly. It extends \l Gradient with properties like the
1529 spread mode.
1530 */
1531
1532QQuickShapeGradient::QQuickShapeGradient(QObject *parent)
1533 : QQuickGradient(parent),
1534 m_spread(PadSpread)
1535{
1536}
1537
1538/*!
1539 \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spread
1540
1541 Specifies how the area outside the gradient area should be filled. The
1542 default value is \c ShapeGradient.PadSpread.
1543
1544 \value ShapeGradient.PadSpread
1545 The area is filled with the closest stop color.
1546
1547 \value ShapeGradient.RepeatSpread
1548 The gradient is repeated outside the gradient area.
1549
1550 \value ShapeGradient.ReflectSpread
1551 The gradient is reflected outside the gradient area.
1552 */
1553
1554QQuickShapeGradient::SpreadMode QQuickShapeGradient::spread() const
1555{
1556 return m_spread;
1557}
1558
1559void QQuickShapeGradient::setSpread(SpreadMode mode)
1560{
1561 if (m_spread != mode) {
1562 m_spread = mode;
1563 emit spreadChanged();
1564 emit updated();
1565 }
1566}
1567
1568/*!
1569 \qmltype LinearGradient
1570 //! \nativetype QQuickShapeLinearGradient
1571 \inqmlmodule QtQuick.Shapes
1572 \ingroup qtquick-paths
1573 \ingroup qtquick-views
1574 \inherits ShapeGradient
1575 \brief Linear gradient.
1576 \since 5.10
1577
1578 Linear gradients interpolate colors between start and end points in Shape
1579 items. Outside these points the gradient is either padded, reflected or
1580 repeated depending on the spread type.
1581
1582 \note LinearGradient is only supported in combination with Shape items. It
1583 is not compatible with \l Rectangle, as that only supports \l Gradient.
1584
1585 \sa QLinearGradient
1586 */
1587
1588QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent)
1589 : QQuickShapeGradient(parent)
1590{
1591}
1592
1593/*!
1594 \qmlproperty real QtQuick.Shapes::LinearGradient::x1
1595 \qmlproperty real QtQuick.Shapes::LinearGradient::y1
1596 \qmlproperty real QtQuick.Shapes::LinearGradient::x2
1597 \qmlproperty real QtQuick.Shapes::LinearGradient::y2
1598
1599 These properties define the start and end points between which color
1600 interpolation occurs. By default both points are set to (0, 0).
1601 */
1602
1603qreal QQuickShapeLinearGradient::x1() const
1604{
1605 return m_start.x();
1606}
1607
1608void QQuickShapeLinearGradient::setX1(qreal v)
1609{
1610 if (m_start.x() != v) {
1611 m_start.setX(v);
1612 emit x1Changed();
1613 emit updated();
1614 }
1615}
1616
1617qreal QQuickShapeLinearGradient::y1() const
1618{
1619 return m_start.y();
1620}
1621
1622void QQuickShapeLinearGradient::setY1(qreal v)
1623{
1624 if (m_start.y() != v) {
1625 m_start.setY(v);
1626 emit y1Changed();
1627 emit updated();
1628 }
1629}
1630
1631qreal QQuickShapeLinearGradient::x2() const
1632{
1633 return m_end.x();
1634}
1635
1636void QQuickShapeLinearGradient::setX2(qreal v)
1637{
1638 if (m_end.x() != v) {
1639 m_end.setX(v);
1640 emit x2Changed();
1641 emit updated();
1642 }
1643}
1644
1645qreal QQuickShapeLinearGradient::y2() const
1646{
1647 return m_end.y();
1648}
1649
1650void QQuickShapeLinearGradient::setY2(qreal v)
1651{
1652 if (m_end.y() != v) {
1653 m_end.setY(v);
1654 emit y2Changed();
1655 emit updated();
1656 }
1657}
1658
1659/*!
1660 \qmltype RadialGradient
1661 //! \nativetype QQuickShapeRadialGradient
1662 \inqmlmodule QtQuick.Shapes
1663 \ingroup qtquick-paths
1664 \ingroup qtquick-views
1665 \inherits ShapeGradient
1666 \brief Radial gradient.
1667 \since 5.10
1668
1669 Radial gradients interpolate colors between a focal circle and a center
1670 circle in Shape items. Points outside the cone defined by the two circles
1671 will be transparent.
1672
1673 Outside the end points the gradient is either padded, reflected or repeated
1674 depending on the spread type.
1675
1676 Below is an example of a simple radial gradient. Here the colors are
1677 interpolated between the specified point and the end points on a circle
1678 specified by the radius:
1679
1680 \code
1681 fillGradient: RadialGradient {
1682 centerX: 50; centerY: 50
1683 centerRadius: 100
1684 focalX: centerX; focalY: centerY
1685 GradientStop { position: 0; color: "blue" }
1686 GradientStop { position: 0.2; color: "green" }
1687 GradientStop { position: 0.4; color: "red" }
1688 GradientStop { position: 0.6; color: "yellow" }
1689 GradientStop { position: 1; color: "cyan" }
1690 }
1691 \endcode
1692
1693 \image shape-radial-gradient.png
1694
1695 Extended radial gradients, where a separate focal circle is specified, are
1696 also supported.
1697
1698 \note RadialGradient is only supported in combination with Shape items. It
1699 is not compatible with \l Rectangle, as that only supports \l Gradient.
1700
1701 \sa QRadialGradient
1702 */
1703
1704QQuickShapeRadialGradient::QQuickShapeRadialGradient(QObject *parent)
1705 : QQuickShapeGradient(parent)
1706{
1707}
1708
1709/*!
1710 \qmlproperty real QtQuick.Shapes::RadialGradient::centerX
1711 \qmlproperty real QtQuick.Shapes::RadialGradient::centerY
1712 \qmlproperty real QtQuick.Shapes::RadialGradient::focalX
1713 \qmlproperty real QtQuick.Shapes::RadialGradient::focalY
1714
1715 These properties define the center and focal points. To specify a simple
1716 radial gradient, set focalX and focalY to the value of centerX and
1717 centerY, respectively.
1718 */
1719
1720qreal QQuickShapeRadialGradient::centerX() const
1721{
1722 return m_centerPoint.x();
1723}
1724
1725void QQuickShapeRadialGradient::setCenterX(qreal v)
1726{
1727 if (m_centerPoint.x() != v) {
1728 m_centerPoint.setX(v);
1729 emit centerXChanged();
1730 emit updated();
1731 }
1732}
1733
1734qreal QQuickShapeRadialGradient::centerY() const
1735{
1736 return m_centerPoint.y();
1737}
1738
1739void QQuickShapeRadialGradient::setCenterY(qreal v)
1740{
1741 if (m_centerPoint.y() != v) {
1742 m_centerPoint.setY(v);
1743 emit centerYChanged();
1744 emit updated();
1745 }
1746}
1747
1748/*!
1749 \qmlproperty real QtQuick.Shapes::RadialGradient::centerRadius
1750 \qmlproperty real QtQuick.Shapes::RadialGradient::focalRadius
1751
1752 These properties define the center and focal radius. For simple radial
1753 gradients, focalRadius should be set to \c 0 (the default value).
1754 */
1755
1756qreal QQuickShapeRadialGradient::centerRadius() const
1757{
1758 return m_centerRadius;
1759}
1760
1761void QQuickShapeRadialGradient::setCenterRadius(qreal v)
1762{
1763 if (m_centerRadius != v) {
1764 m_centerRadius = v;
1765 emit centerRadiusChanged();
1766 emit updated();
1767 }
1768}
1769
1770qreal QQuickShapeRadialGradient::focalX() const
1771{
1772 return m_focalPoint.x();
1773}
1774
1775void QQuickShapeRadialGradient::setFocalX(qreal v)
1776{
1777 if (m_focalPoint.x() != v) {
1778 m_focalPoint.setX(v);
1779 emit focalXChanged();
1780 emit updated();
1781 }
1782}
1783
1784qreal QQuickShapeRadialGradient::focalY() const
1785{
1786 return m_focalPoint.y();
1787}
1788
1789void QQuickShapeRadialGradient::setFocalY(qreal v)
1790{
1791 if (m_focalPoint.y() != v) {
1792 m_focalPoint.setY(v);
1793 emit focalYChanged();
1794 emit updated();
1795 }
1796}
1797
1798qreal QQuickShapeRadialGradient::focalRadius() const
1799{
1800 return m_focalRadius;
1801}
1802
1803void QQuickShapeRadialGradient::setFocalRadius(qreal v)
1804{
1805 if (m_focalRadius != v) {
1806 m_focalRadius = v;
1807 emit focalRadiusChanged();
1808 emit updated();
1809 }
1810}
1811
1812/*!
1813 \qmltype ConicalGradient
1814 //! \nativetype QQuickShapeConicalGradient
1815 \inqmlmodule QtQuick.Shapes
1816 \ingroup qtquick-paths
1817 \ingroup qtquick-views
1818 \inherits ShapeGradient
1819 \brief Conical gradient.
1820 \since 5.10
1821
1822 Conical gradients interpolate colors counter-clockwise around a center
1823 point in Shape items.
1824
1825 \note The \l{ShapeGradient::spread}{spread mode} setting has no effect for
1826 conical gradients.
1827
1828 \note ConicalGradient is only supported in combination with Shape items. It
1829 is not compatible with \l Rectangle, as that only supports \l Gradient.
1830
1831 \sa QConicalGradient
1832 */
1833
1834QQuickShapeConicalGradient::QQuickShapeConicalGradient(QObject *parent)
1835 : QQuickShapeGradient(parent)
1836{
1837}
1838
1839/*!
1840 \qmlproperty real QtQuick.Shapes::ConicalGradient::centerX
1841 \qmlproperty real QtQuick.Shapes::ConicalGradient::centerY
1842
1843 These properties define the center point of the conical gradient.
1844 */
1845
1846qreal QQuickShapeConicalGradient::centerX() const
1847{
1848 return m_centerPoint.x();
1849}
1850
1851void QQuickShapeConicalGradient::setCenterX(qreal v)
1852{
1853 if (m_centerPoint.x() != v) {
1854 m_centerPoint.setX(v);
1855 emit centerXChanged();
1856 emit updated();
1857 }
1858}
1859
1860qreal QQuickShapeConicalGradient::centerY() const
1861{
1862 return m_centerPoint.y();
1863}
1864
1865void QQuickShapeConicalGradient::setCenterY(qreal v)
1866{
1867 if (m_centerPoint.y() != v) {
1868 m_centerPoint.setY(v);
1869 emit centerYChanged();
1870 emit updated();
1871 }
1872}
1873
1874/*!
1875 \qmlproperty real QtQuick.Shapes::ConicalGradient::angle
1876
1877 This property defines the start angle for the conical gradient. The value
1878 is in degrees (0-360).
1879 */
1880
1881qreal QQuickShapeConicalGradient::angle() const
1882{
1883 return m_angle;
1884}
1885
1886void QQuickShapeConicalGradient::setAngle(qreal v)
1887{
1888 if (m_angle != v) {
1889 m_angle = v;
1890 emit angleChanged();
1891 emit updated();
1892 }
1893}
1894
1895QT_END_NAMESPACE
1896
1897#include "moc_qquickshape_p.cpp"
1898

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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