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
qquickimage.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
7
8#include <QtQuick/qsgtextureprovider.h>
9
10#include <QtQuick/private/qsgcontext_p.h>
11#include <private/qsgadaptationlayer_p.h>
12#include <private/qnumeric_p.h>
13
14#include <QtCore/qmath.h>
15#include <QtGui/qpainter.h>
16#include <QtCore/QRunnable>
17
19
20QQuickImageTextureProvider::QQuickImageTextureProvider()
21 : m_texture(nullptr)
22 , m_smooth(false)
23{
24}
25
26void QQuickImageTextureProvider::updateTexture(QSGTexture *texture) {
27 if (m_texture == texture)
28 return;
29
30 if (m_texture)
31 disconnect(m_texture, &QSGTexture::destroyed, this, nullptr);
32
33 m_texture = texture;
34
35 if (m_texture)
36 connect(m_texture, &QSGTexture::destroyed, this, [this]() { updateTexture(nullptr); });
37
38 emit textureChanged();
39}
40
41QSGTexture *QQuickImageTextureProvider::texture() const {
42 if (m_texture) {
43 m_texture->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest);
44 m_texture->setMipmapFiltering(m_mipmap ? QSGTexture::Linear : QSGTexture::None);
45 m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge);
46 m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge);
47 }
48 return m_texture;
49}
50
51QQuickImagePrivate::QQuickImagePrivate()
52 : pixmapChanged(false)
53 , mipmap(false)
54{
55}
56
57/*!
58 \qmltype Image
59 \nativetype QQuickImage
60 \inqmlmodule QtQuick
61 \ingroup qtquick-visual
62 \inherits Item
63 \brief Displays an image.
64
65 The Image type displays an image.
66
67 The source of the image is specified as a URL using the \l source property.
68 Images can be supplied in any of the standard image formats supported by Qt,
69 including bitmap formats such as PNG and JPEG, and vector graphics formats
70 such as SVG. If you need to display animated images, use \l AnimatedSprite
71 or \l AnimatedImage.
72
73 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
74 specified, the Image automatically uses the size of the loaded image.
75 By default, specifying the width and height of the item causes the image
76 to be scaled to that size. This behavior can be changed by setting the
77 \l fillMode property, allowing the image to be stretched and tiled instead.
78
79 It is possible to provide \l {High Resolution Versions of Images}{"@nx" high DPI syntax}.
80
81 \section1 Example Usage
82
83 The following example shows the simplest usage of the Image type.
84
85 \snippet qml/image.qml document
86
87 \beginfloatleft
88 \image declarative-qtlogo.png
89 \endfloat
90
91 \clearfloat
92
93 \section1 Compressed Texture Files
94
95 When supported by the implementation of the underlying graphics API at run
96 time, images can also be supplied in compressed texture files. The content
97 must be a simple RGB(A) format 2D texture. Supported compression schemes are
98 only limited by the underlying driver and GPU. The following container file
99 formats are supported:
100
101 \list
102 \li \c PKM (since Qt 5.10)
103 \li \c KTX (since Qt 5.11)
104 \li \c ASTC (since Qt 5.13)
105 \endlist
106
107 \note The intended vertical orientation of an image in a texture file is not generally well
108 defined. Different texture compression tools have different defaults and options of when to
109 perform vertical flipping of the input image. If an image from a texture file appears upside
110 down, flipping may need to be toggled in the asset conditioning process. Alternatively, the
111 Image element itself can be flipped by either applying a suitable transformation via the
112 transform property or, more conveniently, by setting the mirrorVertically property:
113 \badcode
114 transform: [ Translate { y: -myImage.height }, Scale { yScale: -1 } ]
115 \endcode
116 or
117 \badcode
118 mirrorVertically: true
119 \endcode
120
121 \note Semi-transparent original images require alpha pre-multiplication
122 prior to texture compression in order to be correctly displayed in Qt
123 Quick. This can be done with the following ImageMagick command
124 line:
125 \badcode
126 convert foo.png \‍( +clone -alpha Extract \‍) -channel RGB -compose Multiply -composite foo_pm.png
127 \endcode
128
129 Do not confuse container formats, such as, \c KTX, and the format of the
130 actual texture data stored in the container file. For example, reading a
131 \c KTX file is supported on all platforms, independently of what GPU driver is
132 used at run time. However, this does not guarantee that the compressed
133 texture format, used by the data in the file, is supported at run time. For
134 example, if the KTX file contains compressed data with the format
135 \c{ETC2 RGBA8}, and the 3D graphics API implementation used at run time does not
136 support \c ETC2 compressed textures, the Image item will not display
137 anything.
138
139 \note Compressed texture format support is not under Qt's control, and it
140 is up to the application or device developer to ensure the compressed
141 texture data is provided in the appropriate format for the target
142 environment(s).
143
144 Do not assume that compressed format support is specific to a platform. It
145 may also be specific to the driver and 3D API implementation in use on that
146 particular platform. In practice, implementations of different 3D graphics
147 APIs (e.g., Vulkan and OpenGL) on the same platform (e.g., Windows) from
148 the same vendor for the same hardware may offer a different set of
149 compressed texture formats.
150
151 When targeting desktop environments (Windows, macOS, Linux) only, a general
152 recommendation is to consider using the \c{DXTn}/\c{BCn} formats since
153 these tend to have the widest support amongst the implementations of Direct
154 3D, Vulkan, OpenGL, and Metal on these platforms. In contrast, when
155 targeting mobile or embedded devices, the \c ETC2 or \c ASTC formats are
156 likely to be a better choice since these are typically the formats
157 supported by the OpenGL ES implementations on such hardware.
158
159 An application that intends to run across desktop, mobile, and embedded
160 hardware should plan and design its use of compressed textures carefully.
161 It is highly likely that relying on a single format is not going to be
162 sufficient, and therefore the application will likely need to branch based
163 on the platform to use compressed textures in a format appropriate there,
164 or perhaps to skip using compressed textures in some cases.
165
166 \section1 Automatic Detection of File Extension
167
168 If the \l source URL indicates a non-existing local file or resource, the
169 Image element attempts to auto-detect the file extension. If an existing
170 file can be found by appending any of the supported image file extensions
171 to the \l source URL, then that file will be loaded.
172
173 The file search attempts to look for compressed texture container file
174 extensions first. If the search is unsuccessful, it attempts to search with
175 the file extensions for the
176 \l{QImageReader::supportedImageFormats()}{conventional image file
177 types}. For example:
178
179 \snippet qml/image-ext.qml ext
180
181 This functionality facilitates deploying different image asset file types
182 on different target platforms. This can be useful in order to tune
183 application performance and adapt to different graphics hardware.
184
185 This functionality was introduced in Qt 5.11.
186
187 \section1 Performance
188
189 By default, locally available images are loaded immediately, and the user interface
190 is blocked until loading is complete. If a large image is to be loaded, it may be
191 preferable to load the image in a low priority thread, by enabling the \l asynchronous
192 property.
193
194 If the image is obtained from a network rather than a local resource, it is
195 automatically loaded asynchronously, and the \l progress and \l status properties
196 are updated as appropriate.
197
198 Images are cached and shared internally, so if several Image items have the same \l source,
199 only one copy of the image will be loaded.
200
201 \b Note: Images are often the greatest user of memory in QML user interfaces. It is recommended
202 that images which do not form part of the user interface have their
203 size bounded via the \l sourceSize property. This is especially important for content
204 that is loaded from external sources or provided by the user.
205
206 \sa {Qt Quick Examples - Image Elements}, QQuickImageProvider, QImageReader::setAutoDetectImageFormat()
207*/
208
209QQuickImage::QQuickImage(QQuickItem *parent)
210 : QQuickImageBase(*(new QQuickImagePrivate), parent)
211{
212}
213
214QQuickImage::QQuickImage(QQuickImagePrivate &dd, QQuickItem *parent)
215 : QQuickImageBase(dd, parent)
216{
217}
218
219QQuickImage::~QQuickImage()
220{
221 Q_D(QQuickImage);
222 if (d->provider) {
223 // We're guaranteed to have a window() here because the provider would have
224 // been released in releaseResources() if we were gone from a window.
225 QQuickWindowQObjectCleanupJob::schedule(window(), d->provider);
226 }
227}
228
229void QQuickImagePrivate::setImage(const QImage &image)
230{
231 Q_Q(QQuickImage);
232 currentPix->setImage(image);
233 q->pixmapChange();
234 q->update();
235}
236
237void QQuickImagePrivate::setPixmap(const QQuickPixmap &pixmap)
238{
239 Q_Q(QQuickImage);
240 currentPix->setPixmap(pixmap);
241 q->pixmapChange();
242 q->update();
243}
244
245/*!
246 \qmlproperty enumeration QtQuick::Image::fillMode
247
248 Set this property to define what happens when the source image has a different size
249 than the item.
250
251 \value Image.Stretch the image is scaled to fit
252 \value Image.PreserveAspectFit the image is scaled uniformly to fit without cropping
253 \value Image.PreserveAspectCrop the image is scaled uniformly to fill, cropping if necessary
254 \value Image.Tile the image is duplicated horizontally and vertically
255 \value Image.TileVertically the image is stretched horizontally and tiled vertically
256 \value Image.TileHorizontally the image is stretched vertically and tiled horizontally
257 \value Image.Pad the image is not transformed
258 \br
259
260 \table
261
262 \row
263 \li \image declarative-qtlogo-stretch.png
264 \li Stretch (default)
265 \qml
266 Image {
267 width: 130; height: 100
268 source: "qtlogo.png"
269 }
270 \endqml
271
272 \row
273 \li \image declarative-qtlogo-preserveaspectfit.png
274 \li PreserveAspectFit
275 \qml
276 Image {
277 width: 130; height: 100
278 fillMode: Image.PreserveAspectFit
279 source: "qtlogo.png"
280 }
281 \endqml
282
283 \row
284 \li \image declarative-qtlogo-preserveaspectcrop.png
285 \li PreserveAspectCrop
286 \qml
287 Image {
288 width: 130; height: 100
289 fillMode: Image.PreserveAspectCrop
290 source: "qtlogo.png"
291 clip: true
292 }
293 \endqml
294
295 \row
296 \li \image declarative-qtlogo-tile.png
297 \li Tile
298 \qml
299 Image {
300 width: 120; height: 120
301 fillMode: Image.Tile
302 horizontalAlignment: Image.AlignLeft
303 verticalAlignment: Image.AlignTop
304 source: "qtlogo.png"
305 }
306 \endqml
307
308 \row
309 \li \image declarative-qtlogo-tilevertically.png
310 \li TileVertically
311 \qml
312 Image {
313 width: 120; height: 120
314 fillMode: Image.TileVertically
315 verticalAlignment: Image.AlignTop
316 source: "qtlogo.png"
317 }
318 \endqml
319
320 \row
321 \li \image declarative-qtlogo-tilehorizontally.png
322 \li TileHorizontally
323 \qml
324 Image {
325 width: 120; height: 120
326 fillMode: Image.TileHorizontally
327 verticalAlignment: Image.AlignLeft
328 source: "qtlogo.png"
329 }
330 \endqml
331
332 \endtable
333
334 Note that \c clip is \c false by default which means that the item might
335 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
336
337 \sa {Qt Quick Examples - Image Elements}
338*/
339QQuickImage::FillMode QQuickImage::fillMode() const
340{
341 Q_D(const QQuickImage);
342 return d->fillMode;
343}
344
345void QQuickImage::setFillMode(FillMode mode)
346{
347 Q_D(QQuickImage);
348 if (d->fillMode == mode)
349 return;
350 d->fillMode = mode;
351 if ((mode == PreserveAspectCrop) != d->providerOptions.preserveAspectRatioCrop()) {
352 d->providerOptions.setPreserveAspectRatioCrop(mode == PreserveAspectCrop);
353 if (isComponentComplete())
354 load();
355 } else if ((mode == PreserveAspectFit) != d->providerOptions.preserveAspectRatioFit()) {
356 d->providerOptions.setPreserveAspectRatioFit(mode == PreserveAspectFit);
357 if (isComponentComplete())
358 load();
359 }
360 update();
361 updatePaintedGeometry();
362 emit fillModeChanged();
363}
364
365/*!
366 \qmlproperty real QtQuick::Image::paintedWidth
367 \qmlproperty real QtQuick::Image::paintedHeight
368 \readonly
369
370 These properties hold the size of the image that is actually painted.
371 In most cases it is the same as \c width and \c height, but when using an
372 \l {fillMode}{Image.PreserveAspectFit} or an \l {fillMode}{Image.PreserveAspectCrop}
373 \c paintedWidth or \c paintedHeight can be smaller or larger than
374 \c width and \c height of the Image item.
375*/
376qreal QQuickImage::paintedWidth() const
377{
378 Q_D(const QQuickImage);
379 return d->paintedWidth;
380}
381
382qreal QQuickImage::paintedHeight() const
383{
384 Q_D(const QQuickImage);
385 return d->paintedHeight;
386}
387
388/*!
389 \qmlproperty enumeration QtQuick::Image::status
390 \readonly
391
392 This property holds the status of image loading. It can be one of:
393
394 \value Image.Null No image has been set
395 \value Image.Ready The image has been loaded
396 \value Image.Loading The image is currently being loaded
397 \value Image.Error An error occurred while loading the image
398
399 Use this status to provide an update or respond to the status change in some way.
400 For example, you could:
401
402 \list
403 \li Trigger a state change:
404 \qml
405 State { name: 'loaded'; when: image.status == Image.Ready }
406 \endqml
407
408 \li Implement an \c onStatusChanged signal handler:
409 \qml
410 Image {
411 id: image
412 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
413 }
414 \endqml
415
416 \li Bind to the status value:
417 \qml
418 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
419 \endqml
420 \endlist
421
422 \sa progress
423*/
424
425/*!
426 \qmlproperty real QtQuick::Image::progress
427 \readonly
428
429 This property holds the progress of image loading, from 0.0 (nothing loaded)
430 to 1.0 (finished).
431
432 \sa status
433*/
434
435/*!
436 \qmlproperty bool QtQuick::Image::smooth
437
438 This property holds whether the image is smoothly filtered when scaled or
439 transformed. Smooth filtering gives better visual quality, but it may be slower
440 on some hardware. If the image is displayed at its natural size, this property has
441 no visual or performance effect.
442
443 By default, this property is set to true.
444
445 \sa mipmap
446*/
447
448/*!
449 \qmlproperty size QtQuick::Image::sourceSize
450
451 This property holds the scaled width and height of the full-frame image.
452
453 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
454 the painting of the image, this property sets the maximum number of pixels
455 stored for the loaded image so that large images do not use more
456 memory than necessary. For example, this ensures the image in memory is no
457 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
458 \l {Item::}{height} values:
459
460 \code
461 Rectangle {
462 width: ...
463 height: ...
464
465 Image {
466 anchors.fill: parent
467 source: "reallyBigImage.jpg"
468 sourceSize.width: 1024
469 sourceSize.height: 1024
470 }
471 }
472 \endcode
473
474 If the image's actual size is larger than the sourceSize, the image is scaled down.
475 If only one dimension of the size is set to greater than 0, the
476 other dimension is set in proportion to preserve the source image's aspect ratio.
477 (The \l fillMode is independent of this.)
478
479 If both the sourceSize.width and sourceSize.height are set, the image will be scaled
480 down to fit within the specified size (unless PreserveAspectCrop or PreserveAspectFit
481 are used, then it will be scaled to match the optimal size for cropping/fitting),
482 maintaining the image's aspect ratio. The actual
483 size of the image after scaling is available via \l Item::implicitWidth and \l Item::implicitHeight.
484
485 If the source is an intrinsically scalable image (eg. SVG), this property
486 determines the size of the loaded image regardless of intrinsic size.
487 Avoid changing this property dynamically; rendering an SVG is \e slow compared
488 to an image.
489
490 If the source is a non-scalable image (eg. JPEG), the loaded image will
491 be no greater than this property specifies. For some formats (currently only JPEG),
492 the whole image will never actually be loaded into memory.
493
494 If the \l sourceClipRect property is also set, \c sourceSize determines the scale,
495 but it will be clipped to the size of the clip rectangle.
496
497 sourceSize can be cleared to the natural size of the image
498 by setting sourceSize to \c undefined.
499
500 \note \e {Changing this property dynamically causes the image source to be reloaded,
501 potentially even from the network, if it is not in the disk cache.}
502
503 \sa {Qt Quick Examples - Pointer Handlers}
504*/
505
506/*!
507 \qmlproperty rect QtQuick::Image::sourceClipRect
508 \since 5.15
509
510 This property, if set, holds the rectangular region of the source image to
511 be loaded.
512
513 The \c sourceClipRect works together with the \l sourceSize property to
514 conserve system resources when only a portion of an image needs to be
515 loaded.
516
517 \code
518 Rectangle {
519 width: ...
520 height: ...
521
522 Image {
523 anchors.fill: parent
524 source: "reallyBigImage.svg"
525 sourceSize.width: 1024
526 sourceSize.height: 1024
527 sourceClipRect: Qt.rect(100, 100, 512, 512)
528 }
529 }
530 \endcode
531
532 In the above example, we conceptually scale the SVG graphic to 1024x1024
533 first, and then cut out a region of interest that is 512x512 pixels from a
534 location 100 pixels from the top and left edges. Thus \c sourceSize
535 determines the scale, but the actual output image is 512x512 pixels.
536
537 Some image formats are able to conserve CPU time by rendering only the
538 specified region. Others will need to load the entire image first and then
539 clip it to the specified region.
540
541 This property can be cleared to reload the entire image by setting
542 \c sourceClipRect to \c undefined.
543
544 \note \e {Changing this property dynamically causes the image source to be reloaded,
545 potentially even from the network, if it is not in the disk cache.}
546
547 \note Sub-pixel clipping is not supported: the given rectangle will be
548 passed to \l QImageReader::setScaledClipRect().
549*/
550
551/*!
552 \qmlproperty url QtQuick::Image::source
553
554 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
555
556 The URL may be absolute, or relative to the URL of the component.
557
558 \sa QQuickImageProvider, {Compressed Texture Files}, {Automatic Detection of File Extension}
559*/
560
561/*!
562 \qmlproperty bool QtQuick::Image::asynchronous
563
564 Specifies that images on the local filesystem should be loaded
565 asynchronously in a separate thread. The default value is
566 false, causing the user interface thread to block while the
567 image is loaded. Setting \a asynchronous to true is useful where
568 maintaining a responsive user interface is more desirable
569 than having images immediately visible.
570
571 Note that this property is only valid for images read from the
572 local filesystem. Images loaded via a network resource (e.g. HTTP)
573 are always loaded asynchronously.
574*/
575
576/*!
577 \qmlproperty bool QtQuick::Image::cache
578
579 Specifies whether the image should be cached. The default value is
580 true. Setting \a cache to false is useful when dealing with large images,
581 to make sure that they aren't cached at the expense of small 'ui element' images.
582*/
583
584/*!
585 \qmlproperty bool QtQuick::Image::mirror
586
587 This property holds whether the image should be horizontally inverted
588 (effectively displaying a mirrored image).
589
590 The default value is false.
591*/
592
593/*!
594 \qmlproperty bool QtQuick::Image::mirrorVertically
595
596 This property holds whether the image should be vertically inverted
597 (effectively displaying a mirrored image).
598
599 The default value is false.
600
601 \since 6.2
602*/
603
604/*!
605 \qmlproperty enumeration QtQuick::Image::horizontalAlignment
606 \qmlproperty enumeration QtQuick::Image::verticalAlignment
607
608 Sets the horizontal and vertical alignment of the image. By default, the image is center aligned.
609
610 The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
611 The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
612 and \c Image.AlignVCenter.
613*/
614void QQuickImage::updatePaintedGeometry()
615{
616 Q_D(QQuickImage);
617
618 if (d->fillMode == PreserveAspectFit) {
619 if (!d->currentPix->width() || !d->currentPix->height()) {
620 setImplicitSize(0, 0);
621 return;
622 }
623 const qreal pixWidth = d->currentPix->width() / d->devicePixelRatio;
624 const qreal pixHeight = d->currentPix->height() / d->devicePixelRatio;
625 const qreal w = widthValid() ? width() : pixWidth;
626 const qreal widthScale = w / pixWidth;
627 const qreal h = heightValid() ? height() : pixHeight;
628 const qreal heightScale = h / pixHeight;
629 if (widthScale <= heightScale) {
630 d->paintedWidth = w;
631 d->paintedHeight = widthScale * pixHeight;
632 } else if (heightScale < widthScale) {
633 d->paintedWidth = heightScale * pixWidth;
634 d->paintedHeight = h;
635 }
636 const qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : pixHeight;
637 const qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : pixWidth;
638 setImplicitSize(iWidth, iHeight);
639
640 } else if (d->fillMode == PreserveAspectCrop) {
641 if (!d->currentPix->width() || !d->currentPix->height())
642 return;
643 const qreal pixWidth = d->currentPix->width() / d->devicePixelRatio;
644 const qreal pixHeight = d->currentPix->height() / d->devicePixelRatio;
645 qreal widthScale = width() / pixWidth;
646 qreal heightScale = height() / pixHeight;
647 if (widthScale < heightScale) {
648 widthScale = heightScale;
649 } else if (heightScale < widthScale) {
650 heightScale = widthScale;
651 }
652
653 d->paintedHeight = heightScale * pixHeight;
654 d->paintedWidth = widthScale * pixWidth;
655 } else if (d->fillMode == Pad) {
656 d->paintedWidth = d->currentPix->width() / d->devicePixelRatio;
657 d->paintedHeight = d->currentPix->height() / d->devicePixelRatio;
658 } else {
659 d->paintedWidth = width();
660 d->paintedHeight = height();
661 }
662 emit paintedGeometryChanged();
663}
664
665void QQuickImage::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
666{
667 QQuickImageBase::geometryChange(newGeometry, oldGeometry);
668 if (newGeometry.size() != oldGeometry.size())
669 updatePaintedGeometry();
670}
671
672QRectF QQuickImage::boundingRect() const
673{
674 Q_D(const QQuickImage);
675 return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
676}
677
678QSGTextureProvider *QQuickImage::textureProvider() const
679{
680 Q_D(const QQuickImage);
681
682 // When Item::layer::enabled == true, QQuickItem will be a texture
683 // provider. In this case we should prefer to return the layer rather
684 // than the image itself. The layer will include any children and any
685 // the image's wrap and fill mode.
686 if (QQuickItem::isTextureProvider())
687 return QQuickItem::textureProvider();
688
689 if (!d->window || !d->sceneGraphRenderContext() || QThread::currentThread() != d->sceneGraphRenderContext()->thread()) {
690 qWarning("QQuickImage::textureProvider: can only be queried on the rendering thread of an exposed window");
691 return nullptr;
692 }
693
694 if (!d->provider) {
695 QQuickImagePrivate *dd = const_cast<QQuickImagePrivate *>(d);
696 dd->provider = new QQuickImageTextureProvider;
697 dd->provider->m_smooth = d->smooth;
698 dd->provider->m_mipmap = d->mipmap;
699 dd->provider->updateTexture(d->sceneGraphRenderContext()->textureForFactory(d->currentPix->textureFactory(), window()));
700 }
701
702 return d->provider;
703}
704
705void QQuickImage::invalidateSceneGraph()
706{
707 Q_D(QQuickImage);
708 delete d->provider;
709 d->provider = nullptr;
710}
711
712void QQuickImage::releaseResources()
713{
714 Q_D(QQuickImage);
715 if (d->provider) {
716 QQuickWindowQObjectCleanupJob::schedule(window(), d->provider);
717 d->provider = nullptr;
718 }
719}
720
721QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
722{
723 Q_D(QQuickImage);
724
725 QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->currentPix->textureFactory(), window());
726
727 // Copy over the current texture state into the texture provider...
728 if (d->provider) {
729 d->provider->m_smooth = d->smooth;
730 d->provider->m_mipmap = d->mipmap;
731 d->provider->updateTexture(texture);
732 }
733
734 if (!texture || width() <= 0 || height() <= 0) {
735 delete oldNode;
736 return nullptr;
737 }
738
739 QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
740 if (!node) {
741 d->pixmapChanged = true;
742 node = d->sceneGraphContext()->createInternalImageNode(d->sceneGraphRenderContext());
743 }
744
745 QRectF targetRect;
746 QRectF sourceRect;
747 QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
748 QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
749
750 qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->currentPix->width() / d->devicePixelRatio;
751 qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->currentPix->height() / d->devicePixelRatio;
752
753 int xOffset = 0;
754 if (d->hAlign == QQuickImage::AlignHCenter)
755 xOffset = (width() - pixWidth) / 2;
756 else if (d->hAlign == QQuickImage::AlignRight)
757 xOffset = qCeil(width() - pixWidth);
758
759 int yOffset = 0;
760 if (d->vAlign == QQuickImage::AlignVCenter)
761 yOffset = (height() - pixHeight) / 2;
762 else if (d->vAlign == QQuickImage::AlignBottom)
763 yOffset = qCeil(height() - pixHeight);
764
765 switch (d->fillMode) {
766 case Stretch:
767 targetRect = QRectF(0, 0, width(), height());
768 sourceRect = d->currentPix->rect();
769 break;
770
771 case PreserveAspectFit:
772 targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
773 sourceRect = d->currentPix->rect();
774 break;
775
776 case PreserveAspectCrop: {
777 targetRect = QRectF(0, 0, width(), height());
778 qreal wscale = width() / qreal(d->currentPix->width());
779 qreal hscale = height() / qreal(d->currentPix->height());
780
781 if (wscale > hscale) {
782 int src = (hscale / wscale) * qreal(d->currentPix->height());
783 int y = 0;
784 if (d->vAlign == QQuickImage::AlignVCenter)
785 y = qCeil((d->currentPix->height() - src) / 2.);
786 else if (d->vAlign == QQuickImage::AlignBottom)
787 y = qCeil(d->currentPix->height() - src);
788 sourceRect = QRectF(0, y, d->currentPix->width(), src);
789
790 } else {
791 int src = (wscale / hscale) * qreal(d->currentPix->width());
792 int x = 0;
793 if (d->hAlign == QQuickImage::AlignHCenter)
794 x = qCeil((d->currentPix->width() - src) / 2.);
795 else if (d->hAlign == QQuickImage::AlignRight)
796 x = qCeil(d->currentPix->width() - src);
797 sourceRect = QRectF(x, 0, src, d->currentPix->height());
798 }
799 }
800 break;
801
802 case Tile:
803 targetRect = QRectF(0, 0, width(), height());
804 sourceRect = QRectF(-xOffset, -yOffset, width(), height());
805 hWrap = QSGTexture::Repeat;
806 vWrap = QSGTexture::Repeat;
807 break;
808
809 case TileHorizontally:
810 targetRect = QRectF(0, 0, width(), height());
811 sourceRect = QRectF(-xOffset, 0, width(), d->currentPix->height());
812 hWrap = QSGTexture::Repeat;
813 break;
814
815 case TileVertically:
816 targetRect = QRectF(0, 0, width(), height());
817 sourceRect = QRectF(0, -yOffset, d->currentPix->width(), height());
818 vWrap = QSGTexture::Repeat;
819 break;
820
821 case Pad:
822 qreal w = qMin(qreal(pixWidth), width());
823 qreal h = qMin(qreal(pixHeight), height());
824 qreal x = (pixWidth > width()) ? -xOffset : 0;
825 qreal y = (pixHeight > height()) ? -yOffset : 0;
826 targetRect = QRectF(x + xOffset, y + yOffset, w, h);
827 sourceRect = QRectF(x, y, w, h);
828 break;
829 }
830
831 qreal nsWidth = (hWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->currentPix->width() / d->devicePixelRatio : d->currentPix->width();
832 qreal nsHeight = (vWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->currentPix->height() / d->devicePixelRatio : d->currentPix->height();
833 QRectF nsrect(sourceRect.x() / nsWidth,
834 sourceRect.y() / nsHeight,
835 sourceRect.width() / nsWidth,
836 sourceRect.height() / nsHeight);
837
838 if (targetRect.isEmpty()
839 || !qt_is_finite(targetRect.width()) || !qt_is_finite(targetRect.height())
840 || nsrect.isEmpty()
841 || !qt_is_finite(nsrect.width()) || !qt_is_finite(nsrect.height())) {
842 delete node;
843 return nullptr;
844 }
845
846 if (d->pixmapChanged) {
847 // force update the texture in the node to trigger reconstruction of
848 // geometry and the likes when a atlas segment has changed.
849 if (texture->isAtlasTexture() && (hWrap == QSGTexture::Repeat || vWrap == QSGTexture::Repeat || d->mipmap))
850 node->setTexture(texture->removedFromAtlas());
851 else
852 node->setTexture(texture);
853 d->pixmapChanged = false;
854 }
855
856 node->setMipmapFiltering(d->mipmap ? QSGTexture::Linear : QSGTexture::None);
857 node->setHorizontalWrapMode(hWrap);
858 node->setVerticalWrapMode(vWrap);
859 node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
860
861 node->setTargetRect(targetRect);
862 node->setInnerTargetRect(targetRect);
863 node->setSubSourceRect(nsrect);
864 node->setMirror(d->mirrorHorizontally, d->mirrorVertically);
865 node->setAntialiasing(d->antialiasing);
866 node->update();
867
868 return node;
869}
870
871void QQuickImage::pixmapChange()
872{
873 Q_D(QQuickImage);
874 // PreserveAspectFit calculates the implicit size differently so we
875 // don't call our superclass pixmapChange(), since that would
876 // result in the implicit size being set incorrectly, then updated
877 // in updatePaintedGeometry()
878 if (d->fillMode != PreserveAspectFit)
879 QQuickImageBase::pixmapChange();
880 updatePaintedGeometry();
881 d->pixmapChanged = true;
882
883 // When the pixmap changes, such as being deleted, we need to update the textures
884 update();
885}
886
887QQuickImage::VAlignment QQuickImage::verticalAlignment() const
888{
889 Q_D(const QQuickImage);
890 return d->vAlign;
891}
892
893void QQuickImage::setVerticalAlignment(VAlignment align)
894{
895 Q_D(QQuickImage);
896 if (d->vAlign == align)
897 return;
898
899 d->vAlign = align;
900 update();
901 updatePaintedGeometry();
902 emit verticalAlignmentChanged(align);
903}
904
905QQuickImage::HAlignment QQuickImage::horizontalAlignment() const
906{
907 Q_D(const QQuickImage);
908 return d->hAlign;
909}
910
911void QQuickImage::setHorizontalAlignment(HAlignment align)
912{
913 Q_D(QQuickImage);
914 if (d->hAlign == align)
915 return;
916
917 d->hAlign = align;
918 update();
919 updatePaintedGeometry();
920 emit horizontalAlignmentChanged(align);
921}
922
923/*!
924 \qmlproperty bool QtQuick::Image::mipmap
925 \since 5.3
926
927 This property holds whether the image uses mipmap filtering when scaled or
928 transformed.
929
930 Mipmap filtering gives better visual quality when scaling down
931 compared to smooth, but it may come at a performance cost (both when
932 initializing the image and during rendering).
933
934 By default, this property is set to false.
935
936 \sa smooth
937 */
938
939bool QQuickImage::mipmap() const
940{
941 Q_D(const QQuickImage);
942 return d->mipmap;
943}
944
945void QQuickImage::setMipmap(bool use)
946{
947 Q_D(QQuickImage);
948 if (d->mipmap == use)
949 return;
950 d->mipmap = use;
951 emit mipmapChanged(d->mipmap);
952
953 d->pixmapChanged = true;
954 if (isComponentComplete())
955 load();
956 update();
957}
958
959/*!
960 \qmlproperty bool QtQuick::Image::autoTransform
961 \since 5.5
962
963 This property holds whether the image should automatically apply
964 image transformation metadata such as EXIF orientation.
965
966 By default, this property is set to false.
967 */
968
969/*!
970 \qmlproperty int QtQuick::Image::currentFrame
971 \qmlproperty int QtQuick::Image::frameCount
972 \since 5.14
973
974 currentFrame is the frame that is currently visible. The default is \c 0.
975 You can set it to a number between \c 0 and \c {frameCount - 1} to display a
976 different frame, if the image contains multiple frames.
977
978 frameCount is the number of frames in the image. Most images have only one frame.
979*/
980
981/*!
982 \qmlproperty bool QtQuick::Image::retainWhileLoading
983 \since 6.8
984
985//! [qml-image-retainwhileloading]
986 This property defines the behavior when the \l source property is changed and loading happens
987 asynchronously. This is the case when the \l asynchronous property is set to \c true, or if the
988 image is not on the local file system.
989
990 If \c retainWhileLoading is \c false (the default), the old image is discarded immediately, and
991 the component is cleared while the new image is being loaded. If set to \c true, the old image
992 is retained and remains visible until the new one is ready.
993
994 Enabling this property can avoid flickering in cases where loading the new image takes a long
995 time. It comes at the cost of some extra memory use for double buffering while the new image is
996 being loaded.
997//! [qml-image-retainwhileloading]
998 */
999
1000QT_END_NAMESPACE
1001
1002#include "moc_qquickimage_p_p.cpp"
1003
1004#include "moc_qquickimage_p.cpp"