1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickimage_p.h"
43 #include "qquickimage_p_p.h"
45 #include <QtQuick/qsgtextureprovider.h>
47 #include <QtQuick/private/qsgcontext_p.h>
48 #include <private/qsgadaptationlayer_p.h>
50 #include <QtGui/qpainter.h>
55 class QQuickImageTextureProvider : public QSGTextureProvider
59 QQuickImageTextureProvider()
65 QSGTexture *texture() const {
67 m_texture->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest);
68 m_texture->setMipmapFiltering(QSGTexture::Nearest);
69 m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge);
70 m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge);
75 friend class QQuickImage;
77 QSGTexture *m_texture;
81 #include "qquickimage.moc"
83 QQuickImagePrivate::QQuickImagePrivate()
84 : fillMode(QQuickImage::Stretch)
87 , pixmapChanged(false)
88 , hAlign(QQuickImage::AlignHCenter)
89 , vAlign(QQuickImage::AlignVCenter)
96 \instantiates QQuickImage
97 \inqmlmodule QtQuick 2
98 \ingroup qtquick-visual
100 \brief Displays an image
102 The Image type display an image.
104 The source of the image is specified as a URL using the \l source property.
105 Images can be supplied in any of the standard image formats supported by Qt,
106 including bitmap formats such as PNG and JPEG, and vector graphics formats
107 such as SVG. If you need to display animated images, use \l AnimatedSprite
110 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
111 specified, the Image automatically uses the size of the loaded image.
112 By default, specifying the width and height of the item causes the image
113 to be scaled to that size. This behavior can be changed by setting the
114 \l fillMode property, allowing the image to be stretched and tiled instead.
116 \section1 Example Usage
118 The following example shows the simplest usage of the Image type.
120 \snippet qml/image.qml document
123 \image declarative-qtlogo.png
128 \section1 Performance
130 By default, locally available images are loaded immediately, and the user interface
131 is blocked until loading is complete. If a large image is to be loaded, it may be
132 preferable to load the image in a low priority thread, by enabling the \l asynchronous
135 If the image is obtained from a network rather than a local resource, it is
136 automatically loaded asynchronously, and the \l progress and \l status properties
137 are updated as appropriate.
139 Images are cached and shared internally, so if several Image items have the same \l source,
140 only one copy of the image will be loaded.
142 \b Note: Images are often the greatest user of memory in QML user interfaces. It is recommended
143 that images which do not form part of the user interface have their
144 size bounded via the \l sourceSize property. This is especially important for content
145 that is loaded from external sources or provided by the user.
147 \sa {declarative/imageelements/image}{Image example}, QQuickImageProvider
150 QQuickImage::QQuickImage(QQuickItem *parent)
151 : QQuickImageBase(*(new QQuickImagePrivate), parent)
155 QQuickImage::QQuickImage(QQuickImagePrivate &dd, QQuickItem *parent)
156 : QQuickImageBase(dd, parent)
160 QQuickImage::~QQuickImage()
164 d->provider->deleteLater();
167 void QQuickImagePrivate::setImage(const QImage &image)
173 status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
179 \qmlproperty enumeration QtQuick2::Image::fillMode
181 Set this property to define what happens when the source image has a different size
185 \li Image.Stretch - the image is scaled to fit
186 \li Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
187 \li Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
188 \li Image.Tile - the image is duplicated horizontally and vertically
189 \li Image.TileVertically - the image is stretched horizontally and tiled vertically
190 \li Image.TileHorizontally - the image is stretched vertically and tiled horizontally
191 \li Image.Pad - the image is not transformed
197 \li \image declarative-qtlogo-stretch.png
198 \li Stretch (default)
201 width: 130; height: 100
207 \li \image declarative-qtlogo-preserveaspectfit.png
208 \li PreserveAspectFit
211 width: 130; height: 100
212 fillMode: Image.PreserveAspectFit
218 \li \image declarative-qtlogo-preserveaspectcrop.png
219 \li PreserveAspectCrop
222 width: 130; height: 100
223 fillMode: Image.PreserveAspectCrop
230 \li \image declarative-qtlogo-tile.png
234 width: 120; height: 120
241 \li \image declarative-qtlogo-tilevertically.png
245 width: 120; height: 120
246 fillMode: Image.TileVertically
252 \li \image declarative-qtlogo-tilehorizontally.png
256 width: 120; height: 120
257 fillMode: Image.TileHorizontally
264 Note that \c clip is \c false by default which means that the item might
265 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
267 \sa {declarative/imageelements/image}{Image example}
269 QQuickImage::FillMode QQuickImage::fillMode() const
271 Q_D(const QQuickImage);
275 void QQuickImage::setFillMode(FillMode mode)
278 if (d->fillMode == mode)
282 updatePaintedGeometry();
283 emit fillModeChanged();
288 \qmlproperty real QtQuick2::Image::paintedWidth
289 \qmlproperty real QtQuick2::Image::paintedHeight
291 These properties hold the size of the image that is actually painted.
292 In most cases it is the same as \c width and \c height, but when using a
293 \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop
294 \c paintedWidth or \c paintedHeight can be smaller or larger than
295 \c width and \c height of the Image item.
297 qreal QQuickImage::paintedWidth() const
299 Q_D(const QQuickImage);
300 return d->paintedWidth;
303 qreal QQuickImage::paintedHeight() const
305 Q_D(const QQuickImage);
306 return d->paintedHeight;
310 \qmlproperty enumeration QtQuick2::Image::status
312 This property holds the status of image loading. It can be one of:
314 \li Image.Null - no image has been set
315 \li Image.Ready - the image has been loaded
316 \li Image.Loading - the image is currently being loaded
317 \li Image.Error - an error occurred while loading the image
320 Use this status to provide an update or respond to the status change in some way.
321 For example, you could:
324 \li Trigger a state change:
326 State { name: 'loaded'; when: image.status == Image.Ready }
329 \li Implement an \c onStatusChanged signal handler:
333 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
337 \li Bind to the status value:
339 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
347 \qmlproperty real QtQuick2::Image::progress
349 This property holds the progress of image loading, from 0.0 (nothing loaded)
356 \qmlproperty bool QtQuick2::Image::smooth
358 This property holds whether the image is smoothly filtered when scaled or
359 transformed. Smooth filtering gives better visual quality, but it may be slower
360 on some hardware. If the image is displayed at its natural size, this property has
361 no visual or performance effect.
363 By default, this property is set to true.
367 \qmlproperty QSize QtQuick2::Image::sourceSize
369 This property holds the actual width and height of the loaded image.
371 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
372 the painting of the image, this property sets the actual number of pixels
373 stored for the loaded image so that large images do not use more
374 memory than necessary. For example, this ensures the image in memory is no
375 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
376 \l {Item::}{height} values:
385 source: "reallyBigImage.jpg"
386 sourceSize.width: 1024
387 sourceSize.height: 1024
392 If the image's actual size is larger than the sourceSize, the image is scaled down.
393 If only one dimension of the size is set to greater than 0, the
394 other dimension is set in proportion to preserve the source image's aspect ratio.
395 (The \l fillMode is independent of this.)
397 If both the sourceSize.width and sourceSize.height are set the image will be scaled
398 down to fit within the specified size, maintaining the image's aspect ratio. The actual
399 size of the image after scaling is available via \l Item::implicitWidth and \l Item::implicitHeight.
401 If the source is an intrinsically scalable image (eg. SVG), this property
402 determines the size of the loaded image regardless of intrinsic size.
403 Avoid changing this property dynamically; rendering an SVG is \e slow compared
406 If the source is a non-scalable image (eg. JPEG), the loaded image will
407 be no greater than this property specifies. For some formats (currently only JPEG),
408 the whole image will never actually be loaded into memory.
410 sourceSize can be cleared to the natural size of the image
411 by setting sourceSize to \c undefined.
413 \note \e {Changing this property dynamically causes the image source to be reloaded,
414 potentially even from the network, if it is not in the disk cache.}
418 \qmlproperty url QtQuick2::Image::source
420 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
422 The URL may be absolute, or relative to the URL of the component.
424 \sa QQuickImageProvider
428 \qmlproperty bool QtQuick2::Image::asynchronous
430 Specifies that images on the local filesystem should be loaded
431 asynchronously in a separate thread. The default value is
432 false, causing the user interface thread to block while the
433 image is loaded. Setting \a asynchronous to true is useful where
434 maintaining a responsive user interface is more desirable
435 than having images immediately visible.
437 Note that this property is only valid for images read from the
438 local filesystem. Images loaded via a network resource (e.g. HTTP)
439 are always loaded asynchronously.
443 \qmlproperty bool QtQuick2::Image::cache
445 Specifies whether the image should be cached. The default value is
446 true. Setting \a cache to false is useful when dealing with large images,
447 to make sure that they aren't cached at the expense of small 'ui element' images.
451 \qmlproperty bool QtQuick2::Image::mirror
453 This property holds whether the image should be horizontally inverted
454 (effectively displaying a mirrored image).
456 The default value is false.
460 \qmlproperty enumeration QtQuick2::Image::horizontalAlignment
461 \qmlproperty enumeration QtQuick2::Image::verticalAlignment
463 Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned.
465 The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
466 The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
467 and \c Image.AlignVCenter.
469 void QQuickImage::updatePaintedGeometry()
473 if (d->fillMode == PreserveAspectFit) {
474 if (!d->pix.width() || !d->pix.height()) {
475 setImplicitSize(0, 0);
478 qreal w = widthValid() ? width() : d->pix.width();
479 qreal widthScale = w / qreal(d->pix.width());
480 qreal h = heightValid() ? height() : d->pix.height();
481 qreal heightScale = h / qreal(d->pix.height());
482 if (widthScale <= heightScale) {
484 d->paintedHeight = widthScale * qreal(d->pix.height());
485 } else if (heightScale < widthScale) {
486 d->paintedWidth = heightScale * qreal(d->pix.width());
487 d->paintedHeight = h;
489 qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : d->pix.height();
490 qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : d->pix.width();
491 setImplicitSize(iWidth, iHeight);
493 } else if (d->fillMode == PreserveAspectCrop) {
494 if (!d->pix.width() || !d->pix.height())
496 qreal widthScale = width() / qreal(d->pix.width());
497 qreal heightScale = height() / qreal(d->pix.height());
498 if (widthScale < heightScale) {
499 widthScale = heightScale;
500 } else if (heightScale < widthScale) {
501 heightScale = widthScale;
504 d->paintedHeight = heightScale * qreal(d->pix.height());
505 d->paintedWidth = widthScale * qreal(d->pix.width());
506 } else if (d->fillMode == Pad) {
507 d->paintedWidth = d->pix.width();
508 d->paintedHeight = d->pix.height();
510 d->paintedWidth = width();
511 d->paintedHeight = height();
513 emit paintedGeometryChanged();
516 void QQuickImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
518 QQuickImageBase::geometryChanged(newGeometry, oldGeometry);
519 updatePaintedGeometry();
522 QRectF QQuickImage::boundingRect() const
524 Q_D(const QQuickImage);
525 return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
528 QSGTextureProvider *QQuickImage::textureProvider() const
530 Q_D(const QQuickImage);
532 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
534 && d->sceneGraphContext()
535 && QThread::currentThread() == d->sceneGraphContext()->thread(),
536 "QQuickImage::textureProvider",
537 "Cannot be used outside the GUI thread");
538 QQuickImagePrivate *dd = const_cast<QQuickImagePrivate *>(d);
539 dd->provider = new QQuickImageTextureProvider;
540 dd->provider->m_smooth = d->smooth;
541 dd->provider->m_texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), window());
547 QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
551 QSGTexture *texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), window());
553 // Copy over the current texture state into the texture provider...
555 d->provider->m_smooth = d->smooth;
556 d->provider->m_texture = texture;
559 if (!texture || width() <= 0 || height() <= 0) {
564 QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
566 d->pixmapChanged = true;
567 node = d->sceneGraphContext()->createImageNode();
568 node->setTexture(texture);
571 if (d->pixmapChanged) {
572 // force update the texture in the node to trigger reconstruction of
573 // geometry and the likes when a atlas segment has changed.
575 node->setTexture(texture);
576 d->pixmapChanged = false;
581 QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
582 QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
584 qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width();
585 qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height();
588 if (d->hAlign == QQuickImage::AlignHCenter)
589 xOffset = qCeil((width() - pixWidth) / 2.);
590 else if (d->hAlign == QQuickImage::AlignRight)
591 xOffset = qCeil(width() - pixWidth);
594 if (d->vAlign == QQuickImage::AlignVCenter)
595 yOffset = qCeil((height() - pixHeight) / 2.);
596 else if (d->vAlign == QQuickImage::AlignBottom)
597 yOffset = qCeil(height() - pixHeight);
599 switch (d->fillMode) {
602 targetRect = QRectF(0, 0, width(), height());
603 sourceRect = d->pix.rect();
606 case PreserveAspectFit:
607 targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
608 sourceRect = d->pix.rect();
611 case PreserveAspectCrop: {
612 targetRect = QRect(0, 0, width(), height());
613 qreal wscale = width() / qreal(d->pix.width());
614 qreal hscale = height() / qreal(d->pix.height());
616 if (wscale > hscale) {
617 int src = (hscale / wscale) * qreal(d->pix.height());
619 if (d->vAlign == QQuickImage::AlignVCenter)
620 y = qCeil((d->pix.height() - src) / 2.);
621 else if (d->vAlign == QQuickImage::AlignBottom)
622 y = qCeil(d->pix.height() - src);
623 sourceRect = QRectF(0, y, d->pix.width(), src);
626 int src = (wscale / hscale) * qreal(d->pix.width());
628 if (d->hAlign == QQuickImage::AlignHCenter)
629 x = qCeil((d->pix.width() - src) / 2.);
630 else if (d->hAlign == QQuickImage::AlignRight)
631 x = qCeil(d->pix.width() - src);
632 sourceRect = QRectF(x, 0, src, d->pix.height());
638 targetRect = QRectF(0, 0, width(), height());
639 sourceRect = QRectF(-xOffset, -yOffset, width(), height());
640 hWrap = QSGTexture::Repeat;
641 vWrap = QSGTexture::Repeat;
644 case TileHorizontally:
645 targetRect = QRectF(0, 0, width(), height());
646 sourceRect = QRectF(-xOffset, 0, width(), d->pix.height());
647 hWrap = QSGTexture::Repeat;
651 targetRect = QRectF(0, 0, width(), height());
652 sourceRect = QRectF(0, -yOffset, d->pix.width(), height());
653 vWrap = QSGTexture::Repeat;
657 qreal w = qMin(qreal(d->pix.width()), width());
658 qreal h = qMin(qreal(d->pix.height()), height());
659 qreal x = (d->pix.width() > width()) ? -xOffset : 0;
660 qreal y = (d->pix.height() > height()) ? -yOffset : 0;
661 targetRect = QRectF(x + xOffset, y + yOffset, w, h);
662 sourceRect = QRectF(x, y, w, h);
666 QRectF nsrect(sourceRect.x() / d->pix.width(),
667 sourceRect.y() / d->pix.height(),
668 sourceRect.width() / d->pix.width(),
669 sourceRect.height() / d->pix.height());
671 node->setHorizontalWrapMode(hWrap);
672 node->setVerticalWrapMode(vWrap);
673 node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
675 node->setTargetRect(targetRect);
676 node->setInnerTargetRect(targetRect);
677 node->setSubSourceRect(nsrect);
678 node->setMirror(d->mirror);
679 node->setAntialiasing(d->antialiasing);
685 void QQuickImage::pixmapChange()
688 // PreserveAspectFit calculates the implicit size differently so we
689 // don't call our superclass pixmapChange(), since that would
690 // result in the implicit size being set incorrectly, then updated
691 // in updatePaintedGeometry()
692 if (d->fillMode != PreserveAspectFit)
693 QQuickImageBase::pixmapChange();
694 updatePaintedGeometry();
695 d->pixmapChanged = true;
697 // When the pixmap changes, such as being deleted, we need to update the textures
701 QQuickImage::VAlignment QQuickImage::verticalAlignment() const
703 Q_D(const QQuickImage);
707 void QQuickImage::setVerticalAlignment(VAlignment align)
710 if (d->vAlign == align)
715 updatePaintedGeometry();
716 emit verticalAlignmentChanged(align);
719 QQuickImage::HAlignment QQuickImage::horizontalAlignment() const
721 Q_D(const QQuickImage);
725 void QQuickImage::setHorizontalAlignment(HAlignment align)
728 if (d->hAlign == align)
733 updatePaintedGeometry();
734 emit horizontalAlignmentChanged(align);