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