Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / src / declarative / graphicsitems / qdeclarativeimage.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
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
14 ** this package.
15 **
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.
23 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativeimage_p.h"
43 #include "private/qdeclarativeimage_p_p.h"
44
45 #include <QKeyEvent>
46 #include <QPainter>
47
48 QT_BEGIN_NAMESPACE
49
50
51 /*!
52     \qmlclass Image QDeclarativeImage
53     \since 4.7
54     \ingroup qml-basic-visual-elements
55     \brief The Image element displays an image in a declarative user interface
56     \inherits Item
57
58     The Image element is used to display images in a declarative user interface.
59
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
64     element.
65
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.
71
72     \section1 Example Usage
73
74     The following example shows the simplest usage of the Image element.
75
76     \snippet doc/src/snippets/declarative/image.qml document
77
78     \beginfloatleft
79     \image declarative-qtlogo.png
80     \endfloat
81
82     \clearfloat
83
84     \section1 Performance
85
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
89     property.
90
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.
94
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.
97
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.
102
103     \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider
104 */
105
106 QDeclarativeImage::QDeclarativeImage(QDeclarativeItem *parent)
107     : QDeclarativeImageBase(*(new QDeclarativeImagePrivate), parent)
108 {
109 }
110
111 QDeclarativeImage::QDeclarativeImage(QDeclarativeImagePrivate &dd, QDeclarativeItem *parent)
112     : QDeclarativeImageBase(dd, parent)
113 {
114 }
115
116 QDeclarativeImage::~QDeclarativeImage()
117 {
118 }
119
120 QPixmap QDeclarativeImage::pixmap() const
121 {
122     Q_D(const QDeclarativeImage);
123     return d->pix.pixmap();
124 }
125
126 void QDeclarativeImage::setPixmap(const QPixmap &pix)
127 {
128     Q_D(QDeclarativeImage);
129     if (!d->url.isEmpty())
130         return;
131     d->setPixmap(pix);
132 }
133
134 void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap)
135 {
136     Q_Q(QDeclarativeImage);
137     pix.setPixmap(pixmap);
138
139     q->pixmapChange();
140     status = pix.isNull() ? QDeclarativeImageBase::Null : QDeclarativeImageBase::Ready;
141
142     q->update();
143 }
144
145 /*!
146     \qmlproperty enumeration Image::fillMode
147
148     Set this property to define what happens when the source image has a different size
149     than the item.
150
151     \list
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
158     \endlist
159
160     \table
161     
162     \row
163     \o \image declarative-qtlogo-stretch.png
164     \o Stretch (default)
165     \qml
166     Image {
167         width: 130; height: 100
168         smooth: true
169         source: "qtlogo.png"
170     }
171     \endqml
172
173     \row
174     \o \image declarative-qtlogo-preserveaspectfit.png
175     \o PreserveAspectFit
176     \qml
177     Image {
178         width: 130; height: 100
179         fillMode: Image.PreserveAspectFit
180         smooth: true
181         source: "qtlogo.png"
182     }
183     \endqml
184
185     \row
186     \o \image declarative-qtlogo-preserveaspectcrop.png
187     \o PreserveAspectCrop
188     \qml
189     Image {
190         width: 130; height: 100
191         fillMode: Image.PreserveAspectCrop
192         smooth: true
193         source: "qtlogo.png"
194         clip: true
195     }
196     \endqml
197
198     \row
199     \o \image declarative-qtlogo-tile.png
200     \o Tile
201     \qml
202     Image {
203         width: 120; height: 120
204         fillMode: Image.Tile
205         source: "qtlogo.png"
206     }
207     \endqml
208
209     \row
210     \o \image declarative-qtlogo-tilevertically.png
211     \o TileVertically
212     \qml
213     Image {
214         width: 120; height: 120
215         fillMode: Image.TileVertically
216         smooth: true
217         source: "qtlogo.png"
218     }
219     \endqml
220
221     \row
222     \o \image declarative-qtlogo-tilehorizontally.png
223     \o TileHorizontally
224     \qml
225     Image {
226         width: 120; height: 120
227         fillMode: Image.TileHorizontally
228         smooth: true
229         source: "qtlogo.png"
230     }
231     \endqml
232
233     \endtable
234
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.
237
238     \sa {declarative/imageelements/image}{Image example}
239 */
240 QDeclarativeImage::FillMode QDeclarativeImage::fillMode() const
241 {
242     Q_D(const QDeclarativeImage);
243     return d->fillMode;
244 }
245
246 void QDeclarativeImage::setFillMode(FillMode mode)
247 {
248     Q_D(QDeclarativeImage);
249     if (d->fillMode == mode)
250         return;
251     d->fillMode = mode;
252     update();
253     updatePaintedGeometry();
254     emit fillModeChanged();
255 }
256
257 /*!
258
259     \qmlproperty real Image::paintedWidth
260     \qmlproperty real Image::paintedHeight
261
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.
267 */
268 qreal QDeclarativeImage::paintedWidth() const
269 {
270     Q_D(const QDeclarativeImage);
271     return d->paintedWidth;
272 }
273
274 qreal QDeclarativeImage::paintedHeight() const
275 {
276     Q_D(const QDeclarativeImage);
277     return d->paintedHeight;
278 }
279
280 /*!
281     \qmlproperty enumeration Image::status
282
283     This property holds the status of image loading.  It can be one of:
284     \list
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
289     \endlist
290
291     Use this status to provide an update or respond to the status change in some way.
292     For example, you could:
293
294     \list
295     \o Trigger a state change:
296     \qml
297         State { name: 'loaded'; when: image.status == Image.Ready }
298     \endqml
299
300     \o Implement an \c onStatusChanged signal handler:
301     \qml
302         Image {
303             id: image
304             onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
305         }
306     \endqml
307
308     \o Bind to the status value:
309     \qml
310         Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
311     \endqml
312     \endlist
313
314     \sa progress
315 */
316
317 /*!
318     \qmlproperty real Image::progress
319
320     This property holds the progress of image loading, from 0.0 (nothing loaded)
321     to 1.0 (finished).
322
323     \sa status
324 */
325
326 /*!
327     \qmlproperty bool Image::smooth
328
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
332     performance effect.
333
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.
337 */
338
339 /*!
340     \qmlproperty QSize Image::sourceSize
341
342     This property holds the actual width and height of the loaded image.
343
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:
350
351     \code
352     Rectangle {
353         width: ...
354         height: ...
355
356         Image {
357            anchors.fill: parent
358            source: "reallyBigImage.jpg"
359            sourceSize.width: 1024
360            sourceSize.height: 1024
361         }
362     }
363     \endcode
364
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.)
369
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
373     to an image.
374
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.
378
379     Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image
380     by setting sourceSize to \c undefined.
381  
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.}
384 */
385
386 void QDeclarativeImage::updatePaintedGeometry()
387 {
388     Q_D(QDeclarativeImage);
389
390     if (d->fillMode == PreserveAspectFit) {
391         if (!d->pix.width() || !d->pix.height()) {
392             setImplicitWidth(0);
393             setImplicitHeight(0);
394             return;
395         }
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) {
401             d->paintedWidth = w;
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;
406         }
407         if (widthValid() && !heightValid()) {
408             setImplicitHeight(d->paintedHeight);
409         } else {
410             setImplicitHeight(d->pix.height());
411         }
412         if (heightValid() && !widthValid()) {
413             setImplicitWidth(d->paintedWidth);
414         } else {
415             setImplicitWidth(d->pix.width());
416         }
417     } else if (d->fillMode == PreserveAspectCrop) {
418         if (!d->pix.width() || !d->pix.height())
419             return;
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;
426         }
427
428         d->paintedHeight = heightScale * qreal(d->pix.height());
429         d->paintedWidth = widthScale * qreal(d->pix.width());
430     } else {
431         d->paintedWidth = width();
432         d->paintedHeight = height();
433     }
434     emit paintedGeometryChanged();
435 }
436
437 void QDeclarativeImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
438 {
439     QDeclarativeImageBase::geometryChanged(newGeometry, oldGeometry);
440     updatePaintedGeometry();
441 }
442
443 QRectF QDeclarativeImage::boundingRect() const
444 {
445     Q_D(const QDeclarativeImage);
446     return QRectF(0, 0, qMax(d->mWidth, d->paintedWidth), qMax(d->mHeight, d->paintedHeight));
447 }
448
449 /*!
450     \qmlproperty url Image::source
451
452     Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
453
454     The URL may be absolute, or relative to the URL of the component.
455
456     \sa QDeclarativeImageProvider
457 */
458
459 /*!
460     \qmlproperty bool Image::asynchronous
461
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.
468
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.
472 */
473
474 /*!
475     \qmlproperty bool Image::cache
476     \since Quick 1.1
477
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.
481 */
482
483 /*!
484     \qmlproperty bool Image::mirror
485     \since Quick 1.1
486
487     This property holds whether the image should be horizontally inverted
488     (effectively displaying a mirrored image).
489
490     The default value is false.
491 */
492
493
494 void QDeclarativeImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
495 {
496     Q_D(QDeclarativeImage);
497     if (d->pix.pixmap().isNull() )
498         return;
499
500     int drawWidth = width();
501     int drawHeight = height();
502     bool doClip = false;
503     QTransform transform;
504     qreal widthScale = width() / qreal(d->pix.width());
505     qreal heightScale = height() / qreal(d->pix.height());
506
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();
515             }
516         } else {
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);
524                 }
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);
532                 }
533             }
534             transform.scale(widthScale, heightScale);
535             drawWidth = d->pix.width();
536             drawHeight = d->pix.height();
537             doClip = clip();
538         }
539     }
540
541     QTransform oldTransform;
542     bool oldAA = p->testRenderHint(QPainter::Antialiasing);
543     bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
544     if (d->smooth)
545         p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
546     if (doClip) {
547         p->save();
548         p->setClipRect(QRectF(0, 0, d->mWidth, d->mHeight), Qt::IntersectClip);
549     }
550     if (d->mirror)
551         transform.translate(drawWidth, 0).scale(-1.0, 1.0);
552     if (!transform.isIdentity()) {
553         oldTransform = p->transform();
554         p->setWorldTransform(transform * oldTransform);
555     }
556
557     if (d->fillMode >= Tile)
558         p->drawTiledPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix);
559     else
560         p->drawPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix, QRectF(0, 0, drawWidth, drawHeight));
561
562     if (d->smooth) {
563         p->setRenderHint(QPainter::Antialiasing, oldAA);
564         p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
565     }
566     if (doClip)
567         p->restore();
568     if (!transform.isIdentity())
569         p->setWorldTransform(oldTransform);
570 }
571
572 void QDeclarativeImage::pixmapChange()
573 {
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();
582 }
583
584 QT_END_NAMESPACE