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

source code of qtdeclarative/src/quick/items/qquickimage.cpp