|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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 \brief The Image element allows you to add bitmaps to a scene. |
|
55 \inherits Item |
|
56 |
|
57 An Image element displays a specified \l source image: |
|
58 |
|
59 \table |
|
60 \row |
|
61 \o \image declarative-qtlogo.png |
|
62 \o \qml |
|
63 import Qt 4.7 |
|
64 |
|
65 Image { source: "qtlogo.png" } |
|
66 \endqml |
|
67 \endtable |
|
68 |
|
69 If a size is not specified explicitly, the Image element is sized to the loaded image. |
|
70 Image elements can be stretched and tiled using the \l fillMode property. |
|
71 |
|
72 If the image \l source is a network resource, the image is loaded asynchronous and the |
|
73 \l progress and \l status properties are updated appropriately. Otherwise, if the image is |
|
74 available locally, it is loaded immediately and the user interface is blocked until loading is |
|
75 complete. (This is typically the correct behavior for user interface elements.) |
|
76 For large local images, which do not need to be visible immediately, it may be preferable to |
|
77 enable \l asynchronous loading. This loads the image in the background using a low priority thread. |
|
78 |
|
79 Images are cached and shared internally, so if several Image elements have the same source |
|
80 only one copy of the image will be loaded. |
|
81 |
|
82 \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended |
|
83 that images which do not form part of the user interface have their |
|
84 size bounded via the \l sourceSize property. This is especially important for content |
|
85 that is loaded from external sources or provided by the user. |
|
86 */ |
|
87 |
|
88 /*! |
|
89 \internal |
|
90 \class QDeclarativeImage Image |
|
91 \brief The QDeclarativeImage class provides an image item that you can add to a QDeclarativeView. |
|
92 |
|
93 Example: |
|
94 \qml |
|
95 Image { source: "pics/star.png" } |
|
96 \endqml |
|
97 |
|
98 A QDeclarativeImage object can be instantiated in Qml using the tag \l Image. |
|
99 */ |
|
100 |
|
101 QDeclarativeImage::QDeclarativeImage(QDeclarativeItem *parent) |
|
102 : QDeclarativeImageBase(*(new QDeclarativeImagePrivate), parent) |
|
103 { |
|
104 } |
|
105 |
|
106 QDeclarativeImage::QDeclarativeImage(QDeclarativeImagePrivate &dd, QDeclarativeItem *parent) |
|
107 : QDeclarativeImageBase(dd, parent) |
|
108 { |
|
109 } |
|
110 |
|
111 QDeclarativeImage::~QDeclarativeImage() |
|
112 { |
|
113 } |
|
114 |
|
115 /*! |
|
116 \qmlproperty QPixmap Image::pixmap |
|
117 |
|
118 This property holds the QPixmap image to display. |
|
119 |
|
120 This is useful for displaying images provided by a C++ implementation, |
|
121 for example, a model may provide a data role of type QPixmap. |
|
122 */ |
|
123 |
|
124 QPixmap QDeclarativeImage::pixmap() const |
|
125 { |
|
126 Q_D(const QDeclarativeImage); |
|
127 return d->pix; |
|
128 } |
|
129 |
|
130 void QDeclarativeImage::setPixmap(const QPixmap &pix) |
|
131 { |
|
132 Q_D(QDeclarativeImage); |
|
133 if (!d->url.isEmpty()) |
|
134 return; |
|
135 d->setPixmap(pix); |
|
136 } |
|
137 |
|
138 void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap) |
|
139 { |
|
140 Q_Q(QDeclarativeImage); |
|
141 pix = pixmap; |
|
142 |
|
143 q->setImplicitWidth(pix.width()); |
|
144 q->setImplicitHeight(pix.height()); |
|
145 status = pix.isNull() ? QDeclarativeImageBase::Null : QDeclarativeImageBase::Ready; |
|
146 |
|
147 q->update(); |
|
148 q->pixmapChange(); |
|
149 } |
|
150 |
|
151 /*! |
|
152 \qmlproperty enumeration Image::fillMode |
|
153 |
|
154 Set this property to define what happens when the image set for the item is smaller |
|
155 than the size of the item. |
|
156 |
|
157 \list |
|
158 \o Image.Stretch - the image is scaled to fit |
|
159 \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping |
|
160 \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary |
|
161 \o Image.Tile - the image is duplicated horizontally and vertically |
|
162 \o Image.TileVertically - the image is stretched horizontally and tiled vertically |
|
163 \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally |
|
164 \endlist |
|
165 |
|
166 \table |
|
167 |
|
168 \row |
|
169 \o \image declarative-qtlogo-stretch.png |
|
170 \o Stretch (default) |
|
171 \qml |
|
172 Image { |
|
173 width: 130; height: 100 |
|
174 smooth: true |
|
175 source: "qtlogo.png" |
|
176 } |
|
177 \endqml |
|
178 |
|
179 \row |
|
180 \o \image declarative-qtlogo-preserveaspectfit.png |
|
181 \o PreserveAspectFit |
|
182 \qml |
|
183 Image { |
|
184 width: 130; height: 100 |
|
185 fillMode: Image.PreserveAspectFit |
|
186 smooth: true |
|
187 source: "qtlogo.png" |
|
188 } |
|
189 \endqml |
|
190 |
|
191 \row |
|
192 \o \image declarative-qtlogo-preserveaspectcrop.png |
|
193 \o PreserveAspectCrop |
|
194 \qml |
|
195 Image { |
|
196 width: 130; height: 100 |
|
197 fillMode: Image.PreserveAspectCrop |
|
198 smooth: true |
|
199 source: "qtlogo.png" |
|
200 } |
|
201 \endqml |
|
202 |
|
203 \row |
|
204 \o \image declarative-qtlogo-tile.png |
|
205 \o Tile |
|
206 \qml |
|
207 Image { |
|
208 width: 120; height: 120 |
|
209 fillMode: Image.Tile |
|
210 source: "qtlogo.png" |
|
211 } |
|
212 \endqml |
|
213 |
|
214 \row |
|
215 \o \image declarative-qtlogo-tilevertically.png |
|
216 \o TileVertically |
|
217 \qml |
|
218 Image { |
|
219 width: 120; height: 120 |
|
220 fillMode: Image.TileVertically |
|
221 source: "qtlogo.png" |
|
222 } |
|
223 \endqml |
|
224 |
|
225 \row |
|
226 \o \image declarative-qtlogo-tilehorizontally.png |
|
227 \o TileHorizontally |
|
228 \qml |
|
229 Image { |
|
230 width: 120; height: 120 |
|
231 fillMode: Image.TileHorizontally |
|
232 source: "qtlogo.png" |
|
233 } |
|
234 \endqml |
|
235 |
|
236 \endtable |
|
237 */ |
|
238 QDeclarativeImage::FillMode QDeclarativeImage::fillMode() const |
|
239 { |
|
240 Q_D(const QDeclarativeImage); |
|
241 return d->fillMode; |
|
242 } |
|
243 |
|
244 void QDeclarativeImage::setFillMode(FillMode mode) |
|
245 { |
|
246 Q_D(QDeclarativeImage); |
|
247 if (d->fillMode == mode) |
|
248 return; |
|
249 d->fillMode = mode; |
|
250 update(); |
|
251 updatePaintedGeometry(); |
|
252 emit fillModeChanged(); |
|
253 } |
|
254 |
|
255 qreal QDeclarativeImage::paintedWidth() const |
|
256 { |
|
257 Q_D(const QDeclarativeImage); |
|
258 return d->paintedWidth; |
|
259 } |
|
260 |
|
261 qreal QDeclarativeImage::paintedHeight() const |
|
262 { |
|
263 Q_D(const QDeclarativeImage); |
|
264 return d->paintedHeight; |
|
265 } |
|
266 |
|
267 /*! |
|
268 \qmlproperty enumeration Image::status |
|
269 |
|
270 This property holds the status of image loading. It can be one of: |
|
271 \list |
|
272 \o Image.Null - no image has been set |
|
273 \o Image.Ready - the image has been loaded |
|
274 \o Image.Loading - the image is currently being loaded |
|
275 \o Image.Error - an error occurred while loading the image |
|
276 \endlist |
|
277 |
|
278 Note that a change in the status property does not cause anything to happen |
|
279 (although it reflects what has happened with the image internally). If you wish |
|
280 to react to the change in status you need to do it yourself, for example in one |
|
281 of the following ways: |
|
282 |
|
283 Use this status to provide an update or respond to the status change in some way. |
|
284 For example, you could: |
|
285 |
|
286 \e {Trigger a state change:} |
|
287 \qml |
|
288 State { name: 'loaded'; when: image.status = Image.Ready } |
|
289 \endqml |
|
290 |
|
291 \e {Implement an \c onStatusChanged signal handler:} |
|
292 \qml |
|
293 Image { |
|
294 id: image |
|
295 onStatusChanged: if (image.status == Image.Ready) console.log('Loaded') |
|
296 } |
|
297 \endqml |
|
298 |
|
299 \e {Bind to the status value:} |
|
300 \qml |
|
301 Text { text: image.status != Image.Ready ? 'Not Loaded' : 'Loaded' } |
|
302 \endqml |
|
303 |
|
304 \sa progress |
|
305 */ |
|
306 |
|
307 /*! |
|
308 \qmlproperty real Image::progress |
|
309 |
|
310 This property holds the progress of image loading, from 0.0 (nothing loaded) |
|
311 to 1.0 (finished). |
|
312 |
|
313 \sa status |
|
314 */ |
|
315 |
|
316 /*! |
|
317 \qmlproperty bool Image::smooth |
|
318 |
|
319 Set this property if you want the image to be smoothly filtered when scaled or |
|
320 transformed. Smooth filtering gives better visual quality, but is slower. If |
|
321 the image is displayed at its natural size, this property has no visual or |
|
322 performance effect. |
|
323 |
|
324 \note Generally scaling artifacts are only visible if the image is stationary on |
|
325 the screen. A common pattern when animating an image is to disable smooth |
|
326 filtering at the beginning of the animation and reenable it at the conclusion. |
|
327 */ |
|
328 |
|
329 /*! |
|
330 \qmlproperty QSize Image::sourceSize |
|
331 |
|
332 This property holds the size of the loaded image, in pixels. |
|
333 |
|
334 This is used to control the storage used by a loaded image. Unlike |
|
335 the width and height properties, which scale the painting of the image, this property |
|
336 affects the number of pixels stored. |
|
337 |
|
338 If the image's actual size is larger than the sourceSize, the image is scaled down. |
|
339 If only one dimension of the size is set to greater than 0, the |
|
340 other dimension is set in proportion to preserve the source image's aspect ratio. |
|
341 (The \l fillMode is independent of this.) |
|
342 |
|
343 If the source is an instrinsically scalable image (eg. SVG), this property |
|
344 determines the size of the loaded image regardless of intrinsic size. |
|
345 Avoid changing this property dynamically; rendering an SVG is \e slow compared |
|
346 to an image. |
|
347 |
|
348 If the source is a non-scalable image (eg. JPEG), the loaded image will |
|
349 be no greater than this property specifies. For some formats (currently only JPEG), |
|
350 the whole image will never actually be loaded into memory. |
|
351 |
|
352 \note \e{Changing this property dynamically will lead to the image source being reloaded, |
|
353 potentially even from the network if it is not in the disk cache.} |
|
354 |
|
355 Here is an example that ensures the size of the image in memory is |
|
356 no larger than 1024x1024 pixels, regardless of the size of the Image element. |
|
357 |
|
358 \code |
|
359 Image { |
|
360 anchors.fill: parent |
|
361 source: "images/reallyBigImage.jpg" |
|
362 sourceSize.width: 1024 |
|
363 sourceSize.height: 1024 |
|
364 } |
|
365 \endcode |
|
366 |
|
367 The example below ensures the memory used by the image is no more than necessary |
|
368 to display the image at the size of the Image element. |
|
369 Of course if the Image element is resized a costly reload will be required, so |
|
370 use this technique \e only when the Image size is fixed. |
|
371 |
|
372 \code |
|
373 Image { |
|
374 anchors.fill: parent |
|
375 source: "images/reallyBigImage.jpg" |
|
376 sourceSize.width: width |
|
377 sourceSize.height: height |
|
378 } |
|
379 \endcode |
|
380 */ |
|
381 |
|
382 void QDeclarativeImage::updatePaintedGeometry() |
|
383 { |
|
384 Q_D(QDeclarativeImage); |
|
385 |
|
386 if (d->fillMode == PreserveAspectFit) { |
|
387 if (!d->pix.width() || !d->pix.height()) |
|
388 return; |
|
389 qreal widthScale = width() / qreal(d->pix.width()); |
|
390 qreal heightScale = height() / qreal(d->pix.height()); |
|
391 if (widthScale <= heightScale) { |
|
392 d->paintedWidth = width(); |
|
393 d->paintedHeight = widthScale * qreal(d->pix.height()); |
|
394 } else if(heightScale < widthScale) { |
|
395 d->paintedWidth = heightScale * qreal(d->pix.width()); |
|
396 d->paintedHeight = height(); |
|
397 } |
|
398 if (widthValid() && !heightValid()) { |
|
399 setImplicitHeight(d->paintedHeight); |
|
400 } |
|
401 if (heightValid() && !widthValid()) { |
|
402 setImplicitWidth(d->paintedWidth); |
|
403 } |
|
404 } else { |
|
405 d->paintedWidth = width(); |
|
406 d->paintedHeight = height(); |
|
407 } |
|
408 emit paintedGeometryChanged(); |
|
409 } |
|
410 |
|
411 void QDeclarativeImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
|
412 { |
|
413 QDeclarativeImageBase::geometryChanged(newGeometry, oldGeometry); |
|
414 updatePaintedGeometry(); |
|
415 } |
|
416 |
|
417 /*! |
|
418 \qmlproperty url Image::source |
|
419 |
|
420 Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt. |
|
421 |
|
422 The URL may be absolute, or relative to the URL of the component. |
|
423 */ |
|
424 |
|
425 /*! |
|
426 \qmlproperty bool Image::asynchronous |
|
427 |
|
428 Specifies that images on the local filesystem should be loaded |
|
429 asynchronously in a separate thread. The default value is |
|
430 false, causing the user interface thread to block while the |
|
431 image is loaded. Setting \a asynchronous to true is useful where |
|
432 maintaining a responsive user interface is more desireable |
|
433 than having images immediately visible. |
|
434 |
|
435 Note that this property is only valid for images read from the |
|
436 local filesystem. Images loaded via a network resource (e.g. HTTP) |
|
437 are always loaded asynchonously. |
|
438 */ |
|
439 |
|
440 void QDeclarativeImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) |
|
441 { |
|
442 Q_D(QDeclarativeImage); |
|
443 if (d->pix.isNull()) |
|
444 return; |
|
445 |
|
446 bool oldAA = p->testRenderHint(QPainter::Antialiasing); |
|
447 bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); |
|
448 if (d->smooth) |
|
449 p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); |
|
450 |
|
451 if (width() != d->pix.width() || height() != d->pix.height()) { |
|
452 if (d->fillMode >= Tile) { |
|
453 if (d->fillMode == Tile) { |
|
454 p->drawTiledPixmap(QRectF(0,0,width(),height()), d->pix); |
|
455 } else { |
|
456 qreal widthScale = width() / qreal(d->pix.width()); |
|
457 qreal heightScale = height() / qreal(d->pix.height()); |
|
458 |
|
459 QTransform scale; |
|
460 if (d->fillMode == TileVertically) { |
|
461 scale.scale(widthScale, 1.0); |
|
462 QTransform old = p->transform(); |
|
463 p->setWorldTransform(scale * old); |
|
464 p->drawTiledPixmap(QRectF(0,0,d->pix.width(),height()), d->pix); |
|
465 p->setWorldTransform(old); |
|
466 } else { |
|
467 scale.scale(1.0, heightScale); |
|
468 QTransform old = p->transform(); |
|
469 p->setWorldTransform(scale * old); |
|
470 p->drawTiledPixmap(QRectF(0,0,width(),d->pix.height()), d->pix); |
|
471 p->setWorldTransform(old); |
|
472 } |
|
473 } |
|
474 } else { |
|
475 qreal widthScale = width() / qreal(d->pix.width()); |
|
476 qreal heightScale = height() / qreal(d->pix.height()); |
|
477 |
|
478 QTransform scale; |
|
479 |
|
480 if (d->fillMode == PreserveAspectFit) { |
|
481 if (widthScale <= heightScale) { |
|
482 heightScale = widthScale; |
|
483 scale.translate(0, (height() - heightScale * d->pix.height()) / 2); |
|
484 } else if(heightScale < widthScale) { |
|
485 widthScale = heightScale; |
|
486 scale.translate((width() - widthScale * d->pix.width()) / 2, 0); |
|
487 } |
|
488 } else if (d->fillMode == PreserveAspectCrop) { |
|
489 if (widthScale < heightScale) { |
|
490 widthScale = heightScale; |
|
491 scale.translate((width() - widthScale * d->pix.width()) / 2, 0); |
|
492 } else if(heightScale < widthScale) { |
|
493 heightScale = widthScale; |
|
494 scale.translate(0, (height() - heightScale * d->pix.height()) / 2); |
|
495 } |
|
496 } |
|
497 if (clip()) { |
|
498 p->save(); |
|
499 p->setClipRect(boundingRect(), Qt::IntersectClip); |
|
500 } |
|
501 scale.scale(widthScale, heightScale); |
|
502 QTransform old = p->transform(); |
|
503 p->setWorldTransform(scale * old); |
|
504 p->drawPixmap(0, 0, d->pix); |
|
505 p->setWorldTransform(old); |
|
506 if (clip()) { |
|
507 p->restore(); |
|
508 } |
|
509 } |
|
510 } else { |
|
511 p->drawPixmap(0, 0, d->pix); |
|
512 } |
|
513 |
|
514 if (d->smooth) { |
|
515 p->setRenderHint(QPainter::Antialiasing, oldAA); |
|
516 p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); |
|
517 } |
|
518 } |
|
519 |
|
520 void QDeclarativeImage::pixmapChange() |
|
521 { |
|
522 updatePaintedGeometry(); |
|
523 emit pixmapChanged(); |
|
524 } |
|
525 |
|
526 QT_END_NAMESPACE |