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 if (m_texture && m_texture->isAtlasTexture())
68 const_cast<QQuickImageTextureProvider *>(this)->m_texture = m_texture->removedFromAtlas();
71 m_texture->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest);
72 m_texture->setMipmapFiltering(QSGTexture::Nearest);
73 m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge);
74 m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge);
79 friend class QQuickImage;
81 QSGTexture *m_texture;
85 #include "qquickimage.moc"
87 QQuickImagePrivate::QQuickImagePrivate()
88 : fillMode(QQuickImage::Stretch)
91 , pixmapChanged(false)
92 , hAlign(QQuickImage::AlignHCenter)
93 , vAlign(QQuickImage::AlignVCenter)
99 \qmlclass Image QQuickImage
100 \inqmlmodule QtQuick 2
101 \ingroup qml-basic-visual-elements
102 \brief The Image element displays an image in a declarative user interface
105 The Image element is used to display images in a declarative user interface.
107 The source of the image is specified as a URL using the \l source property.
108 Images can be supplied in any of the standard image formats supported by Qt,
109 including bitmap formats such as PNG and JPEG, and vector graphics formats
110 such as SVG. If you need to display animated images, use the \l AnimatedImage
113 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
114 specified, the Image element automatically uses the size of the loaded image.
115 By default, specifying the width and height of the element causes the image
116 to be scaled to that size. This behavior can be changed by setting the
117 \l fillMode property, allowing the image to be stretched and tiled instead.
119 \section1 Example Usage
121 The following example shows the simplest usage of the Image element.
123 \snippet doc/src/snippets/qml/image.qml document
126 \image declarative-qtlogo.png
131 \section1 Performance
133 By default, locally available images are loaded immediately, and the user interface
134 is blocked until loading is complete. If a large image is to be loaded, it may be
135 preferable to load the image in a low priority thread, by enabling the \l asynchronous
138 If the image is obtained from a network rather than a local resource, it is
139 automatically loaded asynchronously, and the \l progress and \l status properties
140 are updated as appropriate.
142 Images are cached and shared internally, so if several Image elements have the same \l source,
143 only one copy of the image will be loaded.
145 \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended
146 that images which do not form part of the user interface have their
147 size bounded via the \l sourceSize property. This is especially important for content
148 that is loaded from external sources or provided by the user.
150 \sa {declarative/imageelements/image}{Image example}, QQmlImageProvider
153 QQuickImage::QQuickImage(QQuickItem *parent)
154 : QQuickImageBase(*(new QQuickImagePrivate), parent)
158 QQuickImage::QQuickImage(QQuickImagePrivate &dd, QQuickItem *parent)
159 : QQuickImageBase(dd, parent)
163 QQuickImage::~QQuickImage()
167 d->provider->deleteLater();
170 void QQuickImagePrivate::setImage(const QImage &image)
176 status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
182 \qmlproperty enumeration QtQuick2::Image::fillMode
184 Set this property to define what happens when the source image has a different size
188 \o Image.Stretch - the image is scaled to fit
189 \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
190 \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
191 \o Image.Tile - the image is duplicated horizontally and vertically
192 \o Image.TileVertically - the image is stretched horizontally and tiled vertically
193 \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally
194 \o Image.Pad - the image is not transformed
200 \o \image declarative-qtlogo-stretch.png
204 width: 130; height: 100
211 \o \image declarative-qtlogo-preserveaspectfit.png
215 width: 130; height: 100
216 fillMode: Image.PreserveAspectFit
223 \o \image declarative-qtlogo-preserveaspectcrop.png
224 \o PreserveAspectCrop
227 width: 130; height: 100
228 fillMode: Image.PreserveAspectCrop
236 \o \image declarative-qtlogo-tile.png
240 width: 120; height: 120
247 \o \image declarative-qtlogo-tilevertically.png
251 width: 120; height: 120
252 fillMode: Image.TileVertically
259 \o \image declarative-qtlogo-tilehorizontally.png
263 width: 120; height: 120
264 fillMode: Image.TileHorizontally
272 Note that \c clip is \c false by default which means that the element might
273 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
275 \sa {declarative/imageelements/image}{Image example}
277 QQuickImage::FillMode QQuickImage::fillMode() const
279 Q_D(const QQuickImage);
283 void QQuickImage::setFillMode(FillMode mode)
286 if (d->fillMode == mode)
290 updatePaintedGeometry();
291 emit fillModeChanged();
296 \qmlproperty real QtQuick2::Image::paintedWidth
297 \qmlproperty real QtQuick2::Image::paintedHeight
299 These properties hold the size of the image that is actually painted.
300 In most cases it is the same as \c width and \c height, but when using a
301 \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop
302 \c paintedWidth or \c paintedHeight can be smaller or larger than
303 \c width and \c height of the Image element.
305 qreal QQuickImage::paintedWidth() const
307 Q_D(const QQuickImage);
308 return d->paintedWidth;
311 qreal QQuickImage::paintedHeight() const
313 Q_D(const QQuickImage);
314 return d->paintedHeight;
318 \qmlproperty enumeration QtQuick2::Image::status
320 This property holds the status of image loading. It can be one of:
322 \o Image.Null - no image has been set
323 \o Image.Ready - the image has been loaded
324 \o Image.Loading - the image is currently being loaded
325 \o Image.Error - an error occurred while loading the image
328 Use this status to provide an update or respond to the status change in some way.
329 For example, you could:
332 \o Trigger a state change:
334 State { name: 'loaded'; when: image.status == Image.Ready }
337 \o Implement an \c onStatusChanged signal handler:
341 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
345 \o Bind to the status value:
347 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
355 \qmlproperty real QtQuick2::Image::progress
357 This property holds the progress of image loading, from 0.0 (nothing loaded)
364 \qmlproperty bool QtQuick2::Image::smooth
366 Set this property if you want the image to be smoothly filtered when scaled or
367 transformed. Smooth filtering gives better visual quality, but is slower. If
368 the image is displayed at its natural size, this property has no visual or
371 \note Generally scaling artifacts are only visible if the image is stationary on
372 the screen. A common pattern when animating an image is to disable smooth
373 filtering at the beginning of the animation and reenable it at the conclusion.
377 \qmlproperty QSize QtQuick2::Image::sourceSize
379 This property holds the actual width and height of the loaded image.
381 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
382 the painting of the image, this property sets the actual number of pixels
383 stored for the loaded image so that large images do not use more
384 memory than necessary. For example, this ensures the image in memory is no
385 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
386 \l {Item::}{height} values:
395 source: "reallyBigImage.jpg"
396 sourceSize.width: 1024
397 sourceSize.height: 1024
402 If the image's actual size is larger than the sourceSize, the image is scaled down.
403 If only one dimension of the size is set to greater than 0, the
404 other dimension is set in proportion to preserve the source image's aspect ratio.
405 (The \l fillMode is independent of this.)
407 If both the sourceSize.width and sourceSize.height are set the image will be scaled
408 down to fit within the specified size, maintaining the image's aspect ratio. The actual
409 size of the image after scaling is available via \l implicitWidth and \l implicitHeight.
411 If the source is an intrinsically scalable image (eg. SVG), this property
412 determines the size of the loaded image regardless of intrinsic size.
413 Avoid changing this property dynamically; rendering an SVG is \e slow compared
416 If the source is a non-scalable image (eg. JPEG), the loaded image will
417 be no greater than this property specifies. For some formats (currently only JPEG),
418 the whole image will never actually be loaded into memory.
420 sourceSize can be cleared to the natural size of the image
421 by setting sourceSize to \c undefined.
423 \note \e {Changing this property dynamically causes the image source to be reloaded,
424 potentially even from the network, if it is not in the disk cache.}
428 \qmlproperty url QtQuick2::Image::source
430 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
432 The URL may be absolute, or relative to the URL of the component.
434 \sa QQmlImageProvider
438 \qmlproperty bool QtQuick2::Image::asynchronous
440 Specifies that images on the local filesystem should be loaded
441 asynchronously in a separate thread. The default value is
442 false, causing the user interface thread to block while the
443 image is loaded. Setting \a asynchronous to true is useful where
444 maintaining a responsive user interface is more desirable
445 than having images immediately visible.
447 Note that this property is only valid for images read from the
448 local filesystem. Images loaded via a network resource (e.g. HTTP)
449 are always loaded asynchronously.
453 \qmlproperty bool QtQuick2::Image::cache
455 Specifies whether the image should be cached. The default value is
456 true. Setting \a cache to false is useful when dealing with large images,
457 to make sure that they aren't cached at the expense of small 'ui element' images.
461 \qmlproperty bool QtQuick2::Image::mirror
463 This property holds whether the image should be horizontally inverted
464 (effectively displaying a mirrored image).
466 The default value is false.
470 \qmlproperty enumeration QtQuick2::Image::horizontalAlignment
471 \qmlproperty enumeration QtQuick2::Image::verticalAlignment
473 Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned.
475 The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
476 The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
477 and \c Image.AlignVCenter.
479 void QQuickImage::updatePaintedGeometry()
483 if (d->fillMode == PreserveAspectFit) {
484 if (!d->pix.width() || !d->pix.height()) {
485 setImplicitSize(0, 0);
488 qreal w = widthValid() ? width() : d->pix.width();
489 qreal widthScale = w / qreal(d->pix.width());
490 qreal h = heightValid() ? height() : d->pix.height();
491 qreal heightScale = h / qreal(d->pix.height());
492 if (widthScale <= heightScale) {
494 d->paintedHeight = widthScale * qreal(d->pix.height());
495 } else if (heightScale < widthScale) {
496 d->paintedWidth = heightScale * qreal(d->pix.width());
497 d->paintedHeight = h;
499 qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : d->pix.height();
500 qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : d->pix.width();
501 setImplicitSize(iWidth, iHeight);
503 } else if (d->fillMode == PreserveAspectCrop) {
504 if (!d->pix.width() || !d->pix.height())
506 qreal widthScale = width() / qreal(d->pix.width());
507 qreal heightScale = height() / qreal(d->pix.height());
508 if (widthScale < heightScale) {
509 widthScale = heightScale;
510 } else if (heightScale < widthScale) {
511 heightScale = widthScale;
514 d->paintedHeight = heightScale * qreal(d->pix.height());
515 d->paintedWidth = widthScale * qreal(d->pix.width());
516 } else if (d->fillMode == Pad) {
517 d->paintedWidth = d->pix.width();
518 d->paintedHeight = d->pix.height();
520 d->paintedWidth = width();
521 d->paintedHeight = height();
523 emit paintedGeometryChanged();
526 void QQuickImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
528 QQuickImageBase::geometryChanged(newGeometry, oldGeometry);
529 updatePaintedGeometry();
532 QRectF QQuickImage::boundingRect() const
534 Q_D(const QQuickImage);
535 return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
538 QSGTextureProvider *QQuickImage::textureProvider() const
540 Q_D(const QQuickImage);
542 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
544 && d->sceneGraphContext()
545 && QThread::currentThread() == d->sceneGraphContext()->thread(),
546 "QQuickImage::textureProvider",
547 "Cannot be used outside the GUI thread");
548 QQuickImagePrivate *dd = const_cast<QQuickImagePrivate *>(d);
549 dd->provider = new QQuickImageTextureProvider;
550 dd->provider->m_smooth = d->smooth;
551 dd->provider->m_texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), canvas());
557 QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
561 QSGTexture *texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), canvas());
563 // Copy over the current texture state into the texture provider...
565 d->provider->m_smooth = d->smooth;
566 d->provider->m_texture = texture;
569 if (!texture || width() <= 0 || height() <= 0) {
574 QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
576 d->pixmapChanged = true;
577 node = d->sceneGraphContext()->createImageNode();
578 node->setTexture(texture);
581 if (d->pixmapChanged) {
582 // force update the texture in the node to trigger reconstruction of
583 // geometry and the likes when a atlas segment has changed.
585 node->setTexture(texture);
586 d->pixmapChanged = false;
591 QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
592 QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
594 qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width();
595 qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height();
598 if (d->hAlign == QQuickImage::AlignHCenter)
599 xOffset = qCeil((width() - pixWidth) / 2.);
600 else if (d->hAlign == QQuickImage::AlignRight)
601 xOffset = qCeil(width() - pixWidth);
604 if (d->vAlign == QQuickImage::AlignVCenter)
605 yOffset = qCeil((height() - pixHeight) / 2.);
606 else if (d->vAlign == QQuickImage::AlignBottom)
607 yOffset = qCeil(height() - pixHeight);
609 switch (d->fillMode) {
612 targetRect = QRectF(0, 0, width(), height());
613 sourceRect = d->pix.rect();
616 case PreserveAspectFit:
617 targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
618 sourceRect = d->pix.rect();
621 case PreserveAspectCrop: {
622 targetRect = QRect(0, 0, width(), height());
623 qreal wscale = width() / qreal(d->pix.width());
624 qreal hscale = height() / qreal(d->pix.height());
626 if (wscale > hscale) {
627 int src = (hscale / wscale) * qreal(d->pix.height());
629 if (d->vAlign == QQuickImage::AlignVCenter)
630 y = qCeil((d->pix.height() - src) / 2.);
631 else if (d->vAlign == QQuickImage::AlignBottom)
632 y = qCeil(d->pix.height() - src);
633 sourceRect = QRectF(0, y, d->pix.width(), src);
636 int src = (wscale / hscale) * qreal(d->pix.width());
638 if (d->hAlign == QQuickImage::AlignHCenter)
639 x = qCeil((d->pix.width() - src) / 2.);
640 else if (d->hAlign == QQuickImage::AlignRight)
641 x = qCeil(d->pix.width() - src);
642 sourceRect = QRectF(x, 0, src, d->pix.height());
648 targetRect = QRectF(0, 0, width(), height());
649 sourceRect = QRectF(-xOffset, -yOffset, width(), height());
650 hWrap = QSGTexture::Repeat;
651 vWrap = QSGTexture::Repeat;
654 case TileHorizontally:
655 targetRect = QRectF(0, 0, width(), height());
656 sourceRect = QRectF(-xOffset, 0, width(), d->pix.height());
657 hWrap = QSGTexture::Repeat;
661 targetRect = QRectF(0, 0, width(), height());
662 sourceRect = QRectF(0, -yOffset, d->pix.width(), height());
663 vWrap = QSGTexture::Repeat;
667 qreal w = qMin(qreal(d->pix.width()), width());
668 qreal h = qMin(qreal(d->pix.height()), height());
669 qreal x = (d->pix.width() > width()) ? -xOffset : 0;
670 qreal y = (d->pix.height() > height()) ? -yOffset : 0;
671 targetRect = QRectF(x + xOffset, y + yOffset, w, h);
672 sourceRect = QRectF(x, y, w, h);
676 QRectF nsrect(sourceRect.x() / d->pix.width(),
677 sourceRect.y() / d->pix.height(),
678 sourceRect.width() / d->pix.width(),
679 sourceRect.height() / d->pix.height());
682 qreal oldLeft = nsrect.left();
683 nsrect.setLeft(nsrect.right());
684 nsrect.setRight(oldLeft);
687 node->setHorizontalWrapMode(hWrap);
688 node->setVerticalWrapMode(vWrap);
689 node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
691 node->setTargetRect(targetRect);
692 node->setSourceRect(nsrect);
698 void QQuickImage::pixmapChange()
701 // PreserveAspectFit calculates the implicit size differently so we
702 // don't call our superclass pixmapChange(), since that would
703 // result in the implicit size being set incorrectly, then updated
704 // in updatePaintedGeometry()
705 if (d->fillMode != PreserveAspectFit)
706 QQuickImageBase::pixmapChange();
707 updatePaintedGeometry();
708 d->pixmapChanged = true;
710 // When the pixmap changes, such as being deleted, we need to update the textures
714 QQuickImage::VAlignment QQuickImage::verticalAlignment() const
716 Q_D(const QQuickImage);
720 void QQuickImage::setVerticalAlignment(VAlignment align)
723 if (d->vAlign == align)
728 updatePaintedGeometry();
729 emit verticalAlignmentChanged(align);
732 QQuickImage::HAlignment QQuickImage::horizontalAlignment() const
734 Q_D(const QQuickImage);
738 void QQuickImage::setHorizontalAlignment(HAlignment align)
741 if (d->hAlign == align)
746 updatePaintedGeometry();
747 emit horizontalAlignmentChanged(align);