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

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