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()
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 QSGImage;
77 QSGTexture *m_texture;
81 #include "qsgimage.moc"
83 QSGImagePrivate::QSGImagePrivate()
84 : fillMode(QSGImage::Stretch)
87 , pixmapChanged(false)
88 , hAlign(QSGImage::AlignHCenter)
89 , vAlign(QSGImage::AlignVCenter)
95 \qmlclass Image QSGImage
96 \inqmlmodule QtQuick 2
97 \ingroup qml-basic-visual-elements
98 \brief The Image element displays an image in a declarative user interface
101 The Image element is used to display images in a declarative user interface.
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 the \l AnimatedImage
109 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
110 specified, the Image element automatically uses the size of the loaded image.
111 By default, specifying the width and height of the element 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 element.
119 \snippet doc/src/snippets/declarative/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 elements have the same \l source,
139 only one copy of the image will be loaded.
141 \bold 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}, QDeclarativeImageProvider
149 QSGImage::QSGImage(QSGItem *parent)
150 : QSGImageBase(*(new QSGImagePrivate), parent)
154 QSGImage::QSGImage(QSGImagePrivate &dd, QSGItem *parent)
155 : QSGImageBase(dd, parent)
159 QSGImage::~QSGImage()
163 d->provider->deleteLater();
166 void QSGImagePrivate::setPixmap(const QPixmap &pixmap)
169 pix.setPixmap(pixmap);
172 status = pix.isNull() ? QSGImageBase::Null : QSGImageBase::Ready;
178 \qmlproperty enumeration QtQuick2::Image::fillMode
180 Set this property to define what happens when the source image has a different size
184 \o Image.Stretch - the image is scaled to fit
185 \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
186 \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
187 \o Image.Tile - the image is duplicated horizontally and vertically
188 \o Image.TileVertically - the image is stretched horizontally and tiled vertically
189 \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally
190 \o Image.Pad - the image is not transformed
196 \o \image declarative-qtlogo-stretch.png
200 width: 130; height: 100
207 \o \image declarative-qtlogo-preserveaspectfit.png
211 width: 130; height: 100
212 fillMode: Image.PreserveAspectFit
219 \o \image declarative-qtlogo-preserveaspectcrop.png
220 \o PreserveAspectCrop
223 width: 130; height: 100
224 fillMode: Image.PreserveAspectCrop
232 \o \image declarative-qtlogo-tile.png
236 width: 120; height: 120
243 \o \image declarative-qtlogo-tilevertically.png
247 width: 120; height: 120
248 fillMode: Image.TileVertically
255 \o \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 element might
269 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
271 \sa {declarative/imageelements/image}{Image example}
273 QSGImage::FillMode QSGImage::fillMode() const
279 void QSGImage::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 element.
301 qreal QSGImage::paintedWidth() const
304 return d->paintedWidth;
307 qreal QSGImage::paintedHeight() const
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 \o Image.Null - no image has been set
319 \o Image.Ready - the image has been loaded
320 \o Image.Loading - the image is currently being loaded
321 \o 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 \o Trigger a state change:
330 State { name: 'loaded'; when: image.status == Image.Ready }
333 \o Implement an \c onStatusChanged signal handler:
337 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
341 \o 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 the source is an instrinsically scalable image (eg. SVG), this property
404 determines the size of the loaded image regardless of intrinsic size.
405 Avoid changing this property dynamically; rendering an SVG is \e slow compared
408 If the source is a non-scalable image (eg. JPEG), the loaded image will
409 be no greater than this property specifies. For some formats (currently only JPEG),
410 the whole image will never actually be loaded into memory.
412 Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image
413 by setting sourceSize to \c undefined.
415 \note \e {Changing this property dynamically causes the image source to be reloaded,
416 potentially even from the network, if it is not in the disk cache.}
420 \qmlproperty url QtQuick2::Image::source
422 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
424 The URL may be absolute, or relative to the URL of the component.
426 \sa QDeclarativeImageProvider
430 \qmlproperty bool QtQuick2::Image::asynchronous
432 Specifies that images on the local filesystem should be loaded
433 asynchronously in a separate thread. The default value is
434 false, causing the user interface thread to block while the
435 image is loaded. Setting \a asynchronous to true is useful where
436 maintaining a responsive user interface is more desirable
437 than having images immediately visible.
439 Note that this property is only valid for images read from the
440 local filesystem. Images loaded via a network resource (e.g. HTTP)
441 are always loaded asynchonously.
445 \qmlproperty bool QtQuick2::Image::cache
447 Specifies whether the image should be cached. The default value is
448 true. Setting \a cache to false is useful when dealing with large images,
449 to make sure that they aren't cached at the expense of small 'ui element' images.
453 \qmlproperty bool QtQuick2::Image::mirror
455 This property holds whether the image should be horizontally inverted
456 (effectively displaying a mirrored image).
458 The default value is false.
462 \qmlproperty enumeration QtQuick2::Image::horizontalAlignment
463 \qmlproperty enumeration QtQuick2::Image::verticalAlignment
465 Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned.
467 The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
468 The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
469 and \c Image.AlignVCenter.
471 void QSGImage::updatePaintedGeometry()
475 if (d->fillMode == PreserveAspectFit) {
476 if (!d->pix.width() || !d->pix.height()) {
478 setImplicitHeight(0);
481 qreal w = widthValid() ? width() : d->pix.width();
482 qreal widthScale = w / qreal(d->pix.width());
483 qreal h = heightValid() ? height() : d->pix.height();
484 qreal heightScale = h / qreal(d->pix.height());
485 if (widthScale <= heightScale) {
487 d->paintedHeight = widthScale * qreal(d->pix.height());
488 } else if(heightScale < widthScale) {
489 d->paintedWidth = heightScale * qreal(d->pix.width());
490 d->paintedHeight = h;
492 if (widthValid() && !heightValid()) {
493 setImplicitHeight(d->paintedHeight);
495 setImplicitHeight(d->pix.height());
497 if (heightValid() && !widthValid()) {
498 setImplicitWidth(d->paintedWidth);
500 setImplicitWidth(d->pix.width());
502 } else if (d->fillMode == PreserveAspectCrop) {
503 if (!d->pix.width() || !d->pix.height())
505 qreal widthScale = width() / qreal(d->pix.width());
506 qreal heightScale = height() / qreal(d->pix.height());
507 if (widthScale < heightScale) {
508 widthScale = heightScale;
509 } else if(heightScale < widthScale) {
510 heightScale = widthScale;
513 d->paintedHeight = heightScale * qreal(d->pix.height());
514 d->paintedWidth = widthScale * qreal(d->pix.width());
515 } else if (d->fillMode == Pad) {
516 d->paintedWidth = d->pix.width();
517 d->paintedHeight = d->pix.height();
519 d->paintedWidth = width();
520 d->paintedHeight = height();
522 emit paintedGeometryChanged();
525 void QSGImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
527 QSGImageBase::geometryChanged(newGeometry, oldGeometry);
528 updatePaintedGeometry();
531 QRectF QSGImage::boundingRect() const
534 return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
537 QSGTextureProvider *QSGImage::textureProvider() const
541 // Make sure it gets thread affinity on the rendering thread so deletion works properly..
543 && d->sceneGraphContext()
544 && QThread::currentThread() == d->sceneGraphContext()->thread(),
545 "QSGImage::textureProvider",
546 "Cannot be used outside the GUI thread");
547 QSGImagePrivate *dd = const_cast<QSGImagePrivate *>(d);
548 dd->provider = new QSGImageTextureProvider;
549 dd->provider->m_texture = d->pix.texture(d->sceneGraphContext());
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);