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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativeimage_p.h"
43 #include "private/qdeclarativeimage_p_p.h"
52 \qmlclass Image QDeclarativeImage
54 \ingroup qml-basic-visual-elements
55 \brief The Image element displays an image in a declarative user interface
58 The Image element is used to display images in a declarative user interface.
60 The source of the image is specified as a URL using the \l source property.
61 Images can be supplied in any of the standard image formats supported by Qt,
62 including bitmap formats such as PNG and JPEG, and vector graphics formats
63 such as SVG. If you need to display animated images, use the \l AnimatedImage
66 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
67 specified, the Image element automatically uses the size of the loaded image.
68 By default, specifying the width and height of the element causes the image
69 to be scaled to that size. This behavior can be changed by setting the
70 \l fillMode property, allowing the image to be stretched and tiled instead.
72 \section1 Example Usage
74 The following example shows the simplest usage of the Image element.
76 \snippet doc/src/snippets/declarative/image.qml document
79 \image declarative-qtlogo.png
86 By default, locally available images are loaded immediately, and the user interface
87 is blocked until loading is complete. If a large image is to be loaded, it may be
88 preferable to load the image in a low priority thread, by enabling the \l asynchronous
91 If the image is obtained from a network rather than a local resource, it is
92 automatically loaded asynchronously, and the \l progress and \l status properties
93 are updated as appropriate.
95 Images are cached and shared internally, so if several Image elements have the same \l source,
96 only one copy of the image will be loaded.
98 \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended
99 that images which do not form part of the user interface have their
100 size bounded via the \l sourceSize property. This is especially important for content
101 that is loaded from external sources or provided by the user.
103 \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider
106 QDeclarativeImage::QDeclarativeImage(QDeclarativeItem *parent)
107 : QDeclarativeImageBase(*(new QDeclarativeImagePrivate), parent)
111 QDeclarativeImage::QDeclarativeImage(QDeclarativeImagePrivate &dd, QDeclarativeItem *parent)
112 : QDeclarativeImageBase(dd, parent)
116 QDeclarativeImage::~QDeclarativeImage()
120 QPixmap QDeclarativeImage::pixmap() const
122 Q_D(const QDeclarativeImage);
123 return d->pix.pixmap();
126 void QDeclarativeImage::setPixmap(const QPixmap &pix)
128 Q_D(QDeclarativeImage);
129 if (!d->url.isEmpty())
134 void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap)
136 Q_Q(QDeclarativeImage);
137 pix.setPixmap(pixmap);
140 status = pix.isNull() ? QDeclarativeImageBase::Null : QDeclarativeImageBase::Ready;
146 \qmlproperty enumeration Image::fillMode
148 Set this property to define what happens when the source image has a different size
152 \o Image.Stretch - the image is scaled to fit
153 \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
154 \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
155 \o Image.Tile - the image is duplicated horizontally and vertically
156 \o Image.TileVertically - the image is stretched horizontally and tiled vertically
157 \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally
163 \o \image declarative-qtlogo-stretch.png
167 width: 130; height: 100
174 \o \image declarative-qtlogo-preserveaspectfit.png
178 width: 130; height: 100
179 fillMode: Image.PreserveAspectFit
186 \o \image declarative-qtlogo-preserveaspectcrop.png
187 \o PreserveAspectCrop
190 width: 130; height: 100
191 fillMode: Image.PreserveAspectCrop
199 \o \image declarative-qtlogo-tile.png
203 width: 120; height: 120
210 \o \image declarative-qtlogo-tilevertically.png
214 width: 120; height: 120
215 fillMode: Image.TileVertically
222 \o \image declarative-qtlogo-tilehorizontally.png
226 width: 120; height: 120
227 fillMode: Image.TileHorizontally
235 Note that \c clip is \c false by default which means that the element might
236 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
238 \sa {declarative/imageelements/image}{Image example}
240 QDeclarativeImage::FillMode QDeclarativeImage::fillMode() const
242 Q_D(const QDeclarativeImage);
246 void QDeclarativeImage::setFillMode(FillMode mode)
248 Q_D(QDeclarativeImage);
249 if (d->fillMode == mode)
253 updatePaintedGeometry();
254 emit fillModeChanged();
259 \qmlproperty real Image::paintedWidth
260 \qmlproperty real Image::paintedHeight
262 These properties hold the size of the image that is actually painted.
263 In most cases it is the same as \c width and \c height, but when using a
264 \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop
265 \c paintedWidth or \c paintedHeight can be smaller or larger than
266 \c width and \c height of the Image element.
268 qreal QDeclarativeImage::paintedWidth() const
270 Q_D(const QDeclarativeImage);
271 return d->paintedWidth;
274 qreal QDeclarativeImage::paintedHeight() const
276 Q_D(const QDeclarativeImage);
277 return d->paintedHeight;
281 \qmlproperty enumeration Image::status
283 This property holds the status of image loading. It can be one of:
285 \o Image.Null - no image has been set
286 \o Image.Ready - the image has been loaded
287 \o Image.Loading - the image is currently being loaded
288 \o Image.Error - an error occurred while loading the image
291 Use this status to provide an update or respond to the status change in some way.
292 For example, you could:
295 \o Trigger a state change:
297 State { name: 'loaded'; when: image.status == Image.Ready }
300 \o Implement an \c onStatusChanged signal handler:
304 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
308 \o Bind to the status value:
310 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
318 \qmlproperty real Image::progress
320 This property holds the progress of image loading, from 0.0 (nothing loaded)
327 \qmlproperty bool Image::smooth
329 Set this property if you want the image to be smoothly filtered when scaled or
330 transformed. Smooth filtering gives better visual quality, but is slower. If
331 the image is displayed at its natural size, this property has no visual or
334 \note Generally scaling artifacts are only visible if the image is stationary on
335 the screen. A common pattern when animating an image is to disable smooth
336 filtering at the beginning of the animation and reenable it at the conclusion.
340 \qmlproperty QSize Image::sourceSize
342 This property holds the actual width and height of the loaded image.
344 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
345 the painting of the image, this property sets the actual number of pixels
346 stored for the loaded image so that large images do not use more
347 memory than necessary. For example, this ensures the image in memory is no
348 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
349 \l {Item::}{height} values:
358 source: "reallyBigImage.jpg"
359 sourceSize.width: 1024
360 sourceSize.height: 1024
365 If the image's actual size is larger than the sourceSize, the image is scaled down.
366 If only one dimension of the size is set to greater than 0, the
367 other dimension is set in proportion to preserve the source image's aspect ratio.
368 (The \l fillMode is independent of this.)
370 If the source is an instrinsically scalable image (eg. SVG), this property
371 determines the size of the loaded image regardless of intrinsic size.
372 Avoid changing this property dynamically; rendering an SVG is \e slow compared
375 If the source is a non-scalable image (eg. JPEG), the loaded image will
376 be no greater than this property specifies. For some formats (currently only JPEG),
377 the whole image will never actually be loaded into memory.
379 Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image
380 by setting sourceSize to \c undefined.
382 \note \e {Changing this property dynamically causes the image source to be reloaded,
383 potentially even from the network, if it is not in the disk cache.}
386 void QDeclarativeImage::updatePaintedGeometry()
388 Q_D(QDeclarativeImage);
390 if (d->fillMode == PreserveAspectFit) {
391 if (!d->pix.width() || !d->pix.height()) {
393 setImplicitHeight(0);
396 qreal w = widthValid() ? width() : d->pix.width();
397 qreal widthScale = w / qreal(d->pix.width());
398 qreal h = heightValid() ? height() : d->pix.height();
399 qreal heightScale = h / qreal(d->pix.height());
400 if (widthScale <= heightScale) {
402 d->paintedHeight = widthScale * qreal(d->pix.height());
403 } else if(heightScale < widthScale) {
404 d->paintedWidth = heightScale * qreal(d->pix.width());
405 d->paintedHeight = h;
407 if (widthValid() && !heightValid()) {
408 setImplicitHeight(d->paintedHeight);
410 setImplicitHeight(d->pix.height());
412 if (heightValid() && !widthValid()) {
413 setImplicitWidth(d->paintedWidth);
415 setImplicitWidth(d->pix.width());
417 } else if (d->fillMode == PreserveAspectCrop) {
418 if (!d->pix.width() || !d->pix.height())
420 qreal widthScale = width() / qreal(d->pix.width());
421 qreal heightScale = height() / qreal(d->pix.height());
422 if (widthScale < heightScale) {
423 widthScale = heightScale;
424 } else if(heightScale < widthScale) {
425 heightScale = widthScale;
428 d->paintedHeight = heightScale * qreal(d->pix.height());
429 d->paintedWidth = widthScale * qreal(d->pix.width());
431 d->paintedWidth = width();
432 d->paintedHeight = height();
434 emit paintedGeometryChanged();
437 void QDeclarativeImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
439 QDeclarativeImageBase::geometryChanged(newGeometry, oldGeometry);
440 updatePaintedGeometry();
443 QRectF QDeclarativeImage::boundingRect() const
445 Q_D(const QDeclarativeImage);
446 return QRectF(0, 0, qMax(d->mWidth, d->paintedWidth), qMax(d->mHeight, d->paintedHeight));
450 \qmlproperty url Image::source
452 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
454 The URL may be absolute, or relative to the URL of the component.
456 \sa QDeclarativeImageProvider
460 \qmlproperty bool Image::asynchronous
462 Specifies that images on the local filesystem should be loaded
463 asynchronously in a separate thread. The default value is
464 false, causing the user interface thread to block while the
465 image is loaded. Setting \a asynchronous to true is useful where
466 maintaining a responsive user interface is more desirable
467 than having images immediately visible.
469 Note that this property is only valid for images read from the
470 local filesystem. Images loaded via a network resource (e.g. HTTP)
471 are always loaded asynchonously.
475 \qmlproperty bool Image::cache
478 Specifies whether the image should be cached. The default value is
479 true. Setting \a cache to false is useful when dealing with large images,
480 to make sure that they aren't cached at the expense of small 'ui element' images.
484 \qmlproperty bool Image::mirror
487 This property holds whether the image should be horizontally inverted
488 (effectively displaying a mirrored image).
490 The default value is false.
494 void QDeclarativeImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
496 Q_D(QDeclarativeImage);
497 if (d->pix.pixmap().isNull() )
500 int drawWidth = width();
501 int drawHeight = height();
503 QTransform transform;
504 qreal widthScale = width() / qreal(d->pix.width());
505 qreal heightScale = height() / qreal(d->pix.height());
507 if (width() != d->pix.width() || height() != d->pix.height()) {
508 if (d->fillMode >= Tile) {
509 if (d->fillMode == TileVertically) {
510 transform.scale(widthScale, 1.0);
511 drawWidth = d->pix.width();
512 } else if (d->fillMode == TileHorizontally) {
513 transform.scale(1.0, heightScale);
514 drawHeight = d->pix.height();
517 if (d->fillMode == PreserveAspectFit) {
518 if (widthScale <= heightScale) {
519 heightScale = widthScale;
520 transform.translate(0, (height() - heightScale * d->pix.height()) / 2);
521 } else if(heightScale < widthScale) {
522 widthScale = heightScale;
523 transform.translate((width() - widthScale * d->pix.width()) / 2, 0);
525 } else if (d->fillMode == PreserveAspectCrop) {
526 if (widthScale < heightScale) {
527 widthScale = heightScale;
528 transform.translate((width() - widthScale * d->pix.width()) / 2, 0);
529 } else if(heightScale < widthScale) {
530 heightScale = widthScale;
531 transform.translate(0, (height() - heightScale * d->pix.height()) / 2);
534 transform.scale(widthScale, heightScale);
535 drawWidth = d->pix.width();
536 drawHeight = d->pix.height();
541 QTransform oldTransform;
542 bool oldAA = p->testRenderHint(QPainter::Antialiasing);
543 bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
545 p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
548 p->setClipRect(QRectF(0, 0, d->mWidth, d->mHeight), Qt::IntersectClip);
551 transform.translate(drawWidth, 0).scale(-1.0, 1.0);
552 if (!transform.isIdentity()) {
553 oldTransform = p->transform();
554 p->setWorldTransform(transform * oldTransform);
557 if (d->fillMode >= Tile)
558 p->drawTiledPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix);
560 p->drawPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix, QRectF(0, 0, drawWidth, drawHeight));
563 p->setRenderHint(QPainter::Antialiasing, oldAA);
564 p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
568 if (!transform.isIdentity())
569 p->setWorldTransform(oldTransform);
572 void QDeclarativeImage::pixmapChange()
574 Q_D(QDeclarativeImage);
575 // PreserveAspectFit calculates the implicit size differently so we
576 // don't call our superclass pixmapChange(), since that would
577 // result in the implicit size being set incorrectly, then updated
578 // in updatePaintedGeometry()
579 if (d->fillMode != PreserveAspectFit)
580 QDeclarativeImageBase::pixmapChange();
581 updatePaintedGeometry();