141e643d2013399128361fb9d23f1414811028c8
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickimage.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickimage_p.h"
43 #include "qquickimage_p_p.h"
44
45 #include <QtQuick/qsgtextureprovider.h>
46
47 #include <QtQuick/private/qsgcontext_p.h>
48 #include <private/qsgadaptationlayer_p.h>
49
50 #include <QtGui/qpainter.h>
51 #include <qmath.h>
52
53 QT_BEGIN_NAMESPACE
54
55 class QQuickImageTextureProvider : public QSGTextureProvider
56 {
57     Q_OBJECT
58 public:
59     QQuickImageTextureProvider()
60         : m_texture(0)
61         , m_smooth(false)
62     {
63     }
64
65     QSGTexture *texture() const {
66         if (m_texture) {
67             m_texture->setFiltering(m_smooth ? QSGTexture::Linear : QSGTexture::Nearest);
68             m_texture->setMipmapFiltering(QSGTexture::Nearest);
69             m_texture->setHorizontalWrapMode(QSGTexture::ClampToEdge);
70             m_texture->setVerticalWrapMode(QSGTexture::ClampToEdge);
71         }
72         return m_texture;
73     }
74
75     friend class QQuickImage;
76
77     QSGTexture *m_texture;
78     bool m_smooth;
79 };
80
81 #include "qquickimage.moc"
82
83 QQuickImagePrivate::QQuickImagePrivate()
84     : fillMode(QQuickImage::Stretch)
85     , paintedWidth(0)
86     , paintedHeight(0)
87     , pixmapChanged(false)
88     , hAlign(QQuickImage::AlignHCenter)
89     , vAlign(QQuickImage::AlignVCenter)
90     , provider(0)
91 {
92 }
93
94 /*!
95     \qmlclass Image QQuickImage
96     \inqmlmodule QtQuick 2
97     \ingroup qtquick-visual
98     \inherits Item
99     \brief Displays an image
100
101     The Image type display an image.
102
103     The source of the image is specified as a URL using the \l source property.
104     Images can be supplied in any of the standard image formats supported by Qt,
105     including bitmap formats such as PNG and JPEG, and vector graphics formats
106     such as SVG. If you need to display animated images, use \l AnimatedSprite
107     or \l AnimatedImage.
108
109     If the \l{Item::width}{width} and \l{Item::height}{height} properties are not
110     specified, the Image automatically uses the size of the loaded image.
111     By default, specifying the width and height of the item causes the image
112     to be scaled to that size. This behavior can be changed by setting the
113     \l fillMode property, allowing the image to be stretched and tiled instead.
114
115     \section1 Example Usage
116
117     The following example shows the simplest usage of the Image type.
118
119     \snippet qml/image.qml document
120
121     \beginfloatleft
122     \image declarative-qtlogo.png
123     \endfloat
124
125     \clearfloat
126
127     \section1 Performance
128
129     By default, locally available images are loaded immediately, and the user interface
130     is blocked until loading is complete. If a large image is to be loaded, it may be
131     preferable to load the image in a low priority thread, by enabling the \l asynchronous
132     property.
133
134     If the image is obtained from a network rather than a local resource, it is
135     automatically loaded asynchronously, and the \l progress and \l status properties
136     are updated as appropriate.
137
138     Images are cached and shared internally, so if several Image items have the same \l source,
139     only one copy of the image will be loaded.
140
141     \b Note: Images are often the greatest user of memory in QML user interfaces.  It is recommended
142     that images which do not form part of the user interface have their
143     size bounded via the \l sourceSize property. This is especially important for content
144     that is loaded from external sources or provided by the user.
145
146     \sa {declarative/imageelements/image}{Image example}, QQuickImageProvider
147 */
148
149 QQuickImage::QQuickImage(QQuickItem *parent)
150     : QQuickImageBase(*(new QQuickImagePrivate), parent)
151 {
152 }
153
154 QQuickImage::QQuickImage(QQuickImagePrivate &dd, QQuickItem *parent)
155     : QQuickImageBase(dd, parent)
156 {
157 }
158
159 QQuickImage::~QQuickImage()
160 {
161     Q_D(QQuickImage);
162     if (d->provider)
163         d->provider->deleteLater();
164 }
165
166 void QQuickImagePrivate::setImage(const QImage &image)
167 {
168     Q_Q(QQuickImage);
169     pix.setImage(image);
170
171     q->pixmapChange();
172     status = pix.isNull() ? QQuickImageBase::Null : QQuickImageBase::Ready;
173
174     q->update();
175 }
176
177 /*!
178     \qmlproperty enumeration QtQuick2::Image::fillMode
179
180     Set this property to define what happens when the source image has a different size
181     than the item.
182
183     \list
184     \li Image.Stretch - the image is scaled to fit
185     \li Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
186     \li Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
187     \li Image.Tile - the image is duplicated horizontally and vertically
188     \li Image.TileVertically - the image is stretched horizontally and tiled vertically
189     \li Image.TileHorizontally - the image is stretched vertically and tiled horizontally
190     \li Image.Pad - the image is not transformed
191     \endlist
192
193     \table
194
195     \row
196     \li \image declarative-qtlogo-stretch.png
197     \li Stretch (default)
198     \qml
199     Image {
200         width: 130; height: 100
201         smooth: true
202         source: "qtlogo.png"
203     }
204     \endqml
205
206     \row
207     \li \image declarative-qtlogo-preserveaspectfit.png
208     \li PreserveAspectFit
209     \qml
210     Image {
211         width: 130; height: 100
212         fillMode: Image.PreserveAspectFit
213         smooth: true
214         source: "qtlogo.png"
215     }
216     \endqml
217
218     \row
219     \li \image declarative-qtlogo-preserveaspectcrop.png
220     \li PreserveAspectCrop
221     \qml
222     Image {
223         width: 130; height: 100
224         fillMode: Image.PreserveAspectCrop
225         smooth: true
226         source: "qtlogo.png"
227         clip: true
228     }
229     \endqml
230
231     \row
232     \li \image declarative-qtlogo-tile.png
233     \li Tile
234     \qml
235     Image {
236         width: 120; height: 120
237         fillMode: Image.Tile
238         source: "qtlogo.png"
239     }
240     \endqml
241
242     \row
243     \li \image declarative-qtlogo-tilevertically.png
244     \li TileVertically
245     \qml
246     Image {
247         width: 120; height: 120
248         fillMode: Image.TileVertically
249         smooth: true
250         source: "qtlogo.png"
251     }
252     \endqml
253
254     \row
255     \li \image declarative-qtlogo-tilehorizontally.png
256     \li TileHorizontally
257     \qml
258     Image {
259         width: 120; height: 120
260         fillMode: Image.TileHorizontally
261         smooth: true
262         source: "qtlogo.png"
263     }
264     \endqml
265
266     \endtable
267
268     Note that \c clip is \c false by default which means that the item might
269     paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop.
270
271     \sa {declarative/imageelements/image}{Image example}
272 */
273 QQuickImage::FillMode QQuickImage::fillMode() const
274 {
275     Q_D(const QQuickImage);
276     return d->fillMode;
277 }
278
279 void QQuickImage::setFillMode(FillMode mode)
280 {
281     Q_D(QQuickImage);
282     if (d->fillMode == mode)
283         return;
284     d->fillMode = mode;
285     update();
286     updatePaintedGeometry();
287     emit fillModeChanged();
288 }
289
290 /*!
291
292     \qmlproperty real QtQuick2::Image::paintedWidth
293     \qmlproperty real QtQuick2::Image::paintedHeight
294
295     These properties hold the size of the image that is actually painted.
296     In most cases it is the same as \c width and \c height, but when using a
297     \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop
298     \c paintedWidth or \c paintedHeight can be smaller or larger than
299     \c width and \c height of the Image item.
300 */
301 qreal QQuickImage::paintedWidth() const
302 {
303     Q_D(const QQuickImage);
304     return d->paintedWidth;
305 }
306
307 qreal QQuickImage::paintedHeight() const
308 {
309     Q_D(const QQuickImage);
310     return d->paintedHeight;
311 }
312
313 /*!
314     \qmlproperty enumeration QtQuick2::Image::status
315
316     This property holds the status of image loading.  It can be one of:
317     \list
318     \li Image.Null - no image has been set
319     \li Image.Ready - the image has been loaded
320     \li Image.Loading - the image is currently being loaded
321     \li Image.Error - an error occurred while loading the image
322     \endlist
323
324     Use this status to provide an update or respond to the status change in some way.
325     For example, you could:
326
327     \list
328     \li Trigger a state change:
329     \qml
330         State { name: 'loaded'; when: image.status == Image.Ready }
331     \endqml
332
333     \li Implement an \c onStatusChanged signal handler:
334     \qml
335         Image {
336             id: image
337             onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
338         }
339     \endqml
340
341     \li Bind to the status value:
342     \qml
343         Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' }
344     \endqml
345     \endlist
346
347     \sa progress
348 */
349
350 /*!
351     \qmlproperty real QtQuick2::Image::progress
352
353     This property holds the progress of image loading, from 0.0 (nothing loaded)
354     to 1.0 (finished).
355
356     \sa status
357 */
358
359 /*!
360     \qmlproperty bool QtQuick2::Image::smooth
361
362     Set this property if you want the image to be smoothly filtered when scaled or
363     transformed.  Smooth filtering gives better visual quality, but is slower.  If
364     the image is displayed at its natural size, this property has no visual or
365     performance effect.
366
367     \note Generally scaling artifacts are only visible if the image is stationary on
368     the screen.  A common pattern when animating an image is to disable smooth
369     filtering at the beginning of the animation and reenable it at the conclusion.
370 */
371
372 /*!
373     \qmlproperty QSize QtQuick2::Image::sourceSize
374
375     This property holds the actual width and height of the loaded image.
376
377     Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale
378     the painting of the image, this property sets the actual number of pixels
379     stored for the loaded image so that large images do not use more
380     memory than necessary. For example, this ensures the image in memory is no
381     larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and
382     \l {Item::}{height} values:
383
384     \code
385     Rectangle {
386         width: ...
387         height: ...
388
389         Image {
390            anchors.fill: parent
391            source: "reallyBigImage.jpg"
392            sourceSize.width: 1024
393            sourceSize.height: 1024
394         }
395     }
396     \endcode
397
398     If the image's actual size is larger than the sourceSize, the image is scaled down.
399     If only one dimension of the size is set to greater than 0, the
400     other dimension is set in proportion to preserve the source image's aspect ratio.
401     (The \l fillMode is independent of this.)
402
403     If both the sourceSize.width and sourceSize.height are set the image will be scaled
404     down to fit within the specified size, maintaining the image's aspect ratio.  The actual
405     size of the image after scaling is available via \l implicitWidth and \l implicitHeight.
406
407     If the source is an intrinsically scalable image (eg. SVG), this property
408     determines the size of the loaded image regardless of intrinsic size.
409     Avoid changing this property dynamically; rendering an SVG is \e slow compared
410     to an image.
411
412     If the source is a non-scalable image (eg. JPEG), the loaded image will
413     be no greater than this property specifies. For some formats (currently only JPEG),
414     the whole image will never actually be loaded into memory.
415
416     sourceSize can be cleared to the natural size of the image
417     by setting sourceSize to \c undefined.
418
419     \note \e {Changing this property dynamically causes the image source to be reloaded,
420     potentially even from the network, if it is not in the disk cache.}
421 */
422
423 /*!
424     \qmlproperty url QtQuick2::Image::source
425
426     Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
427
428     The URL may be absolute, or relative to the URL of the component.
429
430     \sa QQuickImageProvider
431 */
432
433 /*!
434     \qmlproperty bool QtQuick2::Image::asynchronous
435
436     Specifies that images on the local filesystem should be loaded
437     asynchronously in a separate thread.  The default value is
438     false, causing the user interface thread to block while the
439     image is loaded.  Setting \a asynchronous to true is useful where
440     maintaining a responsive user interface is more desirable
441     than having images immediately visible.
442
443     Note that this property is only valid for images read from the
444     local filesystem.  Images loaded via a network resource (e.g. HTTP)
445     are always loaded asynchronously.
446 */
447
448 /*!
449     \qmlproperty bool QtQuick2::Image::cache
450
451     Specifies whether the image should be cached. The default value is
452     true. Setting \a cache to false is useful when dealing with large images,
453     to make sure that they aren't cached at the expense of small 'ui element' images.
454 */
455
456 /*!
457     \qmlproperty bool QtQuick2::Image::mirror
458
459     This property holds whether the image should be horizontally inverted
460     (effectively displaying a mirrored image).
461
462     The default value is false.
463 */
464
465 /*!
466     \qmlproperty enumeration QtQuick2::Image::horizontalAlignment
467     \qmlproperty enumeration QtQuick2::Image::verticalAlignment
468
469     Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned.
470
471     The valid values for \c horizontalAlignment are \c Image.AlignLeft, \c Image.AlignRight and \c Image.AlignHCenter.
472     The valid values for \c verticalAlignment are \c Image.AlignTop, \c Image.AlignBottom
473     and \c Image.AlignVCenter.
474 */
475 void QQuickImage::updatePaintedGeometry()
476 {
477     Q_D(QQuickImage);
478
479     if (d->fillMode == PreserveAspectFit) {
480         if (!d->pix.width() || !d->pix.height()) {
481             setImplicitSize(0, 0);
482             return;
483         }
484         qreal w = widthValid() ? width() : d->pix.width();
485         qreal widthScale = w / qreal(d->pix.width());
486         qreal h = heightValid() ? height() : d->pix.height();
487         qreal heightScale = h / qreal(d->pix.height());
488         if (widthScale <= heightScale) {
489             d->paintedWidth = w;
490             d->paintedHeight = widthScale * qreal(d->pix.height());
491         } else if (heightScale < widthScale) {
492             d->paintedWidth = heightScale * qreal(d->pix.width());
493             d->paintedHeight = h;
494         }
495         qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : d->pix.height();
496         qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : d->pix.width();
497         setImplicitSize(iWidth, iHeight);
498
499     } else if (d->fillMode == PreserveAspectCrop) {
500         if (!d->pix.width() || !d->pix.height())
501             return;
502         qreal widthScale = width() / qreal(d->pix.width());
503         qreal heightScale = height() / qreal(d->pix.height());
504         if (widthScale < heightScale) {
505             widthScale = heightScale;
506         } else if (heightScale < widthScale) {
507             heightScale = widthScale;
508         }
509
510         d->paintedHeight = heightScale * qreal(d->pix.height());
511         d->paintedWidth = widthScale * qreal(d->pix.width());
512     } else if (d->fillMode == Pad) {
513         d->paintedWidth = d->pix.width();
514         d->paintedHeight = d->pix.height();
515     } else {
516         d->paintedWidth = width();
517         d->paintedHeight = height();
518     }
519     emit paintedGeometryChanged();
520 }
521
522 void QQuickImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
523 {
524     QQuickImageBase::geometryChanged(newGeometry, oldGeometry);
525     updatePaintedGeometry();
526 }
527
528 QRectF QQuickImage::boundingRect() const
529 {
530     Q_D(const QQuickImage);
531     return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
532 }
533
534 QSGTextureProvider *QQuickImage::textureProvider() const
535 {
536     Q_D(const QQuickImage);
537     if (!d->provider) {
538         // Make sure it gets thread affinity on the rendering thread so deletion works properly..
539         Q_ASSERT_X(d->canvas
540                    && d->sceneGraphContext()
541                    && QThread::currentThread() == d->sceneGraphContext()->thread(),
542                    "QQuickImage::textureProvider",
543                    "Cannot be used outside the GUI thread");
544         QQuickImagePrivate *dd = const_cast<QQuickImagePrivate *>(d);
545         dd->provider = new QQuickImageTextureProvider;
546         dd->provider->m_smooth = d->smooth;
547         dd->provider->m_texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), canvas());
548     }
549
550     return d->provider;
551 }
552
553 QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
554 {
555     Q_D(QQuickImage);
556
557     QSGTexture *texture = d->sceneGraphContext()->textureForFactory(d->pix.textureFactory(), canvas());
558
559     // Copy over the current texture state into the texture provider...
560     if (d->provider) {
561         d->provider->m_smooth = d->smooth;
562         d->provider->m_texture = texture;
563     }
564
565     if (!texture || width() <= 0 || height() <= 0) {
566         delete oldNode;
567         return 0;
568     }
569
570     QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
571     if (!node) {
572         d->pixmapChanged = true;
573         node = d->sceneGraphContext()->createImageNode();
574         node->setTexture(texture);
575     }
576
577     if (d->pixmapChanged) {
578         // force update the texture in the node to trigger reconstruction of
579         // geometry and the likes when a atlas segment has changed.
580         node->setTexture(0);
581         node->setTexture(texture);
582         d->pixmapChanged = false;
583     }
584
585     QRectF targetRect;
586     QRectF sourceRect;
587     QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
588     QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
589
590     qreal pixWidth = (d->fillMode == PreserveAspectFit) ? d->paintedWidth : d->pix.width();
591     qreal pixHeight = (d->fillMode == PreserveAspectFit) ? d->paintedHeight : d->pix.height();
592
593     int xOffset = 0;
594     if (d->hAlign == QQuickImage::AlignHCenter)
595         xOffset = qCeil((width() - pixWidth) / 2.);
596     else if (d->hAlign == QQuickImage::AlignRight)
597         xOffset = qCeil(width() - pixWidth);
598
599     int yOffset = 0;
600     if (d->vAlign == QQuickImage::AlignVCenter)
601         yOffset = qCeil((height() - pixHeight) / 2.);
602     else if (d->vAlign == QQuickImage::AlignBottom)
603         yOffset = qCeil(height() - pixHeight);
604
605     switch (d->fillMode) {
606     default:
607     case Stretch:
608         targetRect = QRectF(0, 0, width(), height());
609         sourceRect = d->pix.rect();
610         break;
611
612     case PreserveAspectFit:
613         targetRect = QRectF(xOffset, yOffset, d->paintedWidth, d->paintedHeight);
614         sourceRect = d->pix.rect();
615         break;
616
617     case PreserveAspectCrop: {
618         targetRect = QRect(0, 0, width(), height());
619         qreal wscale = width() / qreal(d->pix.width());
620         qreal hscale = height() / qreal(d->pix.height());
621
622         if (wscale > hscale) {
623             int src = (hscale / wscale) * qreal(d->pix.height());
624             int y = 0;
625             if (d->vAlign == QQuickImage::AlignVCenter)
626                 y = qCeil((d->pix.height() - src) / 2.);
627             else if (d->vAlign == QQuickImage::AlignBottom)
628                 y = qCeil(d->pix.height() - src);
629             sourceRect = QRectF(0, y, d->pix.width(), src);
630
631         } else {
632             int src = (wscale / hscale) * qreal(d->pix.width());
633             int x = 0;
634             if (d->hAlign == QQuickImage::AlignHCenter)
635                 x = qCeil((d->pix.width() - src) / 2.);
636             else if (d->hAlign == QQuickImage::AlignRight)
637                 x = qCeil(d->pix.width() - src);
638             sourceRect = QRectF(x, 0, src, d->pix.height());
639         }
640         }
641         break;
642
643     case Tile:
644         targetRect = QRectF(0, 0, width(), height());
645         sourceRect = QRectF(-xOffset, -yOffset, width(), height());
646         hWrap = QSGTexture::Repeat;
647         vWrap = QSGTexture::Repeat;
648         break;
649
650     case TileHorizontally:
651         targetRect = QRectF(0, 0, width(), height());
652         sourceRect = QRectF(-xOffset, 0, width(), d->pix.height());
653         hWrap = QSGTexture::Repeat;
654         break;
655
656     case TileVertically:
657         targetRect = QRectF(0, 0, width(), height());
658         sourceRect = QRectF(0, -yOffset, d->pix.width(), height());
659         vWrap = QSGTexture::Repeat;
660         break;
661
662     case Pad:
663         qreal w = qMin(qreal(d->pix.width()), width());
664         qreal h = qMin(qreal(d->pix.height()), height());
665         qreal x = (d->pix.width() > width()) ? -xOffset : 0;
666         qreal y = (d->pix.height() > height()) ? -yOffset : 0;
667         targetRect = QRectF(x + xOffset, y + yOffset, w, h);
668         sourceRect = QRectF(x, y, w, h);
669         break;
670     };
671
672     QRectF nsrect(sourceRect.x() / d->pix.width(),
673                   sourceRect.y() / d->pix.height(),
674                   sourceRect.width() / d->pix.width(),
675                   sourceRect.height() / d->pix.height());
676
677     node->setHorizontalWrapMode(hWrap);
678     node->setVerticalWrapMode(vWrap);
679     node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
680
681     node->setTargetRect(targetRect);
682     node->setInnerTargetRect(targetRect);
683     node->setSubSourceRect(nsrect);
684     node->setMirror(d->mirror);
685     node->setAntialiasing(d->antialiasing);
686     node->update();
687
688     return node;
689 }
690
691 void QQuickImage::pixmapChange()
692 {
693     Q_D(QQuickImage);
694     // PreserveAspectFit calculates the implicit size differently so we
695     // don't call our superclass pixmapChange(), since that would
696     // result in the implicit size being set incorrectly, then updated
697     // in updatePaintedGeometry()
698     if (d->fillMode != PreserveAspectFit)
699         QQuickImageBase::pixmapChange();
700     updatePaintedGeometry();
701     d->pixmapChanged = true;
702
703     // When the pixmap changes, such as being deleted, we need to update the textures
704     update();
705 }
706
707 QQuickImage::VAlignment QQuickImage::verticalAlignment() const
708 {
709     Q_D(const QQuickImage);
710     return d->vAlign;
711 }
712
713 void QQuickImage::setVerticalAlignment(VAlignment align)
714 {
715     Q_D(QQuickImage);
716     if (d->vAlign == align)
717         return;
718
719     d->vAlign = align;
720     update();
721     updatePaintedGeometry();
722     emit verticalAlignmentChanged(align);
723 }
724
725 QQuickImage::HAlignment QQuickImage::horizontalAlignment() const
726 {
727     Q_D(const QQuickImage);
728     return d->hAlign;
729 }
730
731 void QQuickImage::setHorizontalAlignment(HAlignment align)
732 {
733     Q_D(QQuickImage);
734     if (d->hAlign == align)
735         return;
736
737     d->hAlign = align;
738     update();
739     updatePaintedGeometry();
740     emit horizontalAlignmentChanged(align);
741 }
742
743 QT_END_NAMESPACE