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)
95 \qmlclass Image QQuickImage
96 \inqmlmodule QtQuick 2
97 \ingroup qtquick-visual
99 \brief Displays an image
101 The Image type display an image.
103 The source of the image is specified as a URL using the \l source property.
104 Images can be supplied in any of the standard image formats supported by Qt,
105 including bitmap formats such as PNG and JPEG, and vector graphics formats
106 such as SVG. If you need to display animated images, use \l AnimatedSprite
109 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
110 specified, the Image automatically uses the size of the loaded image.
111 By default, specifying the width and height of the item causes the image
112 to be scaled to that size. This behavior can be changed by setting the
113 \l fillMode property, allowing the image to be stretched and tiled instead.
115 \section1 Example Usage
117 The following example shows the simplest usage of the Image type.
119 \snippet qml/image.qml document
122 \image declarative-qtlogo.png
127 \section1 Performance
129 By default, locally available images are loaded immediately, and the user interface
130 is blocked until loading is complete. If a large image is to be loaded, it may be
131 preferable to load the image in a low priority thread, by enabling the \l asynchronous
134 If the image is obtained from a network rather than a local resource, it is
135 automatically loaded asynchronously, and the \l progress and \l status properties
136 are updated as appropriate.
138 Images are cached and shared internally, so if several Image items have the same \l source,
139 only one copy of the image will be loaded.
141 \b Note: Images are often the greatest user of memory in QML user interfaces. It is recommended
142 that images which do not form part of the user interface have their
143 size bounded via the \l sourceSize property. This is especially important for content
144 that is loaded from external sources or provided by the user.
146 \sa {declarative/imageelements/image}{Image example}, QQuickImageProvider
149 QQuickImage::QQuickImage(QQuickItem *parent)
150 : QQuickImageBase(*(new QQuickImagePrivate), parent)
154 QQuickImage::QQuickImage(QQuickImagePrivate &dd, QQuickItem *parent)
155 : QQuickImageBase(dd, parent)
159 QQuickImage::~QQuickImage()
163 d->provider->deleteLater();
166 void QQuickImagePrivate::setImage(const QImage &image)
172 status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
178 \qmlproperty enumeration QtQuick2::Image::fillMode
180 Set this property to define what happens when the source image has a different size
184 \li Image.Stretch - the image is scaled to fit
185 \li Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
186 \li Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
187 \li Image.Tile - the image is duplicated horizontally and vertically
188 \li Image.TileVertically - the image is stretched horizontally and tiled vertically
189 \li Image.TileHorizontally - the image is stretched vertically and tiled horizontally
190 \li Image.Pad - the image is not transformed
196 \li \image declarative-qtlogo-stretch.png
197 \li Stretch (default)
200 width: 130; height: 100
207 \li \image declarative-qtlogo-preserveaspectfit.png
208 \li PreserveAspectFit
211 width: 130; height: 100
212 fillMode: Image.PreserveAspectFit
219 \li \image declarative-qtlogo-preserveaspectcrop.png
220 \li PreserveAspectCrop
223 width: 130; height: 100
224 fillMode: Image.PreserveAspectCrop
232 \li \image declarative-qtlogo-tile.png
236 width: 120; height: 120
243 \li \image declarative-qtlogo-tilevertically.png
247 width: 120; height: 120
248 fillMode: Image.TileVertically
255 \li \image declarative-qtlogo-tilehorizontally.png
259 width: 120; height: 120
260 fillMode: Image.TileHorizontally
268 Note that \c clip is \c false by default which means that the item might
269 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
271 \sa {declarative/imageelements/image}{Image example}
273 QQuickImage::FillMode QQuickImage::fillMode() const
275 Q_D(const QQuickImage);
279 void QQuickImage::setFillMode(FillMode mode)
282 if (d->fillMode == mode)
286 updatePaintedGeometry();
287 emit fillModeChanged();
292 \qmlproperty real QtQuick2::Image::paintedWidth
293 \qmlproperty real QtQuick2::Image::paintedHeight
295 These properties hold the size of the image that is actually painted.
296 In most cases it is the same as \c width and \c height, but when using a
297 \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop
298 \c paintedWidth or \c paintedHeight can be smaller or larger than
299 \c width and \c height of the Image item.
301 qreal QQuickImage::paintedWidth() const
303 Q_D(const QQuickImage);
304 return d->paintedWidth;
307 qreal QQuickImage::paintedHeight() const
309 Q_D(const QQuickImage);
310 return d->paintedHeight;
314 \qmlproperty enumeration QtQuick2::Image::status
316 This property holds the status of image loading. It can be one of:
318 \li Image.Null - no image has been set
319 \li Image.Ready - the image has been loaded
320 \li Image.Loading - the image is currently being loaded
321 \li Image.Error - an error occurred while loading the image
324 Use this status to provide an update or respond to the status change in some way.
325 For example, you could:
328 \li Trigger a state change:
330 State { name: 'loaded'; when: image.status == Image.Ready }
333 \li Implement an \c onStatusChanged signal handler:
337 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
341 \li Bind to the status value:
343 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
351 \qmlproperty real QtQuick2::Image::progress
353 This property holds the progress of image loading, from 0.0 (nothing loaded)
360 \qmlproperty bool QtQuick2::Image::smooth
362 Set this property if you want the image to be smoothly filtered when scaled or
363 transformed. Smooth filtering gives better visual quality, but is slower. If
364 the image is displayed at its natural size, this property has no visual or
367 \note Generally scaling artifacts are only visible if the image is stationary on
368 the screen. A common pattern when animating an image is to disable smooth
369 filtering at the beginning of the animation and reenable it at the conclusion.
373 \qmlproperty QSize QtQuick2::Image::sourceSize
375 This property holds the actual width and height of the loaded image.
377 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
378 the painting of the image, this property sets the actual number of pixels
379 stored for the loaded image so that large images do not use more
380 memory than necessary. For example, this ensures the image in memory is no
381 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
382 \l {Item::}{height} values:
391 source: "reallyBigImage.jpg"
392 sourceSize.width: 1024
393 sourceSize.height: 1024
398 If the image's actual size is larger than the sourceSize, the image is scaled down.
399 If only one dimension of the size is set to greater than 0, the
400 other dimension is set in proportion to preserve the source image's aspect ratio.
401 (The \l fillMode is independent of this.)
403 If both the sourceSize.width and sourceSize.height are set the image will be scaled
404 down to fit within the specified size, maintaining the image's aspect ratio. The actual
405 size of the image after scaling is available via \l implicitWidth and \l implicitHeight.
407 If the source is an intrinsically scalable image (eg. SVG), this property
408 determines the size of the loaded image regardless of intrinsic size.
409 Avoid changing this property dynamically; rendering an SVG is \e slow compared
412 If the source is a non-scalable image (eg. JPEG), the loaded image will
413 be no greater than this property specifies. For some formats (currently only JPEG),
414 the whole image will never actually be loaded into memory.
416 sourceSize can be cleared to the natural size of the image
417 by setting sourceSize to \c undefined.
419 \note \e {Changing this property dynamically causes the image source to be reloaded,
420 potentially even from the network, if it is not in the disk cache.}
424 \qmlproperty url QtQuick2::Image::source
426 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
428 The URL may be absolute, or relative to the URL of the component.
430 \sa QQuickImageProvider
434 \qmlproperty bool QtQuick2::Image::asynchronous
436 Specifies that images on the local filesystem should be loaded
437 asynchronously in a separate thread. The default value is
438 false, causing the user interface thread to block while the
439 image is loaded. Setting \a asynchronous to true is useful where
440 maintaining a responsive user interface is more desirable
441 than having images immediately visible.
443 Note that this property is only valid for images read from the
444 local filesystem. Images loaded via a network resource (e.g. HTTP)
445 are always loaded asynchronously.
449 \qmlproperty bool QtQuick2::Image::cache
451 Specifies whether the image should be cached. The default value is
452 true. Setting \a cache to false is useful when dealing with large images,
453 to make sure that they aren't cached at the expense of small 'ui element' images.
457 \qmlproperty bool QtQuick2::Image::mirror
459 This property holds whether the image should be horizontally inverted
460 (effectively displaying a mirrored image).
462 The default value is false.
466 \qmlproperty enumeration QtQuick2::Image::horizontalAlignment
467 \qmlproperty enumeration QtQuick2::Image::verticalAlignment
469 Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned.
471 The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
472 The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
473 and \c Image.AlignVCenter.
475 void QQuickImage::updatePaintedGeometry()
479 if (d->fillMode == PreserveAspectFit) {
480 if (!d->pix.width() || !d->pix.height()) {
481 setImplicitSize(0, 0);
484 qreal w = widthValid() ? width() : d->pix.width();
485 qreal widthScale = w / qreal(d->pix.width());
486 qreal h = heightValid() ? height() : d->pix.height();
487 qreal heightScale = h / qreal(d->pix.height());
488 if (widthScale <= heightScale) {
490 d->paintedHeight = widthScale * qreal(d->pix.height());
491 } else if (heightScale < widthScale) {
492 d->paintedWidth = heightScale * qreal(d->pix.width());
493 d->paintedHeight = h;
495 qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : d->pix.height();
496 qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : d->pix.width();
497 setImplicitSize(iWidth, iHeight);
499 } else if (d->fillMode == PreserveAspectCrop) {
500 if (!d->pix.width() || !d->pix.height())
502 qreal widthScale = width() / qreal(d->pix.width());
503 qreal heightScale = height() / qreal(d->pix.height());
504 if (widthScale < heightScale) {
505 widthScale = heightScale;
506 } else if (heightScale < widthScale) {
507 heightScale = widthScale;
510 d->paintedHeight = heightScale * qreal(d->pix.height());
511 d->paintedWidth = widthScale * qreal(d->pix.width());
512 } else if (d->fillMode == Pad) {
513 d->paintedWidth = d->pix.width();
514 d->paintedHeight = d->pix.height();
516 d->paintedWidth = width();
517 d->paintedHeight = height();
519 emit paintedGeometryChanged();
522 void QQuickImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
524 QQuickImageBase::geometryChanged(newGeometry, oldGeometry);
525 updatePaintedGeometry();
528 QRectF QQuickImage::boundingRect() const
530 Q_D(const QQuickImage);
531 return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
534 QSGTextureProvider *QQuickImage::textureProvider() const
536 Q_D(const QQuickImage);
538 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
540 && d->sceneGraphContext()
541 && QThread::currentThread() == d->sceneGraphContext()->thread(),
542 "QQuickImage::textureProvider",
543 "Cannot be used outside the GUI thread");
544 QQuickImagePrivate *dd = const_cast<QQuickImagePrivate *>(d);
545 dd->provider = new QQuickImageTextureProvider;
546 dd->provider->m_smooth = d->smooth;
547 dd->provider->m_texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), canvas());
553 QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
557 QSGTexture *texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), canvas());
559 // Copy over the current texture state into the texture provider...
561 d->provider->m_smooth = d->smooth;
562 d->provider->m_texture = texture;
565 if (!texture || width() <= 0 || height() <= 0) {
570 QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
572 d->pixmapChanged = true;
573 node = d->sceneGraphContext()->createImageNode();
574 node->setTexture(texture);
577 if (d->pixmapChanged) {
578 // force update the texture in the node to trigger reconstruction of
579 // geometry and the likes when a atlas segment has changed.
581 node->setTexture(texture);
582 d->pixmapChanged = false;
587 QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
588 QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
590 qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width();
591 qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height();
594 if (d->hAlign == QQuickImage::AlignHCenter)
595 xOffset = qCeil((width() - pixWidth) / 2.);
596 else if (d->hAlign == QQuickImage::AlignRight)
597 xOffset = qCeil(width() - pixWidth);
600 if (d->vAlign == QQuickImage::AlignVCenter)
601 yOffset = qCeil((height() - pixHeight) / 2.);
602 else if (d->vAlign == QQuickImage::AlignBottom)
603 yOffset = qCeil(height() - pixHeight);
605 switch (d->fillMode) {
608 targetRect = QRectF(0, 0, width(), height());
609 sourceRect = d->pix.rect();
612 case PreserveAspectFit:
613 targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
614 sourceRect = d->pix.rect();
617 case PreserveAspectCrop: {
618 targetRect = QRect(0, 0, width(), height());
619 qreal wscale = width() / qreal(d->pix.width());
620 qreal hscale = height() / qreal(d->pix.height());
622 if (wscale > hscale) {
623 int src = (hscale / wscale) * qreal(d->pix.height());
625 if (d->vAlign == QQuickImage::AlignVCenter)
626 y = qCeil((d->pix.height() - src) / 2.);
627 else if (d->vAlign == QQuickImage::AlignBottom)
628 y = qCeil(d->pix.height() - src);
629 sourceRect = QRectF(0, y, d->pix.width(), src);
632 int src = (wscale / hscale) * qreal(d->pix.width());
634 if (d->hAlign == QQuickImage::AlignHCenter)
635 x = qCeil((d->pix.width() - src) / 2.);
636 else if (d->hAlign == QQuickImage::AlignRight)
637 x = qCeil(d->pix.width() - src);
638 sourceRect = QRectF(x, 0, src, d->pix.height());
644 targetRect = QRectF(0, 0, width(), height());
645 sourceRect = QRectF(-xOffset, -yOffset, width(), height());
646 hWrap = QSGTexture::Repeat;
647 vWrap = QSGTexture::Repeat;
650 case TileHorizontally:
651 targetRect = QRectF(0, 0, width(), height());
652 sourceRect = QRectF(-xOffset, 0, width(), d->pix.height());
653 hWrap = QSGTexture::Repeat;
657 targetRect = QRectF(0, 0, width(), height());
658 sourceRect = QRectF(0, -yOffset, d->pix.width(), height());
659 vWrap = QSGTexture::Repeat;
663 qreal w = qMin(qreal(d->pix.width()), width());
664 qreal h = qMin(qreal(d->pix.height()), height());
665 qreal x = (d->pix.width() > width()) ? -xOffset : 0;
666 qreal y = (d->pix.height() > height()) ? -yOffset : 0;
667 targetRect = QRectF(x + xOffset, y + yOffset, w, h);
668 sourceRect = QRectF(x, y, w, h);
672 QRectF nsrect(sourceRect.x() / d->pix.width(),
673 sourceRect.y() / d->pix.height(),
674 sourceRect.width() / d->pix.width(),
675 sourceRect.height() / d->pix.height());
677 node->setHorizontalWrapMode(hWrap);
678 node->setVerticalWrapMode(vWrap);
679 node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
681 node->setTargetRect(targetRect);
682 node->setInnerTargetRect(targetRect);
683 node->setSubSourceRect(nsrect);
684 node->setMirror(d->mirror);
685 node->setAntialiasing(d->antialiasing);
691 void QQuickImage::pixmapChange()
694 // PreserveAspectFit calculates the implicit size differently so we
695 // don't call our superclass pixmapChange(), since that would
696 // result in the implicit size being set incorrectly, then updated
697 // in updatePaintedGeometry()
698 if (d->fillMode != PreserveAspectFit)
699 QQuickImageBase::pixmapChange();
700 updatePaintedGeometry();
701 d->pixmapChanged = true;
703 // When the pixmap changes, such as being deleted, we need to update the textures
707 QQuickImage::VAlignment QQuickImage::verticalAlignment() const
709 Q_D(const QQuickImage);
713 void QQuickImage::setVerticalAlignment(VAlignment align)
716 if (d->vAlign == align)
721 updatePaintedGeometry();
722 emit verticalAlignmentChanged(align);
725 QQuickImage::HAlignment QQuickImage::horizontalAlignment() const
727 Q_D(const QQuickImage);
731 void QQuickImage::setHorizontalAlignment(HAlignment align)
734 if (d->hAlign == align)
739 updatePaintedGeometry();
740 emit horizontalAlignmentChanged(align);