1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgimage_p.h"
43 #include "qsgimage_p_p.h"
45 #include <private/qsgtextureprovider_p.h>
47 #include <private/qsgcontext_p.h>
48 #include <private/qsgadaptationlayer_p.h>
50 #include <QtGui/qpainter.h>
55 class QSGImageTextureProvider : public QSGTextureProvider
59 QSGImageTextureProvider(const QSGImage *imageItem)
60 : d((QSGImagePrivate *) QSGItemPrivate::get(imageItem))
66 QSGTexture *texture() const {
68 m_texture->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest);
69 m_texture->setMipmapFiltering(QSGTexture::Nearest);
70 m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge);
71 m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge);
76 friend class QSGImage;
79 QSGTexture *m_texture;
83 #include "qsgimage.moc"
85 QSGImagePrivate::QSGImagePrivate()
86 : fillMode(QSGImage::Stretch)
89 , pixmapChanged(false)
90 , hAlign(QSGImage::AlignHCenter)
91 , vAlign(QSGImage::AlignVCenter)
97 \qmlclass Image QSGImage
98 \inqmlmodule QtQuick 2
99 \ingroup qml-basic-visual-elements
100 \brief The Image element displays an image in a declarative user interface
103 The Image element is used to display images in a declarative user interface.
105 The source of the image is specified as a URL using the \l source property.
106 Images can be supplied in any of the standard image formats supported by Qt,
107 including bitmap formats such as PNG and JPEG, and vector graphics formats
108 such as SVG. If you need to display animated images, use the \l AnimatedImage
111 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
112 specified, the Image element automatically uses the size of the loaded image.
113 By default, specifying the width and height of the element causes the image
114 to be scaled to that size. This behavior can be changed by setting the
115 \l fillMode property, allowing the image to be stretched and tiled instead.
117 \section1 Example Usage
119 The following example shows the simplest usage of the Image element.
121 \snippet doc/src/snippets/declarative/image.qml document
124 \image declarative-qtlogo.png
129 \section1 Performance
131 By default, locally available images are loaded immediately, and the user interface
132 is blocked until loading is complete. If a large image is to be loaded, it may be
133 preferable to load the image in a low priority thread, by enabling the \l asynchronous
136 If the image is obtained from a network rather than a local resource, it is
137 automatically loaded asynchronously, and the \l progress and \l status properties
138 are updated as appropriate.
140 Images are cached and shared internally, so if several Image elements have the same \l source,
141 only one copy of the image will be loaded.
143 \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended
144 that images which do not form part of the user interface have their
145 size bounded via the \l sourceSize property. This is especially important for content
146 that is loaded from external sources or provided by the user.
148 \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider
151 QSGImage::QSGImage(QSGItem *parent)
152 : QSGImageBase(*(new QSGImagePrivate), parent)
156 QSGImage::QSGImage(QSGImagePrivate &dd, QSGItem *parent)
157 : QSGImageBase(dd, parent)
161 QSGImage::~QSGImage()
165 d->provider->deleteLater();
168 void QSGImagePrivate::setPixmap(const QPixmap &pixmap)
171 pix.setPixmap(pixmap);
174 status = pix.isNull() ? QSGImageBase::Null : QSGImageBase::Ready;
180 \qmlproperty enumeration QtQuick2::Image::fillMode
182 Set this property to define what happens when the source image has a different size
186 \o Image.Stretch - the image is scaled to fit
187 \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
188 \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
189 \o Image.Tile - the image is duplicated horizontally and vertically
190 \o Image.TileVertically - the image is stretched horizontally and tiled vertically
191 \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally
192 \o Image.Pad - the image is not transformed
198 \o \image declarative-qtlogo-stretch.png
202 width: 130; height: 100
209 \o \image declarative-qtlogo-preserveaspectfit.png
213 width: 130; height: 100
214 fillMode: Image.PreserveAspectFit
221 \o \image declarative-qtlogo-preserveaspectcrop.png
222 \o PreserveAspectCrop
225 width: 130; height: 100
226 fillMode: Image.PreserveAspectCrop
234 \o \image declarative-qtlogo-tile.png
238 width: 120; height: 120
245 \o \image declarative-qtlogo-tilevertically.png
249 width: 120; height: 120
250 fillMode: Image.TileVertically
257 \o \image declarative-qtlogo-tilehorizontally.png
261 width: 120; height: 120
262 fillMode: Image.TileHorizontally
270 Note that \c clip is \c false by default which means that the element might
271 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
273 \sa {declarative/imageelements/image}{Image example}
275 QSGImage::FillMode QSGImage::fillMode() const
281 void QSGImage::setFillMode(FillMode mode)
284 if (d->fillMode == mode)
288 updatePaintedGeometry();
289 emit fillModeChanged();
294 \qmlproperty real QtQuick2::Image::paintedWidth
295 \qmlproperty real QtQuick2::Image::paintedHeight
297 These properties hold the size of the image that is actually painted.
298 In most cases it is the same as \c width and \c height, but when using a
299 \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop
300 \c paintedWidth or \c paintedHeight can be smaller or larger than
301 \c width and \c height of the Image element.
303 qreal QSGImage::paintedWidth() const
306 return d->paintedWidth;
309 qreal QSGImage::paintedHeight() const
312 return d->paintedHeight;
316 \qmlproperty enumeration QtQuick2::Image::status
318 This property holds the status of image loading. It can be one of:
320 \o Image.Null - no image has been set
321 \o Image.Ready - the image has been loaded
322 \o Image.Loading - the image is currently being loaded
323 \o Image.Error - an error occurred while loading the image
326 Use this status to provide an update or respond to the status change in some way.
327 For example, you could:
330 \o Trigger a state change:
332 State { name: 'loaded'; when: image.status == Image.Ready }
335 \o Implement an \c onStatusChanged signal handler:
339 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
343 \o Bind to the status value:
345 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
353 \qmlproperty real QtQuick2::Image::progress
355 This property holds the progress of image loading, from 0.0 (nothing loaded)
362 \qmlproperty bool QtQuick2::Image::smooth
364 Set this property if you want the image to be smoothly filtered when scaled or
365 transformed. Smooth filtering gives better visual quality, but is slower. If
366 the image is displayed at its natural size, this property has no visual or
369 \note Generally scaling artifacts are only visible if the image is stationary on
370 the screen. A common pattern when animating an image is to disable smooth
371 filtering at the beginning of the animation and reenable it at the conclusion.
375 \qmlproperty QSize QtQuick2::Image::sourceSize
377 This property holds the actual width and height of the loaded image.
379 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
380 the painting of the image, this property sets the actual number of pixels
381 stored for the loaded image so that large images do not use more
382 memory than necessary. For example, this ensures the image in memory is no
383 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
384 \l {Item::}{height} values:
393 source: "reallyBigImage.jpg"
394 sourceSize.width: 1024
395 sourceSize.height: 1024
400 If the image's actual size is larger than the sourceSize, the image is scaled down.
401 If only one dimension of the size is set to greater than 0, the
402 other dimension is set in proportion to preserve the source image's aspect ratio.
403 (The \l fillMode is independent of this.)
405 If the source is an instrinsically scalable image (eg. SVG), this property
406 determines the size of the loaded image regardless of intrinsic size.
407 Avoid changing this property dynamically; rendering an SVG is \e slow compared
410 If the source is a non-scalable image (eg. JPEG), the loaded image will
411 be no greater than this property specifies. For some formats (currently only JPEG),
412 the whole image will never actually be loaded into memory.
414 Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image
415 by setting sourceSize to \c undefined.
417 \note \e {Changing this property dynamically causes the image source to be reloaded,
418 potentially even from the network, if it is not in the disk cache.}
422 \qmlproperty url QtQuick2::Image::source
424 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
426 The URL may be absolute, or relative to the URL of the component.
428 \sa QDeclarativeImageProvider
432 \qmlproperty bool QtQuick2::Image::asynchronous
434 Specifies that images on the local filesystem should be loaded
435 asynchronously in a separate thread. The default value is
436 false, causing the user interface thread to block while the
437 image is loaded. Setting \a asynchronous to true is useful where
438 maintaining a responsive user interface is more desirable
439 than having images immediately visible.
441 Note that this property is only valid for images read from the
442 local filesystem. Images loaded via a network resource (e.g. HTTP)
443 are always loaded asynchonously.
447 \qmlproperty bool QtQuick2::Image::cache
449 Specifies whether the image should be cached. The default value is
450 true. Setting \a cache to false is useful when dealing with large images,
451 to make sure that they aren't cached at the expense of small 'ui element' images.
455 \qmlproperty bool QtQuick2::Image::mirror
457 This property holds whether the image should be horizontally inverted
458 (effectively displaying a mirrored image).
460 The default value is false.
464 \qmlproperty enumeration QtQuick2::Image::horizontalAlignment
465 \qmlproperty enumeration QtQuick2::Image::verticalAlignment
467 Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned.
469 The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
470 The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
471 and \c Image.AlignVCenter.
473 void QSGImage::updatePaintedGeometry()
477 if (d->fillMode == PreserveAspectFit) {
478 if (!d->pix.width() || !d->pix.height()) {
480 setImplicitHeight(0);
483 qreal w = widthValid() ? width() : d->pix.width();
484 qreal widthScale = w / qreal(d->pix.width());
485 qreal h = heightValid() ? height() : d->pix.height();
486 qreal heightScale = h / qreal(d->pix.height());
487 if (widthScale <= heightScale) {
489 d->paintedHeight = widthScale * qreal(d->pix.height());
490 } else if(heightScale < widthScale) {
491 d->paintedWidth = heightScale * qreal(d->pix.width());
492 d->paintedHeight = h;
494 if (widthValid() && !heightValid()) {
495 setImplicitHeight(d->paintedHeight);
497 setImplicitHeight(d->pix.height());
499 if (heightValid() && !widthValid()) {
500 setImplicitWidth(d->paintedWidth);
502 setImplicitWidth(d->pix.width());
504 } else if (d->fillMode == PreserveAspectCrop) {
505 if (!d->pix.width() || !d->pix.height())
507 qreal widthScale = width() / qreal(d->pix.width());
508 qreal heightScale = height() / qreal(d->pix.height());
509 if (widthScale < heightScale) {
510 widthScale = heightScale;
511 } else if(heightScale < widthScale) {
512 heightScale = widthScale;
515 d->paintedHeight = heightScale * qreal(d->pix.height());
516 d->paintedWidth = widthScale * qreal(d->pix.width());
517 } else if (d->fillMode == Pad) {
518 d->paintedWidth = d->pix.width();
519 d->paintedHeight = d->pix.height();
521 d->paintedWidth = width();
522 d->paintedHeight = height();
524 emit paintedGeometryChanged();
527 void QSGImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
529 QSGImageBase::geometryChanged(newGeometry, oldGeometry);
530 updatePaintedGeometry();
533 QRectF QSGImage::boundingRect() const
536 return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
539 QSGTextureProvider *QSGImage::textureProvider() const
543 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
545 && d->sceneGraphContext()
546 && QThread::currentThread() == d->sceneGraphContext()->thread(),
547 "QSGImage::textureProvider",
548 "Cannot be used outside the GUI thread");
549 const_cast<QSGImagePrivate *>(d)->provider = new QSGImageTextureProvider(this);
555 QSGNode *QSGImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
559 QSGTexture *texture = d->pix.texture(d->sceneGraphContext());
561 // Copy over the current texture state into the texture provider...
563 d->provider->m_smooth = d->smooth;
564 d->provider->m_texture = texture;
567 if (!texture || width() <= 0 || height() <= 0) {
572 QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
574 d->pixmapChanged = true;
575 node = d->sceneGraphContext()->createImageNode();
576 node->setTexture(texture);
579 if (d->pixmapChanged) {
580 // force update the texture in the node to trigger reconstruction of
581 // geometry and the likes when a atlas segment has changed.
583 node->setTexture(texture);
584 d->pixmapChanged = false;
589 QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
590 QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
592 qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width();
593 qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height();
596 if (d->hAlign == QSGImage::AlignHCenter)
597 xOffset = qCeil((width() - pixWidth) / 2.);
598 else if (d->hAlign == QSGImage::AlignRight)
599 xOffset = qCeil(width() - pixWidth);
602 if (d->vAlign == QSGImage::AlignVCenter)
603 yOffset = qCeil((height() - pixHeight) / 2.);
604 else if (d->vAlign == QSGImage::AlignBottom)
605 yOffset = qCeil(height() - pixHeight);
607 switch (d->fillMode) {
610 targetRect = QRectF(0, 0, width(), height());
611 sourceRect = d->pix.rect();
614 case PreserveAspectFit:
615 targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
616 sourceRect = d->pix.rect();
619 case PreserveAspectCrop: {
620 targetRect = QRect(0, 0, width(), height());
621 qreal wscale = width() / qreal(d->pix.width());
622 qreal hscale = height() / qreal(d->pix.height());
624 if (wscale > hscale) {
625 int src = (hscale / wscale) * qreal(d->pix.height());
627 if (d->vAlign == QSGImage::AlignVCenter)
628 y = qCeil((d->pix.height() - src) / 2.);
629 else if (d->vAlign == QSGImage::AlignBottom)
630 y = qCeil(d->pix.height() - src);
631 sourceRect = QRectF(0, y, d->pix.width(), src);
634 int src = (wscale / hscale) * qreal(d->pix.width());
636 if (d->hAlign == QSGImage::AlignHCenter)
637 x = qCeil((d->pix.width() - src) / 2.);
638 else if (d->hAlign == QSGImage::AlignRight)
639 x = qCeil(d->pix.width() - src);
640 sourceRect = QRectF(x, 0, src, d->pix.height());
646 targetRect = QRectF(0, 0, width(), height());
647 sourceRect = QRectF(-xOffset, -yOffset, width(), height());
648 hWrap = QSGTexture::Repeat;
649 vWrap = QSGTexture::Repeat;
652 case TileHorizontally:
653 targetRect = QRectF(0, 0, width(), height());
654 sourceRect = QRectF(-xOffset, 0, width(), d->pix.height());
655 hWrap = QSGTexture::Repeat;
659 targetRect = QRectF(0, 0, width(), height());
660 sourceRect = QRectF(0, -yOffset, d->pix.width(), height());
661 vWrap = QSGTexture::Repeat;
665 qreal w = qMin(qreal(d->pix.width()), width());
666 qreal h = qMin(qreal(d->pix.height()), height());
667 qreal x = (d->pix.width() > width()) ? -xOffset : 0;
668 qreal y = (d->pix.height() > height()) ? -yOffset : 0;
669 targetRect = QRectF(x + xOffset, y + yOffset, w, h);
670 sourceRect = QRectF(x, y, w, h);
674 QRectF nsrect(sourceRect.x() / d->pix.width(),
675 sourceRect.y() / d->pix.height(),
676 sourceRect.width() / d->pix.width(),
677 sourceRect.height() / d->pix.height());
680 qreal oldLeft = nsrect.left();
681 nsrect.setLeft(nsrect.right());
682 nsrect.setRight(oldLeft);
685 node->setHorizontalWrapMode(hWrap);
686 node->setVerticalWrapMode(vWrap);
687 node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
689 node->setTargetRect(targetRect);
690 node->setSourceRect(nsrect);
696 void QSGImage::pixmapChange()
699 // PreserveAspectFit calculates the implicit size differently so we
700 // don't call our superclass pixmapChange(), since that would
701 // result in the implicit size being set incorrectly, then updated
702 // in updatePaintedGeometry()
703 if (d->fillMode != PreserveAspectFit)
704 QSGImageBase::pixmapChange();
705 updatePaintedGeometry();
706 d->pixmapChanged = true;
709 QSGImage::VAlignment QSGImage::verticalAlignment() const
715 void QSGImage::setVerticalAlignment(VAlignment align)
718 if (d->vAlign == align)
723 updatePaintedGeometry();
724 emit verticalAlignmentChanged(align);
727 QSGImage::HAlignment QSGImage::horizontalAlignment() const
733 void QSGImage::setHorizontalAlignment(HAlignment align)
736 if (d->hAlign == align)
741 updatePaintedGeometry();
742 emit horizontalAlignmentChanged(align);