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