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