809d373f568e54e9d8ee5727ad167f7f7910f325
[profile/ivi/qtdeclarative.git] / src / qtquick1 / graphicsitems / qdeclarativeimage.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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     \inqmlmodule QtQuick 1
56     \since QtQuick 1.0
57     \ingroup qml-basic-visual-elements
58     \brief The Image element displays an image in a declarative user interface
59     \inherits Item
60
61     The Image element is used to display images in a declarative user interface.
62
63     The source of the image is specified as a URL using the \l source property.
64     Images can be supplied in any of the standard image formats supported by Qt,
65     including bitmap formats such as PNG and JPEG, and vector graphics formats
66     such as SVG. If you need to display animated images, use the \l AnimatedImage
67     element.
68
69     If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
70     specified, the Image element automatically uses the size of the loaded image.
71     By default, specifying the width and height of the element causes the image
72     to be scaled to that size. This behavior can be changed by setting the
73     \l fillMode property, allowing the image to be stretched and tiled instead.
74
75     \section1 Example Usage
76
77     The following example shows the simplest usage of the Image element.
78
79     \snippet doc/src/snippets/qtquick1/image.qml document
80
81     \beginfloatleft
82     \image declarative-qtlogo.png
83     \endfloat
84
85     \clearfloat
86
87     \section1 Performance
88
89     By default, locally available images are loaded immediately, and the user interface
90     is blocked until loading is complete. If a large image is to be loaded, it may be
91     preferable to load the image in a low priority thread, by enabling the \l asynchronous
92     property.
93
94     If the image is obtained from a network rather than a local resource, it is
95     automatically loaded asynchronously, and the \l progress and \l status properties
96     are updated as appropriate.
97
98     Images are cached and shared internally, so if several Image elements have the same \l source,
99     only one copy of the image will be loaded.
100
101     \bold Note: Images are often the greatest user of memory in QML user interfaces.  It is recommended
102     that images which do not form part of the user interface have their
103     size bounded via the \l sourceSize property. This is especially important for content
104     that is loaded from external sources or provided by the user.
105
106     \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider
107 */
108
109 QDeclarative1Image::QDeclarative1Image(QDeclarativeItem *parent)
110     : QDeclarative1ImageBase(*(new QDeclarative1ImagePrivate), parent)
111 {
112 }
113
114 QDeclarative1Image::QDeclarative1Image(QDeclarative1ImagePrivate &dd, QDeclarativeItem *parent)
115     : QDeclarative1ImageBase(dd, parent)
116 {
117 }
118
119 QDeclarative1Image::~QDeclarative1Image()
120 {
121 }
122
123 QPixmap QDeclarative1Image::pixmap() const
124 {
125     Q_D(const QDeclarative1Image);
126     return d->pix.pixmap();
127 }
128
129 void QDeclarative1Image::setPixmap(const QPixmap &pix)
130 {
131     Q_D(QDeclarative1Image);
132     if (!d->url.isEmpty())
133         return;
134     d->setPixmap(pix);
135 }
136
137 void QDeclarative1ImagePrivate::setPixmap(const QPixmap &pixmap)
138 {
139     Q_Q(QDeclarative1Image);
140     pix.setPixmap(pixmap);
141
142     q->pixmapChange();
143     status = pix.isNull() ? QDeclarative1ImageBase::Null : QDeclarative1ImageBase::Ready;
144
145     q->update();
146 }
147
148 /*!
149     \qmlproperty enumeration QtQuick1::Image::fillMode
150
151     Set this property to define what happens when the source image has a different size
152     than the item.
153
154     \list
155     \o Image.Stretch - the image is scaled to fit
156     \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
157     \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
158     \o Image.Tile - the image is duplicated horizontally and vertically
159     \o Image.TileVertically - the image is stretched horizontally and tiled vertically
160     \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally
161     \endlist
162
163     \table
164     
165     \row
166     \o \image declarative-qtlogo-stretch.png
167     \o Stretch (default)
168     \qml
169     Image {
170         width: 130; height: 100
171         smooth: true
172         source: "qtlogo.png"
173     }
174     \endqml
175
176     \row
177     \o \image declarative-qtlogo-preserveaspectfit.png
178     \o PreserveAspectFit
179     \qml
180     Image {
181         width: 130; height: 100
182         fillMode: Image.PreserveAspectFit
183         smooth: true
184         source: "qtlogo.png"
185     }
186     \endqml
187
188     \row
189     \o \image declarative-qtlogo-preserveaspectcrop.png
190     \o PreserveAspectCrop
191     \qml
192     Image {
193         width: 130; height: 100
194         fillMode: Image.PreserveAspectCrop
195         smooth: true
196         source: "qtlogo.png"
197         clip: true
198     }
199     \endqml
200
201     \row
202     \o \image declarative-qtlogo-tile.png
203     \o Tile
204     \qml
205     Image {
206         width: 120; height: 120
207         fillMode: Image.Tile
208         source: "qtlogo.png"
209     }
210     \endqml
211
212     \row
213     \o \image declarative-qtlogo-tilevertically.png
214     \o TileVertically
215     \qml
216     Image {
217         width: 120; height: 120
218         fillMode: Image.TileVertically
219         smooth: true
220         source: "qtlogo.png"
221     }
222     \endqml
223
224     \row
225     \o \image declarative-qtlogo-tilehorizontally.png
226     \o TileHorizontally
227     \qml
228     Image {
229         width: 120; height: 120
230         fillMode: Image.TileHorizontally
231         smooth: true
232         source: "qtlogo.png"
233     }
234     \endqml
235
236     \endtable
237
238     Note that \c clip is \c false by default which means that the element might
239     paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
240
241     \sa {declarative/imageelements/image}{Image example}
242 */
243 QDeclarative1Image::FillMode QDeclarative1Image::fillMode() const
244 {
245     Q_D(const QDeclarative1Image);
246     return d->fillMode;
247 }
248
249 void QDeclarative1Image::setFillMode(FillMode mode)
250 {
251     Q_D(QDeclarative1Image);
252     if (d->fillMode == mode)
253         return;
254     d->fillMode = mode;
255     update();
256     updatePaintedGeometry();
257     emit fillModeChanged();
258 }
259
260 /*!
261
262     \qmlproperty real QtQuick1::Image::paintedWidth
263     \qmlproperty real QtQuick1::Image::paintedHeight
264
265     These properties hold the size of the image that is actually painted.
266     In most cases it is the same as \c width and \c height, but when using a 
267     \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop 
268     \c paintedWidth or \c paintedHeight can be smaller or larger than 
269     \c width and \c height of the Image element.
270 */
271 qreal QDeclarative1Image::paintedWidth() const
272 {
273     Q_D(const QDeclarative1Image);
274     return d->paintedWidth;
275 }
276
277 qreal QDeclarative1Image::paintedHeight() const
278 {
279     Q_D(const QDeclarative1Image);
280     return d->paintedHeight;
281 }
282
283 /*!
284     \qmlproperty enumeration QtQuick1::Image::status
285
286     This property holds the status of image loading.  It can be one of:
287     \list
288     \o Image.Null - no image has been set
289     \o Image.Ready - the image has been loaded
290     \o Image.Loading - the image is currently being loaded
291     \o Image.Error - an error occurred while loading the image
292     \endlist
293
294     Use this status to provide an update or respond to the status change in some way.
295     For example, you could:
296
297     \list
298     \o Trigger a state change:
299     \qml
300         State { name: 'loaded'; when: image.status == Image.Ready }
301     \endqml
302
303     \o Implement an \c onStatusChanged signal handler:
304     \qml
305         Image {
306             id: image
307             onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
308         }
309     \endqml
310
311     \o Bind to the status value:
312     \qml
313         Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
314     \endqml
315     \endlist
316
317     \sa progress
318 */
319
320 /*!
321     \qmlproperty real QtQuick1::Image::progress
322
323     This property holds the progress of image loading, from 0.0 (nothing loaded)
324     to 1.0 (finished).
325
326     \sa status
327 */
328
329 /*!
330     \qmlproperty bool QtQuick1::Image::smooth
331
332     Set this property if you want the image to be smoothly filtered when scaled or
333     transformed.  Smooth filtering gives better visual quality, but is slower.  If
334     the image is displayed at its natural size, this property has no visual or
335     performance effect.
336
337     \note Generally scaling artifacts are only visible if the image is stationary on
338     the screen.  A common pattern when animating an image is to disable smooth
339     filtering at the beginning of the animation and reenable it at the conclusion.
340 */
341
342 /*!
343     \qmlproperty QSize QtQuick1::Image::sourceSize
344
345     This property holds the actual width and height of the loaded image.
346
347     Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
348     the painting of the image, this property sets the actual number of pixels
349     stored for the loaded image so that large images do not use more
350     memory than necessary. For example, this ensures the image in memory is no
351     larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and 
352     \l {Item::}{height} values:
353
354     \code
355     Rectangle {
356         width: ...
357         height: ...
358
359         Image {
360            anchors.fill: parent
361            source: "reallyBigImage.jpg"
362            sourceSize.width: 1024
363            sourceSize.height: 1024
364         }
365     }
366     \endcode
367
368     If the image's actual size is larger than the sourceSize, the image is scaled down.
369     If only one dimension of the size is set to greater than 0, the
370     other dimension is set in proportion to preserve the source image's aspect ratio.
371     (The \l fillMode is independent of this.)
372
373     If the source is an instrinsically scalable image (eg. SVG), this property
374     determines the size of the loaded image regardless of intrinsic size.
375     Avoid changing this property dynamically; rendering an SVG is \e slow compared
376     to an image.
377
378     If the source is a non-scalable image (eg. JPEG), the loaded image will
379     be no greater than this property specifies. For some formats (currently only JPEG),
380     the whole image will never actually be loaded into memory.
381
382     Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image
383     by setting sourceSize to \c undefined.
384  
385     \note \e {Changing this property dynamically causes the image source to be reloaded,
386     potentially even from the network, if it is not in the disk cache.}
387 */
388
389 void QDeclarative1Image::updatePaintedGeometry()
390 {
391     Q_D(QDeclarative1Image);
392
393     if (d->fillMode == PreserveAspectFit) {
394         if (!d->pix.width() || !d->pix.height()) {
395             setImplicitWidth(0);
396             setImplicitHeight(0);
397             return;
398         }
399         qreal w = widthValid() ? width() : d->pix.width();
400         qreal widthScale = w / qreal(d->pix.width());
401         qreal h = heightValid() ? height() : d->pix.height();
402         qreal heightScale = h / qreal(d->pix.height());
403         if (widthScale <= heightScale) {
404             d->paintedWidth = w;
405             d->paintedHeight = widthScale * qreal(d->pix.height());
406         } else if(heightScale < widthScale) {
407             d->paintedWidth = heightScale * qreal(d->pix.width());
408             d->paintedHeight = h;
409         }
410         if (widthValid() && !heightValid()) {
411             setImplicitHeight(d->paintedHeight);
412         } else {
413             setImplicitHeight(d->pix.height());
414         }
415         if (heightValid() && !widthValid()) {
416             setImplicitWidth(d->paintedWidth);
417         } else {
418             setImplicitWidth(d->pix.width());
419         }
420     } else if (d->fillMode == PreserveAspectCrop) {
421         if (!d->pix.width() || !d->pix.height())
422             return;
423         qreal widthScale = width() / qreal(d->pix.width());
424         qreal heightScale = height() / qreal(d->pix.height());
425         if (widthScale < heightScale) {
426             widthScale = heightScale;
427         } else if(heightScale < widthScale) {
428             heightScale = widthScale;
429         }
430
431         d->paintedHeight = heightScale * qreal(d->pix.height());
432         d->paintedWidth = widthScale * qreal(d->pix.width());
433     } else {
434         d->paintedWidth = width();
435         d->paintedHeight = height();
436     }
437     emit paintedGeometryChanged();
438 }
439
440 void QDeclarative1Image::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
441 {
442     QDeclarative1ImageBase::geometryChanged(newGeometry, oldGeometry);
443     updatePaintedGeometry();
444 }
445
446 QRectF QDeclarative1Image::boundingRect() const
447 {
448     Q_D(const QDeclarative1Image);
449     QRectF boundingRect(0, 0, qMax(d->mWidth, d->paintedWidth), qMax(d->mHeight, d->paintedHeight));
450
451     if (d->fillMode == PreserveAspectCrop) {
452         if (!d->pix.width() || !d->pix.height())
453             return boundingRect;
454         qreal widthScale = width() / qreal(d->pix.width());
455         qreal heightScale = height() / qreal(d->pix.height());
456         if (widthScale < heightScale) {
457             widthScale = heightScale;
458             boundingRect.moveTo((width() - widthScale * d->pix.width()) / 2, 0);
459         } else if (heightScale < widthScale) {
460             heightScale = widthScale;
461             boundingRect.moveTo(0, (height() - heightScale * d->pix.height()) / 2);
462         }
463     }
464     return boundingRect;
465 }
466
467 /*!
468     \qmlproperty url QtQuick1::Image::source
469
470     Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
471
472     The URL may be absolute, or relative to the URL of the component.
473
474     \sa QDeclarativeImageProvider
475 */
476
477 /*!
478     \qmlproperty bool QtQuick1::Image::asynchronous
479
480     Specifies that images on the local filesystem should be loaded
481     asynchronously in a separate thread.  The default value is
482     false, causing the user interface thread to block while the
483     image is loaded.  Setting \a asynchronous to true is useful where
484     maintaining a responsive user interface is more desirable
485     than having images immediately visible.
486
487     Note that this property is only valid for images read from the
488     local filesystem.  Images loaded via a network resource (e.g. HTTP)
489     are always loaded asynchonously.
490 */
491
492 /*!
493     \qmlproperty bool QtQuick1::Image::cache
494     \since Quick 1.1
495
496     Specifies whether the image should be cached. The default value is
497     true. Setting \a cache to false is useful when dealing with large images,
498     to make sure that they aren't cached at the expense of small 'ui element' images.
499 */
500
501 /*!
502     \qmlproperty bool QtQuick1::Image::mirror
503     \since Quick 1.1
504
505     This property holds whether the image should be horizontally inverted
506     (effectively displaying a mirrored image).
507
508     The default value is false.
509 */
510
511
512 void QDeclarative1Image::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
513 {
514     Q_D(QDeclarative1Image);
515     if (d->pix.pixmap().isNull() )
516         return;
517
518     int drawWidth = width();
519     int drawHeight = height();
520     bool doClip = false;
521     QTransform transform;
522     qreal widthScale = width() / qreal(d->pix.width());
523     qreal heightScale = height() / qreal(d->pix.height());
524
525     if (width() != d->pix.width() || height() != d->pix.height()) {
526         if (d->fillMode >= Tile) {
527             if (d->fillMode == TileVertically) {
528                 transform.scale(widthScale, 1.0);
529                 drawWidth = d->pix.width();
530             } else if (d->fillMode == TileHorizontally) {
531                 transform.scale(1.0, heightScale);
532                 drawHeight = d->pix.height();
533             }
534         } else {
535             if (d->fillMode == PreserveAspectFit) {
536                 if (widthScale <= heightScale) {
537                     heightScale = widthScale;
538                     transform.translate(0, (height() - heightScale * d->pix.height()) / 2);
539                 } else if(heightScale < widthScale) {
540                     widthScale = heightScale;
541                     transform.translate((width() - widthScale * d->pix.width()) / 2, 0);
542                 }
543             } else if (d->fillMode == PreserveAspectCrop) {
544                 if (widthScale < heightScale) {
545                     widthScale = heightScale;
546                     transform.translate((width() - widthScale * d->pix.width()) / 2, 0);
547                 } else if(heightScale < widthScale) {
548                     heightScale = widthScale;
549                     transform.translate(0, (height() - heightScale * d->pix.height()) / 2);
550                 }
551             }
552             transform.scale(widthScale, heightScale);
553             drawWidth = d->pix.width();
554             drawHeight = d->pix.height();
555             doClip = clip();
556         }
557     }
558
559     QTransform oldTransform;
560     bool oldAA = p->testRenderHint(QPainter::Antialiasing);
561     bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
562     if (d->smooth)
563         p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
564     if (doClip) {
565         p->save();
566         p->setClipRect(QRectF(0, 0, d->mWidth, d->mHeight), Qt::IntersectClip);
567     }
568     if (d->mirror)
569         transform.translate(drawWidth, 0).scale(-1.0, 1.0);
570     if (!transform.isIdentity()) {
571         oldTransform = p->transform();
572         p->setWorldTransform(transform * oldTransform);
573     }
574
575     if (d->fillMode >= Tile)
576         p->drawTiledPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix);
577     else
578         p->drawPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix, QRectF(0, 0, drawWidth, drawHeight));
579
580     if (d->smooth) {
581         p->setRenderHint(QPainter::Antialiasing, oldAA);
582         p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
583     }
584     if (doClip)
585         p->restore();
586     if (!transform.isIdentity())
587         p->setWorldTransform(oldTransform);
588 }
589
590 void QDeclarative1Image::pixmapChange()
591 {
592     Q_D(QDeclarative1Image);
593     // PreserveAspectFit calculates the implicit size differently so we
594     // don't call our superclass pixmapChange(), since that would
595     // result in the implicit size being set incorrectly, then updated
596     // in updatePaintedGeometry()
597     if (d->fillMode != PreserveAspectFit)
598         QDeclarative1ImageBase::pixmapChange();
599     updatePaintedGeometry();
600 }
601
602
603
604 QT_END_NAMESPACE