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