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