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 "QtQuick1/private/qdeclarativeimage_p.h"
43 #include "QtQuick1/private/qdeclarativeimage_p_p.h"
54 \qmlclass Image QDeclarative1Image
56 \ingroup qml-basic-visual-elements
57 \brief The Image element displays an image in a declarative user interface
60 The Image element is used to display images in a declarative user interface.
62 The source of the image is specified as a URL using the \l source property.
63 Images can be supplied in any of the standard image formats supported by Qt,
64 including bitmap formats such as PNG and JPEG, and vector graphics formats
65 such as SVG. If you need to display animated images, use the \l AnimatedImage
68 If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
69 specified, the Image element automatically uses the size of the loaded image.
70 By default, specifying the width and height of the element causes the image
71 to be scaled to that size. This behavior can be changed by setting the
72 \l fillMode property, allowing the image to be stretched and tiled instead.
74 \section1 Example Usage
76 The following example shows the simplest usage of the Image element.
78 \snippet doc/src/snippets/declarative/image.qml document
81 \image declarative-qtlogo.png
88 By default, locally available images are loaded immediately, and the user interface
89 is blocked until loading is complete. If a large image is to be loaded, it may be
90 preferable to load the image in a low priority thread, by enabling the \l asynchronous
93 If the image is obtained from a network rather than a local resource, it is
94 automatically loaded asynchronously, and the \l progress and \l status properties
95 are updated as appropriate.
97 Images are cached and shared internally, so if several Image elements have the same \l source,
98 only one copy of the image will be loaded.
100 \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended
101 that images which do not form part of the user interface have their
102 size bounded via the \l sourceSize property. This is especially important for content
103 that is loaded from external sources or provided by the user.
105 \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider
108 QDeclarative1Image::QDeclarative1Image(QDeclarativeItem *parent)
109 : QDeclarative1ImageBase(*(new QDeclarative1ImagePrivate), parent)
113 QDeclarative1Image::QDeclarative1Image(QDeclarative1ImagePrivate &dd, QDeclarativeItem *parent)
114 : QDeclarative1ImageBase(dd, parent)
118 QDeclarative1Image::~QDeclarative1Image()
122 QPixmap QDeclarative1Image::pixmap() const
124 Q_D(const QDeclarative1Image);
125 return d->pix.pixmap();
128 void QDeclarative1Image::setPixmap(const QPixmap &pix)
130 Q_D(QDeclarative1Image);
131 if (!d->url.isEmpty())
136 void QDeclarative1ImagePrivate::setPixmap(const QPixmap &pixmap)
138 Q_Q(QDeclarative1Image);
139 pix.setPixmap(pixmap);
142 status = pix.isNull() ? QDeclarative1ImageBase::Null : QDeclarative1ImageBase::Ready;
148 \qmlproperty enumeration Image::fillMode
150 Set this property to define what happens when the source image has a different size
154 \o Image.Stretch - the image is scaled to fit
155 \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
156 \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
157 \o Image.Tile - the image is duplicated horizontally and vertically
158 \o Image.TileVertically - the image is stretched horizontally and tiled vertically
159 \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally
165 \o \image declarative-qtlogo-stretch.png
169 width: 130; height: 100
176 \o \image declarative-qtlogo-preserveaspectfit.png
180 width: 130; height: 100
181 fillMode: Image.PreserveAspectFit
188 \o \image declarative-qtlogo-preserveaspectcrop.png
189 \o PreserveAspectCrop
192 width: 130; height: 100
193 fillMode: Image.PreserveAspectCrop
201 \o \image declarative-qtlogo-tile.png
205 width: 120; height: 120
212 \o \image declarative-qtlogo-tilevertically.png
216 width: 120; height: 120
217 fillMode: Image.TileVertically
224 \o \image declarative-qtlogo-tilehorizontally.png
228 width: 120; height: 120
229 fillMode: Image.TileHorizontally
237 Note that \c clip is \c false by default which means that the element might
238 paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
240 \sa {declarative/imageelements/image}{Image example}
242 QDeclarative1Image::FillMode QDeclarative1Image::fillMode() const
244 Q_D(const QDeclarative1Image);
248 void QDeclarative1Image::setFillMode(FillMode mode)
250 Q_D(QDeclarative1Image);
251 if (d->fillMode == mode)
255 updatePaintedGeometry();
256 emit fillModeChanged();
261 \qmlproperty real Image::paintedWidth
262 \qmlproperty real Image::paintedHeight
264 These properties hold the size of the image that is actually painted.
265 In most cases it is the same as \c width and \c height, but when using a
266 \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop
267 \c paintedWidth or \c paintedHeight can be smaller or larger than
268 \c width and \c height of the Image element.
270 qreal QDeclarative1Image::paintedWidth() const
272 Q_D(const QDeclarative1Image);
273 return d->paintedWidth;
276 qreal QDeclarative1Image::paintedHeight() const
278 Q_D(const QDeclarative1Image);
279 return d->paintedHeight;
283 \qmlproperty enumeration Image::status
285 This property holds the status of image loading. It can be one of:
287 \o Image.Null - no image has been set
288 \o Image.Ready - the image has been loaded
289 \o Image.Loading - the image is currently being loaded
290 \o Image.Error - an error occurred while loading the image
293 Use this status to provide an update or respond to the status change in some way.
294 For example, you could:
297 \o Trigger a state change:
299 State { name: 'loaded'; when: image.status == Image.Ready }
302 \o Implement an \c onStatusChanged signal handler:
306 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
310 \o Bind to the status value:
312 Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
320 \qmlproperty real Image::progress
322 This property holds the progress of image loading, from 0.0 (nothing loaded)
329 \qmlproperty bool Image::smooth
331 Set this property if you want the image to be smoothly filtered when scaled or
332 transformed. Smooth filtering gives better visual quality, but is slower. If
333 the image is displayed at its natural size, this property has no visual or
336 \note Generally scaling artifacts are only visible if the image is stationary on
337 the screen. A common pattern when animating an image is to disable smooth
338 filtering at the beginning of the animation and reenable it at the conclusion.
342 \qmlproperty QSize Image::sourceSize
344 This property holds the actual width and height of the loaded image.
346 Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
347 the painting of the image, this property sets the actual number of pixels
348 stored for the loaded image so that large images do not use more
349 memory than necessary. For example, this ensures the image in memory is no
350 larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
351 \l {Item::}{height} values:
360 source: "reallyBigImage.jpg"
361 sourceSize.width: 1024
362 sourceSize.height: 1024
367 If the image's actual size is larger than the sourceSize, the image is scaled down.
368 If only one dimension of the size is set to greater than 0, the
369 other dimension is set in proportion to preserve the source image's aspect ratio.
370 (The \l fillMode is independent of this.)
372 If the source is an instrinsically scalable image (eg. SVG), this property
373 determines the size of the loaded image regardless of intrinsic size.
374 Avoid changing this property dynamically; rendering an SVG is \e slow compared
377 If the source is a non-scalable image (eg. JPEG), the loaded image will
378 be no greater than this property specifies. For some formats (currently only JPEG),
379 the whole image will never actually be loaded into memory.
381 Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image
382 by setting sourceSize to \c undefined.
384 \note \e {Changing this property dynamically causes the image source to be reloaded,
385 potentially even from the network, if it is not in the disk cache.}
388 void QDeclarative1Image::updatePaintedGeometry()
390 Q_D(QDeclarative1Image);
392 if (d->fillMode == PreserveAspectFit) {
393 if (!d->pix.width() || !d->pix.height()) {
395 setImplicitHeight(0);
398 qreal w = widthValid() ? width() : d->pix.width();
399 qreal widthScale = w / qreal(d->pix.width());
400 qreal h = heightValid() ? height() : d->pix.height();
401 qreal heightScale = h / qreal(d->pix.height());
402 if (widthScale <= heightScale) {
404 d->paintedHeight = widthScale * qreal(d->pix.height());
405 } else if(heightScale < widthScale) {
406 d->paintedWidth = heightScale * qreal(d->pix.width());
407 d->paintedHeight = h;
409 if (widthValid() && !heightValid()) {
410 setImplicitHeight(d->paintedHeight);
412 setImplicitHeight(d->pix.height());
414 if (heightValid() && !widthValid()) {
415 setImplicitWidth(d->paintedWidth);
417 setImplicitWidth(d->pix.width());
419 } else if (d->fillMode == PreserveAspectCrop) {
420 if (!d->pix.width() || !d->pix.height())
422 qreal widthScale = width() / qreal(d->pix.width());
423 qreal heightScale = height() / qreal(d->pix.height());
424 if (widthScale < heightScale) {
425 widthScale = heightScale;
426 } else if(heightScale < widthScale) {
427 heightScale = widthScale;
430 d->paintedHeight = heightScale * qreal(d->pix.height());
431 d->paintedWidth = widthScale * qreal(d->pix.width());
433 d->paintedWidth = width();
434 d->paintedHeight = height();
436 emit paintedGeometryChanged();
439 void QDeclarative1Image::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
441 QDeclarative1ImageBase::geometryChanged(newGeometry, oldGeometry);
442 updatePaintedGeometry();
445 QRectF QDeclarative1Image::boundingRect() const
447 Q_D(const QDeclarative1Image);
448 return QRectF(0, 0, qMax(d->mWidth, d->paintedWidth), qMax(d->mHeight, d->paintedHeight));
452 \qmlproperty url Image::source
454 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
456 The URL may be absolute, or relative to the URL of the component.
458 \sa QDeclarativeImageProvider
462 \qmlproperty bool Image::asynchronous
464 Specifies that images on the local filesystem should be loaded
465 asynchronously in a separate thread. The default value is
466 false, causing the user interface thread to block while the
467 image is loaded. Setting \a asynchronous to true is useful where
468 maintaining a responsive user interface is more desirable
469 than having images immediately visible.
471 Note that this property is only valid for images read from the
472 local filesystem. Images loaded via a network resource (e.g. HTTP)
473 are always loaded asynchonously.
477 \qmlproperty bool Image::cache
480 Specifies whether the image should be cached. The default value is
481 true. Setting \a cache to false is useful when dealing with large images,
482 to make sure that they aren't cached at the expense of small 'ui element' images.
486 \qmlproperty bool Image::mirror
489 This property holds whether the image should be horizontally inverted
490 (effectively displaying a mirrored image).
492 The default value is false.
496 void QDeclarative1Image::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
498 Q_D(QDeclarative1Image);
499 if (d->pix.pixmap().isNull() )
502 int drawWidth = width();
503 int drawHeight = height();
505 QTransform transform;
506 qreal widthScale = width() / qreal(d->pix.width());
507 qreal heightScale = height() / qreal(d->pix.height());
509 if (width() != d->pix.width() || height() != d->pix.height()) {
510 if (d->fillMode >= Tile) {
511 if (d->fillMode == TileVertically) {
512 transform.scale(widthScale, 1.0);
513 drawWidth = d->pix.width();
514 } else if (d->fillMode == TileHorizontally) {
515 transform.scale(1.0, heightScale);
516 drawHeight = d->pix.height();
519 if (d->fillMode == PreserveAspectFit) {
520 if (widthScale <= heightScale) {
521 heightScale = widthScale;
522 transform.translate(0, (height() - heightScale * d->pix.height()) / 2);
523 } else if(heightScale < widthScale) {
524 widthScale = heightScale;
525 transform.translate((width() - widthScale * d->pix.width()) / 2, 0);
527 } else if (d->fillMode == PreserveAspectCrop) {
528 if (widthScale < heightScale) {
529 widthScale = heightScale;
530 transform.translate((width() - widthScale * d->pix.width()) / 2, 0);
531 } else if(heightScale < widthScale) {
532 heightScale = widthScale;
533 transform.translate(0, (height() - heightScale * d->pix.height()) / 2);
536 transform.scale(widthScale, heightScale);
537 drawWidth = d->pix.width();
538 drawHeight = d->pix.height();
543 QTransform oldTransform;
544 bool oldAA = p->testRenderHint(QPainter::Antialiasing);
545 bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
547 p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
550 p->setClipRect(QRectF(0, 0, d->mWidth, d->mHeight), Qt::IntersectClip);
553 transform.translate(drawWidth, 0).scale(-1.0, 1.0);
554 if (!transform.isIdentity()) {
555 oldTransform = p->transform();
556 p->setWorldTransform(transform * oldTransform);
559 if (d->fillMode >= Tile)
560 p->drawTiledPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix);
562 p->drawPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix, QRectF(0, 0, drawWidth, drawHeight));
565 p->setRenderHint(QPainter::Antialiasing, oldAA);
566 p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
570 if (!transform.isIdentity())
571 p->setWorldTransform(oldTransform);
574 void QDeclarative1Image::pixmapChange()
576 Q_D(QDeclarative1Image);
577 // PreserveAspectFit calculates the implicit size differently so we
578 // don't call our superclass pixmapChange(), since that would
579 // result in the implicit size being set incorrectly, then updated
580 // in updatePaintedGeometry()
581 if (d->fillMode != PreserveAspectFit)
582 QDeclarative1ImageBase::pixmapChange();
583 updatePaintedGeometry();