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