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