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