src/declarative/graphicsitems/qdeclarativeimage.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     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