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
qquickvectorimage.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtCore/qurl.h>
5#include <QtCore/QScopeGuard>
9#include <QtQuickVectorImageGenerator/private/qquickitemgenerator_p.h>
10#include <QtQuickVectorImageGenerator/private/qquickvectorimageglobal_p.h>
11#include <QtCore/qloggingcategory.h>
12
13#include <private/qquicktranslate_p.h>
14#include <private/qquickanimation_p.h>
15
17
18static bool useQmlGenerator()
19{
20 static const bool val = !qEnvironmentVariableIsSet("QT_QUICKVECTORIMAGE_USE_ITEM_GENERATOR");
21 return val;
22}
23
24/*!
25 \qmlmodule QtQuick.VectorImage
26 \title Qt Quick Vector Image QML Types
27 \ingroup qmlmodules
28 \brief Provides QML types for displaying vector image files.
29 \since 6.8
30
31 To use the types in this module, import the module with the following line:
32
33 \qml
34 import QtQuick.VectorImage
35 \endqml
36
37 Qt Quick Vector Image provides support for displaying vector image files in a Qt Quick
38 scene.
39
40 It currently supports the \c SVG file format. In addition, Lottie support
41 can be enabled by setting the
42 \l{QtQuick.VectorImage::VectorImage::}{assumeTrustedSource} property to true
43 and including the plugin from the \l{Qt Lottie Animation} module.
44
45 Qt supports multiple options for displaying SVG files. For an overview and comparison of
46 the different ones, see the documentation of the \l{svgtoqml} tool.
47
48 \section1 QML Types
49*/
50
51void QQuickVectorImagePrivate::setSource(const QUrl &source)
52{
53 Q_Q(QQuickVectorImage);
54 if (sourceFile == source)
55 return;
56
57 sourceFile = source;
59 emit q->sourceChanged();
60}
61
63{
64 Q_Q(QQuickVectorImage);
65
66 if (!q->isComponentComplete())
67 return;
68
69 QQmlContext *ctx = qmlContext(q);
70 QUrl resolvedUrl = ctx->resolvedUrl(sourceFile);
71 QString localFile = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
72
73 if (localFile.isEmpty())
74 return;
75
76 if (rootItem && !retainWhileLoading) {
77 rootItem->deleteLater();
78 rootItem = nullptr;
79 emit q->generatedItemChanged();
80 }
81
82 if (incubator != nullptr) {
83 // If the incubator is still alive, it means it was interrupted before we could add the
84 // object to the parent item.
85 QObject *obj = incubator->object();
86 delete obj;
87
88 incubator->disconnect(q);
89 incubator->deleteLater();
90 }
91
92 if (pendingRootItem != nullptr) {
93 delete pendingRootItem;
94 pendingRootItem = nullptr;
95 }
96
97 QQuickVectorImageGenerator::GeneratorFlags flags;
98 if (preferredRendererType == QQuickVectorImage::CurveRenderer)
99 flags.setFlag(QQuickVectorImageGenerator::CurveRenderer);
101 flags.setFlag(QQuickVectorImageGenerator::AssumeTrustedSource);
102 if (m_asyncShapes)
103 flags.setFlag(QQuickVectorImageGenerator::AsyncShapes);
104 if (asynchronous)
105 flags.setFlag(QQuickVectorImageGenerator::AsynchronousLoading);
106
107 if (useQmlGenerator()) {
108 QQmlIncubator::IncubationMode mode =
109 asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::Synchronous;
110
111 if (!context || context->engine() != qmlContext(q)->engine())
112 context.reset(new QQmlContext(qmlContext(q)->engine()));
113
114 incubator = new QQuickVectorImageIncubator(mode, context.get(), q);
115 QObject::connect(incubator, &QQuickVectorImageIncubator::statusUpdated, q,
116 &QQuickVectorImage::statusChanged);
117 QObject::connect(incubator, &QQuickVectorImageIncubator::statusUpdated, q,
118 &QQuickVectorImage::updateItem);
119 incubator->start(localFile, flags);
120 } else {
121 QQuickItemGenerator gen(localFile, flags);
122 gen.generate();
123
124 if (gen.errorState() == QQuickVectorImageGenerator::NoError) {
125 pendingRootItem = gen.takeRootItem();
126 } else {
127 qCWarning(lcQuickVectorImage) << "QQuickItemGenerator: failed to generate" << localFile
128 << "(errorState:" << gen.errorState() << ")";
129 }
130 q->updateItem();
131 emit q->statusChanged();
132 }
133}
134
135void QQuickVectorImage::updateItem()
136{
137 Q_D(QQuickVectorImage);
138
139 QQuickItem *item = nullptr;
140 if (d->incubator != nullptr) {
141 if (d->incubator->object() == nullptr || !d->incubator->isReady())
142 return;
143 item = qobject_cast<QQuickItem *>(d->incubator->object());
144 if (item == nullptr) {
145 qCWarning(lcQuickVectorImage)
146 << "QQuickVectorImage::updateItem: Root item not a QQuickItem:"
147 << d->incubator->errors();
148 return;
149 }
150 } else {
151 item = d->pendingRootItem;
152 d->pendingRootItem = nullptr;
153 if (item == nullptr)
154 return;
155 }
156
157 if (d->rootItem != nullptr)
158 d->rootItem->deleteLater();
159
160 d->rootItem = new QQuickItem(this);
161 d->rootItem->setParentItem(this);
162 auto emitter = qScopeGuard([&] { emit generatedItemChanged(); }); // emit at any function exit
163
164 if (item->width() == 0 || item->height() == 0)
165 return;
166
167 d->rootItem->setImplicitWidth(item->width());
168 d->rootItem->setImplicitHeight(item->height());
169
170 item->setParent(d->rootItem);
171 item->setParentItem(d->rootItem);
172
173 setImplicitWidth(d->rootItem->width());
174 setImplicitHeight(d->rootItem->height());
175
176 updateAnimationProperties();
177 updateRootItemScale();
178 update();
179
180 static int freezeTime = qEnvironmentVariableIntValue("QT_QUICKVECTORIMAGE_FREEZE");
181 if (freezeTime != 0) {
182 if (freezeTime < 0)
183 freezeTime = 400; // TBD: calculate better default, e.g. midtime of total anim duration
184 animations()->setPaused(true);
185 const QList<QQuickAbstractAnimation *> anims = d->rootItem->findChildren<QQuickAbstractAnimation *>();
186 for (QQuickAbstractAnimation *anim : anims) {
187 if (anim->group() == nullptr)
188 anim->setCurrentTime(freezeTime);
189 }
190 }
191
192 if (d->incubator) {
193 d->incubator->disconnect(this);
194 d->incubator->deleteLater();
195 d->incubator = nullptr;
196 }
197}
198
199/*!
200 \qmltype VectorImage
201 \inqmlmodule QtQuick.VectorImage
202 \inherits Item
203 \brief Loads a vector image file and displays it in a Qt Quick scene.
204 \since 6.8
205
206 The VectorImage can be used to load a vector image file and display this as an item in a Qt
207 Quick scene.
208
209 It currently supports the \c SVG file format. In addition, Lottie support can be enabled by
210 setting the \l{assumeTrustedSource} property to true and including the plugin from the
211 \l{Qt Lottie Animation} module.
212
213 \note This complements the approach of loading the vector image file through an \l Image
214 element: \l Image creates a raster version of the image at the requested size. VectorImage
215 builds a Qt Quick scene that represents the image. This means the resulting item can be scaled
216 and rotated without losing quality, and it will typically consume less memory than the
217 rasterized version.
218*/
219QQuickVectorImage::QQuickVectorImage(QQuickItem *parent)
220 : QQuickItem(*(new QQuickVectorImagePrivate), parent)
221{
222 setFlag(QQuickItem::ItemHasContents, true);
223
224 QObject::connect(this, &QQuickItem::widthChanged, this, &QQuickVectorImage::updateRootItemScale);
225 QObject::connect(this, &QQuickItem::heightChanged, this, &QQuickVectorImage::updateRootItemScale);
226 QObject::connect(this, &QQuickVectorImage::fillModeChanged, this, &QQuickVectorImage::updateRootItemScale);
227}
228
229QQuickVectorImage::~QQuickVectorImage()
230{
231 Q_D(QQuickVectorImage);
232 // This may have a running thread, so we need to delete it before we start deleting children
233 delete d->incubator;
234 d->incubator = nullptr;
235}
236
237/*!
238 \qmlproperty url QtQuick.VectorImage::VectorImage::source
239
240 This property holds the URL of the vector image file to load.
241
242 VectorImage currently supports the \c SVG file format. In addition, Lottie support can be
243 enabled by setting the \l{assumeTrustedSource} property to true and including the plugin from
244 the \l{Qt Lottie Animation} module.
245*/
246QUrl QQuickVectorImage::source() const
247{
248 Q_D(const QQuickVectorImage);
249 return d->sourceFile;
250}
251
252void QQuickVectorImage::setSource(const QUrl &source)
253{
254 Q_D(QQuickVectorImage);
255 d->setSource(source);
256}
257
258void QQuickVectorImage::updateRootItemScale()
259{
260 Q_D(QQuickVectorImage);
261
262 if (d->rootItem == nullptr
263 || qFuzzyIsNull(d->rootItem->width())
264 || qFuzzyIsNull(d->rootItem->height())) {
265 return;
266 }
267
268 auto xformProp = d->rootItem->transform();
269 QQuickScale *scaleTransform = nullptr;
270 if (xformProp.count(&xformProp) == 0) {
271 scaleTransform = new QQuickScale;
272 scaleTransform->setParent(d->rootItem);
273 xformProp.append(&xformProp, scaleTransform);
274 } else {
275 scaleTransform = qobject_cast<QQuickScale *>(xformProp.at(&xformProp, 0));
276 }
277
278 if (scaleTransform != nullptr) {
279 qreal xScale = width() / d->rootItem->width();
280 qreal yScale = height() / d->rootItem->height();
281
282 switch (d->fillMode) {
283 case QQuickVectorImage::NoResize:
284 xScale = yScale = 1.0;
285 break;
286 case QQuickVectorImage::PreserveAspectFit:
287 xScale = yScale = qMin(xScale, yScale);
288 break;
289 case QQuickVectorImage::PreserveAspectCrop:
290 xScale = yScale = qMax(xScale, yScale);
291 break;
292 case QQuickVectorImage::Stretch:
293 // Already correct
294 break;
295 };
296
297 scaleTransform->setXScale(xScale);
298 scaleTransform->setYScale(yScale);
299 }
300}
301
302void QQuickVectorImage::updateAnimationProperties()
303{
304 Q_D(QQuickVectorImage);
305 if (Q_UNLIKELY(d->rootItem == nullptr || d->rootItem->childItems().isEmpty()))
306 return;
307
308 QQuickItem *childItem = d->rootItem->childItems().first();
309 if (Q_LIKELY(d->animations != nullptr)) {
310 childItem->setProperty("loops", d->animations->loops());
311 childItem->setProperty("paused", d->animations->paused());
312 }
313}
314
315QQuickVectorImageAnimations *QQuickVectorImage::animations()
316{
317 Q_D(QQuickVectorImage);
318 if (d->animations == nullptr) {
319 d->animations = new QQuickVectorImageAnimations;
320 QQml_setParent_noEvent(d->animations, this);
321 QObject::connect(d->animations, &QQuickVectorImageAnimations::loopsChanged, this, &QQuickVectorImage::updateAnimationProperties);
322 QObject::connect(d->animations, &QQuickVectorImageAnimations::pausedChanged, this, &QQuickVectorImage::updateAnimationProperties);
323 }
324
325 return d->animations;
326}
327
328/*!
329 \qmlproperty enumeration QtQuick.VectorImage::VectorImage::fillMode
330
331 This property defines what happens if the width and height of the VectorImage differs from
332 the implicit size of its contents.
333
334 \value VectorImage.NoResize The contents are still rendered at the size provided by
335 the input.
336 \value VectorImage.Stretch The contents are scaled to match the width and height of
337 the \c{VectorImage}. (This is the default.)
338 \value VectorImage.PreserveAspectFit The contents are scaled to fit inside the bounds of the
339 \c VectorImage, while preserving aspect ratio. The
340 actual bounding rect of the contents will sometimes be
341 smaller than the \c VectorImage item.
342 \value VectorImage.PreserveAspectCrop The contents are scaled to fill the \c VectorImage item,
343 while preserving the aspect ratio. The actual bounds of
344 the contents will sometimes be larger than the
345 \c VectorImage item.
346*/
347
348QQuickVectorImage::FillMode QQuickVectorImage::fillMode() const
349{
350 Q_D(const QQuickVectorImage);
351 return d->fillMode;
352}
353
354void QQuickVectorImage::setFillMode(FillMode newFillMode)
355{
356 Q_D(QQuickVectorImage);
357 if (d->fillMode == newFillMode)
358 return;
359 d->fillMode = newFillMode;
360 emit fillModeChanged();
361}
362
363/*!
364 \qmlproperty enumeration QtQuick.VectorImage::VectorImage::preferredRendererType
365
366 Requests a specific backend to use for rendering shapes in the \c VectorImage.
367
368 \value VectorImage.GeometryRenderer Equivalent to Shape.GeometryRenderer. This backend flattens
369 curves and triangulates the result. It will give aliased results unless multi-sampling is
370 enabled, and curve flattening may be visible when the item is scaled.
371 \value VectorImage.CurveRenderer Equivalent to Shape.CurveRenderer. With this backend, curves
372 are rendered on the GPU and anti-aliasing is built in. Will typically give better visual
373 results, but at some extra cost to performance.
374
375 The default is \c{VectorImage.GeometryRenderer}.
376*/
377
378QQuickVectorImage::RendererType QQuickVectorImage::preferredRendererType() const
379{
380 Q_D(const QQuickVectorImage);
381 return d->preferredRendererType;
382}
383
384void QQuickVectorImage::setPreferredRendererType(RendererType newPreferredRendererType)
385{
386 Q_D(QQuickVectorImage);
387 if (d->preferredRendererType == newPreferredRendererType)
388 return;
389 d->preferredRendererType = newPreferredRendererType;
390 d->loadFile();
391 emit preferredRendererTypeChanged();
392}
393
394/*!
395 \qmlproperty bool QtQuick.VectorImage::VectorImage::asynchronousShapes
396 \since 6.11
397
398 This property controls the {QtQuick.Shapes::Shape::asynchronous}{asynchronous} property of the
399 \l Shape items in the Quick scene that VectorImage builds to represent the image.
400
401 Setting this property to \c true will offload the CPU part of the rendering processing of the
402 shapes to separate worker threads. This can improve CPU utilization and user interface
403 responsiveness.
404
405 By default this property is \c false.
406
407 \sa asynchronous
408*/
409
410bool QQuickVectorImage::asynchronousShapes() const
411{
412 Q_D(const QQuickVectorImage);
413 return d->m_asyncShapes;
414}
415
416void QQuickVectorImage::setAsynchronousShapes(bool asynchronous)
417{
418 Q_D(QQuickVectorImage);
419 if (d->m_asyncShapes == asynchronous)
420 return;
421 d->m_asyncShapes = asynchronous;
422 emit asynchronousShapesChanged();
423}
424
425/*!
426 \qmlproperty bool QtQuick.VectorImage::VectorImage::asynchronous
427 \since 6.12
428
429 This property holds whether the image will be loaded asynchronously. When set to to \c true,
430 the UI will remain reactive while the image is loading. The \l status property can be used
431 to check the current progress.
432
433 By default this property is \c false.
434
435 \sa asynchronousShapes, status
436*/
437
438bool QQuickVectorImage::asynchronous() const
439{
440 Q_D(const QQuickVectorImage);
441 return d->asynchronous;
442}
443
444void QQuickVectorImage::setAsynchronous(bool asynchronous)
445{
446 Q_D(QQuickVectorImage);
447 if (d->asynchronous == asynchronous)
448 return;
449 d->asynchronous = asynchronous;
450 emit asynchronousChanged();
451}
452
453/*!
454 \qmlproperty bool QtQuick.VectorImage::VectorImage::retainWhileLoading
455 \since 6.12
456
457 This property defines the behavior when the \l source property is changed and loading happens
458 asynchronously. This is the case when the \l asynchronous property is set to \c true.
459
460 If \c retainWhileLoading is \c false (the default), the old image is discarded immediately, and
461 the component is cleared while the new image is being loaded. If set to \c true, the old image
462 is retained and remains visible until the new one is ready.
463
464 Enabling this property can avoid flickering in cases where loading the new image takes a long
465 time. It comes at the cost of some extra memory use while the new image is being loaded.
466
467 \sa asynchronous
468*/
469bool QQuickVectorImage::retainWhileLoading() const
470{
471 Q_D(const QQuickVectorImage);
472 return d->retainWhileLoading;
473}
474
475void QQuickVectorImage::setRetainWhileLoading(bool retainWhileLoading)
476{
477 Q_D(QQuickVectorImage);
478 if (d->retainWhileLoading == retainWhileLoading)
479 return;
480 d->retainWhileLoading = retainWhileLoading;
481 emit retainWhileLoadingChanged();
482}
483
484/*!
485 \qmlproperty enumeration QtQuick.VectorImage::VectorImage::status
486 \since 6.12
487
488 This property holds the status of vector image loading. It can be one of:
489
490 \value VectorImage.Null No vector image has been set
491 \value VectorImage.Ready The vector image has been loaded
492 \value VectorImage.Loading The vector image is currently being loaded
493 \value VectorImage.Error An error occurred while loading the vector image
494*/
495QQuickVectorImage::Status QQuickVectorImage::status() const
496{
497 Q_D(const QQuickVectorImage);
498 if (d->incubator == nullptr) {
499 if (d->rootItem == nullptr)
500 return Status::Null;
501 else
502 return Status::Ready;
503 }
504
505 switch (d->incubator->status()) {
506 case QQmlIncubator::Null:
507 return Status::Null;
508 case QQmlIncubator::Loading:
509 return Status::Loading;
510 case QQmlIncubator::Error:
511 return Status::Error;
512 case QQmlIncubator::Ready:
513 return Status::Ready;
514 };
515
516 return Status::Error;
517}
518
519/*!
520 \qmlproperty bool QtQuick.VectorImage::VectorImage::assumeTrustedSource
521 \since 6.10
522
523 Setting this to true when loading trusted source files expands support for some features that
524 may be unsafe in an uncontrolled setting. For SVG in particular, this maps to the
525 \l{QtSvg::Option}{AssumeTrustedSource option}.
526
527 When this is set to true, VectorImage will also try to load the image using the Lottie format
528 plugin if this is available. See \l{Qt Lottie Animation} for additional information.
529
530 By default this property is \c false.
531
532 \sa svgtoqml, lottietoqml
533 */
534
535bool QQuickVectorImage::assumeTrustedSource() const
536{
537 Q_D(const QQuickVectorImage);
538 return d->assumeTrustedSource;
539}
540
541void QQuickVectorImage::setAssumeTrustedSource(bool assumeTrustedSource)
542{
543 Q_D(QQuickVectorImage);
544 if (d->assumeTrustedSource == assumeTrustedSource)
545 return;
546 d->assumeTrustedSource = assumeTrustedSource;
547 d->loadFile();
548 emit assumeTrustedSourceChanged();
549}
550
551/*!
552 \qmlproperty Item QtQuick.VectorImage::VectorImage::generatedItem
553 \since 6.12
554 \readonly
555
556 When a vector image file is loaded, this property holds the top level Item of the generated Qt
557 Quick scene. When no file is loaded, this property is \c null.
558*/
559
560QQuickItem *QQuickVectorImage::generatedItem() const
561{
562 Q_D(const QQuickVectorImage);
563 return d->rootItem ? d->rootItem->childItems().value(0) : nullptr;
564}
565
566void QQuickVectorImage::componentComplete()
567{
568 Q_D(QQuickVectorImage);
569 QQuickItem::componentComplete();
570
571 d->loadFile();
572}
573
574/*!
575 \qmlpropertygroup QtQuick.VectorImage::VectorImage::animations
576 \qmlproperty bool QtQuick.VectorImage::VectorImage::animations.paused
577 \qmlproperty int QtQuick.VectorImage::VectorImage::animations.loops
578 \since 6.10
579
580 These properties can be used to control animations in the image, if it contains any.
581
582 The \c paused property can be set to true to temporarily pause all animations. When the
583 property is reset to \c false, the animations will resume where they were. By default this
584 property is \c false.
585
586 The \c loops property defines the number of times the animations in the document will repeat.
587 By default this property is 1. Any animations that is set to loop indefinitely in the source
588 image will be unaffected by this property. To make all animations in the document repeat
589 indefinitely, the \c loops property can be set to \c{Animation.Infinite}.
590*/
591int QQuickVectorImageAnimations::loops() const
592{
593 return m_loops;
594}
595
596void QQuickVectorImageAnimations::setLoops(int loops)
597{
598 if (m_loops == loops)
599 return;
600 m_loops = loops;
601 emit loopsChanged();
602}
603
604bool QQuickVectorImageAnimations::paused() const
605{
606 return m_paused;
607}
608
609void QQuickVectorImageAnimations::setPaused(bool paused)
610{
611 if (m_paused == paused)
612 return;
613 m_paused = paused;
614 emit pausedChanged();
615}
616
617void QQuickVectorImageAnimations::restart()
618{
619 QQuickVectorImage *parentVectorImage = qobject_cast<QQuickVectorImage *>(parent());
620 if (Q_UNLIKELY(parentVectorImage == nullptr)) {
621 qCWarning(lcQuickVectorImage) << Q_FUNC_INFO << "Parent is not a VectorImage";
622 return;
623 }
624
625 QQuickVectorImagePrivate *d = QQuickVectorImagePrivate::get(parentVectorImage);
626
627 if (Q_UNLIKELY(d->rootItem == nullptr || d->rootItem->childItems().isEmpty()))
628 return;
629
630 QQuickItem *childItem = d->rootItem->childItems().first();
631 QMetaObject::invokeMethod(childItem, "restart");
632}
633
634QT_END_NAMESPACE
635
636#include <moc_qquickvectorimage_p.cpp>
void setSource(const QUrl &source)
\qmlmodule QtQuick.VectorImage \title Qt Quick Vector Image QML Types
QQuickVectorImageIncubator * incubator
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE bool useQmlGenerator()