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 {Qt logo displayed in an Image element}
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 {Qt logo stretched to fill
264 the entire image area}
265 \li Stretch (default)
266 \qml
267 Image {
268 width: 130; height: 100
269 source: "qtlogo.png"
270 }
271 \endqml
272
273 \row
274 \li \image declarative-qtlogo-preserveaspectfit.png {Qt logo scaled
275 uniformly to fit within the image area}
276 \li PreserveAspectFit
277 \qml
278 Image {
279 width: 130; height: 100
280 fillMode: Image.PreserveAspectFit
281 source: "qtlogo.png"
282 }
283 \endqml
284
285 \row
286 \li \image declarative-qtlogo-preserveaspectcrop.png {Qt logo scaled
287 uniformly to fill the area with edges cropped}
288 \li PreserveAspectCrop
289 \qml
290 Image {
291 width: 130; height: 100
292 fillMode: Image.PreserveAspectCrop
293 source: "qtlogo.png"
294 clip: true
295 }
296 \endqml
297
298 \row
299 \li \image declarative-qtlogo-tile.png {Qt logo tiled horizontally
300 and vertically to fill the area}
301 \li Tile
302 \qml
303 Image {
304 width: 120; height: 120
305 fillMode: Image.Tile
306 horizontalAlignment: Image.AlignLeft
307 verticalAlignment: Image.AlignTop
308 source: "qtlogo.png"
309 }
310 \endqml
311
312 \row
313 \li \image declarative-qtlogo-tilevertically.png {Qt logo stretched
314 horizontally and tiled vertically}
315 \li TileVertically
316 \qml
317 Image {
318 width: 120; height: 120
319 fillMode: Image.TileVertically
320 verticalAlignment: Image.AlignTop
321 source: "qtlogo.png"
322 }
323 \endqml
324
325 \row
326 \li \image declarative-qtlogo-tilehorizontally.png {Qt logo stretched
327 vertically and tiled horizontally}
328 \li TileHorizontally
329 \qml
330 Image {
331 width: 120; height: 120
332 fillMode: Image.TileHorizontally
333 verticalAlignment: Image.AlignLeft
334 source: "qtlogo.png"
335 }
336 \endqml
337
338 \endtable
339
340 Note that \c clip is \c false by default which means that the item might
341 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
342
343 \sa {Qt Quick Examples - Image Elements}
344*/
345QQuickImage::FillMode QQuickImage::fillMode() const
346{
347 Q_D(const QQuickImage);
348 return d->fillMode;
349}
350
351void QQuickImage::setFillMode(FillMode mode)
352{
353 Q_D(QQuickImage);
354 if (d->fillMode == mode)
355 return;
356 d->fillMode = mode;
357 if ((mode == PreserveAspectCrop) != d->providerOptions.preserveAspectRatioCrop()) {
358 d->providerOptions.setPreserveAspectRatioCrop(mode == PreserveAspectCrop);
359 if (isComponentComplete())
360 load();
361 } else if ((mode == PreserveAspectFit) != d->providerOptions.preserveAspectRatioFit()) {
362 d->providerOptions.setPreserveAspectRatioFit(mode == PreserveAspectFit);
363 if (isComponentComplete())
364 load();
365 }
366 update();
367 updatePaintedGeometry();
368 emit fillModeChanged();
369}
370
371/*!
372 \qmlproperty real QtQuick::Image::paintedWidth
373 \qmlproperty real QtQuick::Image::paintedHeight
374 \readonly
375
376 These properties hold the size of the image that is actually painted.
377 In most cases it is the same as \c width and \c height, but when using an
378 \l {fillMode}{Image.PreserveAspectFit} or an \l {fillMode}{Image.PreserveAspectCrop}
379 \c paintedWidth or \c paintedHeight can be smaller or larger than
380 \c width and \c height of the Image item.
381*/
382qreal QQuickImage::paintedWidth() const
383{
384 Q_D(const QQuickImage);
385 return d->paintedWidth;
386}
387
388qreal QQuickImage::paintedHeight() const
389{
390 Q_D(const QQuickImage);
391 return d->paintedHeight;
392}
393
394/*!
395 \qmlproperty enumeration QtQuick::Image::status
396 \readonly
397
398 This property holds the status of image loading. It can be one of:
399
400 \value Image.Null No image has been set
401 \value Image.Ready The image has been loaded
402 \value Image.Loading The image is currently being loaded
403 \value Image.Error An error occurred while loading the image
404
405 Use this status to provide an update or respond to the status change in some way.
406 For example, you could:
407
408 \list
409 \li Trigger a state change:
410 \qml
411 State { name: 'loaded'; when: image.status == Image.Ready }
412 \endqml
413
414 \li Implement an \c onStatusChanged signal handler:
415 \qml
416 Image {
417 id: image
418 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
419 }
420 \endqml
421
422 \li Bind to the status value:
423 \qml
424 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
425 \endqml
426 \endlist
427
428 \sa progress
429*/
430
431/*!
432 \qmlproperty real QtQuick::Image::progress
433 \readonly
434
435 This property holds the progress of image loading, from 0.0 (nothing loaded)
436 to 1.0 (finished).
437
438 \sa status
439*/
440
441/*!
442 \qmlproperty bool QtQuick::Image::smooth
443
444 This property holds whether the image is smoothly filtered when scaled or
445 transformed. Smooth filtering gives better visual quality, but it may be slower
446 on some hardware. If the image is displayed at its natural size, this property has
447 no visual or performance effect.
448
449 By default, this property is set to true.
450
451 \sa mipmap
452*/
453
454/*!
455 \qmlproperty size QtQuick::Image::sourceSize
456
457 This property holds the scaled width and height of the full-frame image.
458
459 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
460 the painting of the image, this property sets the maximum number of pixels
461 stored for the loaded image so that large images do not use more
462 memory than necessary. For example, this ensures the image in memory is no
463 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
464 \l {Item::}{height} values:
465
466 \code
467 Rectangle {
468 width: ...
469 height: ...
470
471 Image {
472 anchors.fill: parent
473 source: "reallyBigImage.jpg"
474 sourceSize.width: 1024
475 sourceSize.height: 1024
476 }
477 }
478 \endcode
479
480 If the image's actual size is larger than the sourceSize, the image is scaled down.
481 If only one dimension of the size is set to greater than 0, the
482 other dimension is set in proportion to preserve the source image's aspect ratio.
483 (The \l fillMode is independent of this.)
484
485 If both the sourceSize.width and sourceSize.height are set, the image will be scaled
486 down to fit within the specified size (unless PreserveAspectCrop or PreserveAspectFit
487 are used, then it will be scaled to match the optimal size for cropping/fitting),
488 maintaining the image's aspect ratio. The actual
489 size of the image after scaling is available via \l Item::implicitWidth and \l Item::implicitHeight.
490
491 If the source is an intrinsically scalable image (eg. SVG), this property
492 determines the size of the loaded image regardless of intrinsic size.
493 Avoid changing this property dynamically; rendering an SVG is \e slow compared
494 to an image.
495
496 If the source is a non-scalable image (eg. JPEG), the loaded image will
497 be no greater than this property specifies. For some formats (currently only JPEG),
498 the whole image will never actually be loaded into memory.
499
500 If the \l sourceClipRect property is also set, \c sourceSize determines the scale,
501 but it will be clipped to the size of the clip rectangle.
502
503 sourceSize can be cleared to the natural size of the image
504 by setting sourceSize to \c undefined.
505
506 \note \e {Changing this property dynamically causes the image source to be reloaded,
507 potentially even from the network, if it is not in the disk cache.}
508
509 \sa {Qt Quick Examples - Pointer Handlers}
510*/
511
512/*!
513 \qmlproperty rect QtQuick::Image::sourceClipRect
514 \since 5.15
515
516 This property, if set, holds the rectangular region of the source image to
517 be loaded.
518
519 The \c sourceClipRect works together with the \l sourceSize property to
520 conserve system resources when only a portion of an image needs to be
521 loaded.
522
523 \code
524 Rectangle {
525 width: ...
526 height: ...
527
528 Image {
529 anchors.fill: parent
530 source: "reallyBigImage.svg"
531 sourceSize.width: 1024
532 sourceSize.height: 1024
533 sourceClipRect: Qt.rect(100, 100, 512, 512)
534 }
535 }
536 \endcode
537
538 In the above example, we conceptually scale the SVG graphic to 1024x1024
539 first, and then cut out a region of interest that is 512x512 pixels from a
540 location 100 pixels from the top and left edges. Thus \c sourceSize
541 determines the scale, but the actual output image is 512x512 pixels.
542
543 Some image formats are able to conserve CPU time by rendering only the
544 specified region. Others will need to load the entire image first and then
545 clip it to the specified region.
546
547 This property can be cleared to reload the entire image by setting
548 \c sourceClipRect to \c undefined.
549
550 \note \e {Changing this property dynamically causes the image source to be reloaded,
551 potentially even from the network, if it is not in the disk cache.}
552
553 \note Sub-pixel clipping is not supported: the given rectangle will be
554 passed to \l QImageReader::setScaledClipRect().
555*/
556
557/*!
558 \qmlproperty url QtQuick::Image::source
559
560 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
561
562 The URL may be absolute, or relative to the URL of the component.
563
564 \sa QQuickImageProvider, {Compressed Texture Files}, {Automatic Detection of File Extension}
565*/
566
567/*!
568 \qmlproperty bool QtQuick::Image::asynchronous
569
570 Specifies that images on the local filesystem should be loaded
571 asynchronously in a separate thread. The default value is
572 false, causing the user interface thread to block while the
573 image is loaded. Setting \a asynchronous to true is useful where
574 maintaining a responsive user interface is more desirable
575 than having images immediately visible.
576
577 Note that this property is only valid for images read from the
578 local filesystem. Images loaded via a network resource (e.g. HTTP)
579 are always loaded asynchronously.
580*/
581
582/*!
583 \qmlproperty bool QtQuick::Image::cache
584
585 Specifies whether the image should be cached. The default value is
586 true. Setting \a cache to false is useful when dealing with large images,
587 to make sure that they aren't cached at the expense of small 'ui element' images.
588*/
589
590/*!
591 \qmlproperty bool QtQuick::Image::mirror
592
593 This property holds whether the image should be horizontally inverted
594 (effectively displaying a mirrored image).
595
596 The default value is false.
597*/
598
599/*!
600 \qmlproperty bool QtQuick::Image::mirrorVertically
601
602 This property holds whether the image should be vertically inverted
603 (effectively displaying a mirrored image).
604
605 The default value is false.
606
607 \since 6.2
608*/
609
610/*!
611 \qmlproperty enumeration QtQuick::Image::horizontalAlignment
612 \qmlproperty enumeration QtQuick::Image::verticalAlignment
613
614 Sets the horizontal and vertical alignment of the image. By default, the image is center aligned.
615
616 The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
617 The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
618 and \c Image.AlignVCenter.
619*/
620void QQuickImage::updatePaintedGeometry()
621{
622 Q_D(QQuickImage);
623
624 if (d->fillMode == PreserveAspectFit) {
625 if (!d->currentPix->width() || !d->currentPix->height()) {
626 setImplicitSize(0, 0);
627 return;
628 }
629 const qreal pixWidth = d->currentPix->width() / d->devicePixelRatio;
630 const qreal pixHeight = d->currentPix->height() / d->devicePixelRatio;
631 const qreal w = widthValid() ? width() : pixWidth;
632 const qreal widthScale = w / pixWidth;
633 const qreal h = heightValid() ? height() : pixHeight;
634 const qreal heightScale = h / pixHeight;
635 if (widthScale <= heightScale) {
636 d->paintedWidth = w;
637 d->paintedHeight = widthScale * pixHeight;
638 } else if (heightScale < widthScale) {
639 d->paintedWidth = heightScale * pixWidth;
640 d->paintedHeight = h;
641 }
642 const qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : pixHeight;
643 const qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : pixWidth;
644 setImplicitSize(iWidth, iHeight);
645
646 } else if (d->fillMode == PreserveAspectCrop) {
647 if (!d->currentPix->width() || !d->currentPix->height())
648 return;
649 const qreal pixWidth = d->currentPix->width() / d->devicePixelRatio;
650 const qreal pixHeight = d->currentPix->height() / d->devicePixelRatio;
651 qreal widthScale = width() / pixWidth;
652 qreal heightScale = height() / pixHeight;
653 if (widthScale < heightScale) {
654 widthScale = heightScale;
655 } else if (heightScale < widthScale) {
656 heightScale = widthScale;
657 }
658
659 d->paintedHeight = heightScale * pixHeight;
660 d->paintedWidth = widthScale * pixWidth;
661 } else if (d->fillMode == Pad) {
662 d->paintedWidth = d->currentPix->width() / d->devicePixelRatio;
663 d->paintedHeight = d->currentPix->height() / d->devicePixelRatio;
664 } else {
665 d->paintedWidth = width();
666 d->paintedHeight = height();
667 }
668 emit paintedGeometryChanged();
669}
670
671void QQuickImage::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
672{
673 QQuickImageBase::geometryChange(newGeometry, oldGeometry);
674 if (newGeometry.size() != oldGeometry.size())
675 updatePaintedGeometry();
676}
677
678QRectF QQuickImage::boundingRect() const
679{
680 Q_D(const QQuickImage);
681 return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
682}
683
684QSGTextureProvider *QQuickImage::textureProvider() const
685{
686 Q_D(const QQuickImage);
687
688 // When Item::layer::enabled == true, QQuickItem will be a texture
689 // provider. In this case we should prefer to return the layer rather
690 // than the image itself. The layer will include any children and any
691 // the image's wrap and fill mode.
692 if (QQuickItem::isTextureProvider())
693 return QQuickItem::textureProvider();
694
695 if (!d->window || !d->sceneGraphRenderContext() || QThread::currentThread() != d->sceneGraphRenderContext()->thread()) {
696 qWarning("QQuickImage::textureProvider: can only be queried on the rendering thread of an exposed window");
697 return nullptr;
698 }
699
700 if (!d->provider) {
701 QQuickImagePrivate *dd = const_cast<QQuickImagePrivate *>(d);
702 dd->provider = new QQuickImageTextureProvider;
703 dd->provider->m_smooth = d->smooth;
704 dd->provider->m_mipmap = d->mipmap;
705 dd->provider->updateTexture(d->sceneGraphRenderContext()->textureForFactory(d->currentPix->textureFactory(), window()));
706 }
707
708 return d->provider;
709}
710
711void QQuickImage::invalidateSceneGraph()
712{
713 Q_D(QQuickImage);
714 delete d->provider;
715 d->provider = nullptr;
716}
717
718void QQuickImage::releaseResources()
719{
720 Q_D(QQuickImage);
721 if (d->provider) {
722 QQuickWindowQObjectCleanupJob::schedule(window(), d->provider);
723 d->provider = nullptr;
724 }
725}
726
727QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
728{
729 Q_D(QQuickImage);
730
731 QSGTexture *texture = d->sceneGraphRenderContext()->textureForFactory(d->currentPix->textureFactory(), window());
732
733 // Copy over the current texture state into the texture provider...
734 if (d->provider) {
735 d->provider->m_smooth = d->smooth;
736 d->provider->m_mipmap = d->mipmap;
737 d->provider->updateTexture(texture);
738 }
739
740 if (!texture || width() <= 0 || height() <= 0) {
741 delete oldNode;
742 return nullptr;
743 }
744
745 QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
746 if (!node) {
747 d->pixmapChanged = true;
748 node = d->sceneGraphContext()->createInternalImageNode(d->sceneGraphRenderContext());
749 }
750
751 QRectF targetRect;
752 QRectF sourceRect;
753 QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
754 QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
755
756 qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->currentPix->width() / d->devicePixelRatio;
757 qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->currentPix->height() / d->devicePixelRatio;
758
759 int xOffset = 0;
760 if (d->hAlign == QQuickImage::AlignHCenter)
761 xOffset = (width() - pixWidth) / 2;
762 else if (d->hAlign == QQuickImage::AlignRight)
763 xOffset = qCeil(width() - pixWidth);
764
765 int yOffset = 0;
766 if (d->vAlign == QQuickImage::AlignVCenter)
767 yOffset = (height() - pixHeight) / 2;
768 else if (d->vAlign == QQuickImage::AlignBottom)
769 yOffset = qCeil(height() - pixHeight);
770
771 switch (d->fillMode) {
772 case Stretch:
773 targetRect = QRectF(0, 0, width(), height());
774 sourceRect = d->currentPix->rect();
775 break;
776
777 case PreserveAspectFit:
778 targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
779 sourceRect = d->currentPix->rect();
780 break;
781
782 case PreserveAspectCrop: {
783 targetRect = QRectF(0, 0, width(), height());
784 qreal wscale = width() / qreal(d->currentPix->width());
785 qreal hscale = height() / qreal(d->currentPix->height());
786
787 if (wscale > hscale) {
788 int src = (hscale / wscale) * qreal(d->currentPix->height());
789 int y = 0;
790 if (d->vAlign == QQuickImage::AlignVCenter)
791 y = qCeil((d->currentPix->height() - src) / 2.);
792 else if (d->vAlign == QQuickImage::AlignBottom)
793 y = qCeil(d->currentPix->height() - src);
794 sourceRect = QRectF(0, y, d->currentPix->width(), src);
795
796 } else {
797 int src = (wscale / hscale) * qreal(d->currentPix->width());
798 int x = 0;
799 if (d->hAlign == QQuickImage::AlignHCenter)
800 x = qCeil((d->currentPix->width() - src) / 2.);
801 else if (d->hAlign == QQuickImage::AlignRight)
802 x = qCeil(d->currentPix->width() - src);
803 sourceRect = QRectF(x, 0, src, d->currentPix->height());
804 }
805 }
806 break;
807
808 case Tile:
809 targetRect = QRectF(0, 0, width(), height());
810 sourceRect = QRectF(-xOffset, -yOffset, width(), height());
811 hWrap = QSGTexture::Repeat;
812 vWrap = QSGTexture::Repeat;
813 break;
814
815 case TileHorizontally:
816 targetRect = QRectF(0, 0, width(), height());
817 sourceRect = QRectF(-xOffset, 0, width(), d->currentPix->height());
818 hWrap = QSGTexture::Repeat;
819 break;
820
821 case TileVertically:
822 targetRect = QRectF(0, 0, width(), height());
823 sourceRect = QRectF(0, -yOffset, d->currentPix->width(), height());
824 vWrap = QSGTexture::Repeat;
825 break;
826
827 case Pad:
828 qreal w = qMin(qreal(pixWidth), width());
829 qreal h = qMin(qreal(pixHeight), height());
830 qreal x = (pixWidth > width()) ? -xOffset : 0;
831 qreal y = (pixHeight > height()) ? -yOffset : 0;
832 targetRect = QRectF(x + xOffset, y + yOffset, w, h);
833 sourceRect = QRectF(x, y, w, h);
834 break;
835 }
836
837 qreal nsWidth = (hWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->currentPix->width() / d->devicePixelRatio : d->currentPix->width();
838 qreal nsHeight = (vWrap == QSGTexture::Repeat || d->fillMode == Pad) ? d->currentPix->height() / d->devicePixelRatio : d->currentPix->height();
839 QRectF nsrect(sourceRect.x() / nsWidth,
840 sourceRect.y() / nsHeight,
841 sourceRect.width() / nsWidth,
842 sourceRect.height() / nsHeight);
843
844 if (targetRect.isEmpty()
845 || !qt_is_finite(targetRect.width()) || !qt_is_finite(targetRect.height())
846 || nsrect.isEmpty()
847 || !qt_is_finite(nsrect.width()) || !qt_is_finite(nsrect.height())) {
848 delete node;
849 return nullptr;
850 }
851
852 if (d->pixmapChanged) {
853 // force update the texture in the node to trigger reconstruction of
854 // geometry and the likes when a atlas segment has changed.
855 if (texture->isAtlasTexture() && (hWrap == QSGTexture::Repeat || vWrap == QSGTexture::Repeat || d->mipmap))
856 node->setTexture(texture->removedFromAtlas());
857 else
858 node->setTexture(texture);
859 d->pixmapChanged = false;
860 }
861
862 node->setMipmapFiltering(d->mipmap ? QSGTexture::Linear : QSGTexture::None);
863 node->setHorizontalWrapMode(hWrap);
864 node->setVerticalWrapMode(vWrap);
865 node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
866
867 node->setTargetRect(targetRect);
868 node->setInnerTargetRect(targetRect);
869 node->setSubSourceRect(nsrect);
870 node->setMirror(d->mirrorHorizontally, d->mirrorVertically);
871 node->setAntialiasing(d->antialiasing);
872 node->update();
873
874 return node;
875}
876
877void QQuickImage::pixmapChange()
878{
879 Q_D(QQuickImage);
880 // PreserveAspectFit calculates the implicit size differently so we
881 // don't call our superclass pixmapChange(), since that would
882 // result in the implicit size being set incorrectly, then updated
883 // in updatePaintedGeometry()
884 if (d->fillMode != PreserveAspectFit)
885 QQuickImageBase::pixmapChange();
886 updatePaintedGeometry();
887 d->pixmapChanged = true;
888
889 // When the pixmap changes, such as being deleted, we need to update the textures
890 update();
891}
892
893QQuickImage::VAlignment QQuickImage::verticalAlignment() const
894{
895 Q_D(const QQuickImage);
896 return d->vAlign;
897}
898
899void QQuickImage::setVerticalAlignment(VAlignment align)
900{
901 Q_D(QQuickImage);
902 if (d->vAlign == align)
903 return;
904
905 d->vAlign = align;
906 update();
907 updatePaintedGeometry();
908 emit verticalAlignmentChanged(align);
909}
910
911QQuickImage::HAlignment QQuickImage::horizontalAlignment() const
912{
913 Q_D(const QQuickImage);
914 return d->hAlign;
915}
916
917void QQuickImage::setHorizontalAlignment(HAlignment align)
918{
919 Q_D(QQuickImage);
920 if (d->hAlign == align)
921 return;
922
923 d->hAlign = align;
924 update();
925 updatePaintedGeometry();
926 emit horizontalAlignmentChanged(align);
927}
928
929/*!
930 \qmlproperty bool QtQuick::Image::mipmap
931 \since 5.3
932
933 This property holds whether the image uses mipmap filtering when scaled or
934 transformed.
935
936 Mipmap filtering gives better visual quality when scaling down
937 compared to smooth, but it may come at a performance cost (both when
938 initializing the image and during rendering).
939
940 By default, this property is set to false.
941
942 \sa smooth
943 */
944
945bool QQuickImage::mipmap() const
946{
947 Q_D(const QQuickImage);
948 return d->mipmap;
949}
950
951void QQuickImage::setMipmap(bool use)
952{
953 Q_D(QQuickImage);
954 if (d->mipmap == use)
955 return;
956 d->mipmap = use;
957 emit mipmapChanged(d->mipmap);
958
959 d->pixmapChanged = true;
960 if (isComponentComplete())
961 load();
962 update();
963}
964
965/*!
966 \qmlproperty bool QtQuick::Image::autoTransform
967 \since 5.5
968
969 This property holds whether the image should automatically apply
970 image transformation metadata such as EXIF orientation.
971
972 By default, this property is set to false.
973 */
974
975/*!
976 \qmlproperty int QtQuick::Image::currentFrame
977 \qmlproperty int QtQuick::Image::frameCount
978 \since 5.14
979
980 currentFrame is the frame that is currently visible. The default is \c 0.
981 You can set it to a number between \c 0 and \c {frameCount - 1} to display a
982 different frame, if the image contains multiple frames.
983
984 frameCount is the number of frames in the image. Most images have only one frame.
985*/
986
987/*!
988 \qmlproperty bool QtQuick::Image::retainWhileLoading
989 \since 6.8
990
991//! [qml-image-retainwhileloading]
992 This property defines the behavior when the \l source property is changed and loading happens
993 asynchronously. This is the case when the \l asynchronous property is set to \c true, or if the
994 image is not on the local file system.
995
996 If \c retainWhileLoading is \c false (the default), the old image is discarded immediately, and
997 the component is cleared while the new image is being loaded. If set to \c true, the old image
998 is retained and remains visible until the new one is ready.
999
1000 Enabling this property can avoid flickering in cases where loading the new image takes a long
1001 time. It comes at the cost of some extra memory use for double buffering while the new image is
1002 being loaded.
1003//! [qml-image-retainwhileloading]
1004 */
1005
1006QT_END_NAMESPACE
1007
1008#include "moc_qquickimage_p_p.cpp"
1009
1010#include "moc_qquickimage_p.cpp"
Combined button and popup list for selecting options.