src/declarative/graphicsitems/qdeclarativeimage.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/declarative/graphicsitems/qdeclarativeimage.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -0,0 +1,526 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qdeclarativeimage_p.h"
+#include "private/qdeclarativeimage_p_p.h"
+
+#include <QKeyEvent>
+#include <QPainter>
+
+QT_BEGIN_NAMESPACE
+
+
+/*!
+    \qmlclass Image QDeclarativeImage
+    \since 4.7
+    \brief The Image element allows you to add bitmaps to a scene.
+    \inherits Item
+
+    An Image element displays a specified \l source image:
+
+    \table
+    \row
+    \o \image declarative-qtlogo.png
+    \o \qml
+        import Qt 4.7
+
+        Image { source: "qtlogo.png" }
+    \endqml
+    \endtable
+    
+    If a size is not specified explicitly, the Image element is sized to the loaded image.
+    Image elements can be stretched and tiled using the \l fillMode property.
+
+    If the image \l source is a network resource, the image is loaded asynchronous and the
+    \l progress and \l status properties are updated appropriately.  Otherwise, if the image is
+    available locally, it is loaded immediately and the user interface is blocked until loading is
+    complete.  (This is typically the correct behavior for user interface elements.)
+    For large local images, which do not need to be visible immediately, it may be preferable to
+    enable \l asynchronous loading.  This loads the image in the background using a low priority thread.
+
+    Images are cached and shared internally, so if several Image elements have the same source
+    only one copy of the image will be loaded.
+
+    \bold Note: Images are often the greatest user of memory in QML user interfaces.  It is recommended
+    that images which do not form part of the user interface have their
+    size bounded via the \l sourceSize property. This is especially important for content
+    that is loaded from external sources or provided by the user.
+*/
+
+/*!
+    \internal
+    \class QDeclarativeImage Image
+    \brief The QDeclarativeImage class provides an image item that you can add to a QDeclarativeView.
+
+    Example:
+    \qml
+    Image { source: "pics/star.png" }
+    \endqml
+
+    A QDeclarativeImage object can be instantiated in Qml using the tag \l Image.
+*/
+
+QDeclarativeImage::QDeclarativeImage(QDeclarativeItem *parent)
+    : QDeclarativeImageBase(*(new QDeclarativeImagePrivate), parent)
+{
+}
+
+QDeclarativeImage::QDeclarativeImage(QDeclarativeImagePrivate &dd, QDeclarativeItem *parent)
+    : QDeclarativeImageBase(dd, parent)
+{
+}
+
+QDeclarativeImage::~QDeclarativeImage()
+{
+}
+
+/*!
+    \qmlproperty QPixmap Image::pixmap
+
+    This property holds the QPixmap image to display.
+
+    This is useful for displaying images provided by a C++ implementation,
+    for example, a model may provide a data role of type QPixmap.
+*/
+
+QPixmap QDeclarativeImage::pixmap() const
+{
+    Q_D(const QDeclarativeImage);
+    return d->pix;
+}
+
+void QDeclarativeImage::setPixmap(const QPixmap &pix)
+{
+    Q_D(QDeclarativeImage);
+    if (!d->url.isEmpty())
+        return;
+    d->setPixmap(pix);
+}
+
+void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap)
+{
+    Q_Q(QDeclarativeImage);
+    pix = pixmap;
+
+    q->setImplicitWidth(pix.width());
+    q->setImplicitHeight(pix.height());
+    status = pix.isNull() ? QDeclarativeImageBase::Null : QDeclarativeImageBase::Ready;
+
+    q->update();
+    q->pixmapChange();
+}
+
+/*!
+    \qmlproperty enumeration Image::fillMode
+
+    Set this property to define what happens when the image set for the item is smaller
+    than the size of the item.
+
+    \list
+    \o Image.Stretch - the image is scaled to fit
+    \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping
+    \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary
+    \o Image.Tile - the image is duplicated horizontally and vertically
+    \o Image.TileVertically - the image is stretched horizontally and tiled vertically
+    \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally
+    \endlist
+
+    \table
+    
+    \row
+    \o \image declarative-qtlogo-stretch.png
+    \o Stretch (default)
+    \qml
+    Image {
+        width: 130; height: 100
+        smooth: true
+        source: "qtlogo.png"
+    }
+    \endqml
+
+    \row
+    \o \image declarative-qtlogo-preserveaspectfit.png
+    \o PreserveAspectFit
+    \qml
+    Image {
+        width: 130; height: 100
+        fillMode: Image.PreserveAspectFit
+        smooth: true
+        source: "qtlogo.png"
+    }
+    \endqml
+
+    \row
+    \o \image declarative-qtlogo-preserveaspectcrop.png
+    \o PreserveAspectCrop
+    \qml
+    Image {
+        width: 130; height: 100
+        fillMode: Image.PreserveAspectCrop
+        smooth: true
+        source: "qtlogo.png"
+    }
+    \endqml
+
+    \row
+    \o \image declarative-qtlogo-tile.png
+    \o Tile
+    \qml
+    Image {
+        width: 120; height: 120
+        fillMode: Image.Tile
+        source: "qtlogo.png"
+    }
+    \endqml
+
+    \row
+    \o \image declarative-qtlogo-tilevertically.png
+    \o TileVertically
+    \qml
+    Image {
+        width: 120; height: 120
+        fillMode: Image.TileVertically
+        source: "qtlogo.png"
+    }
+    \endqml
+
+    \row
+    \o \image declarative-qtlogo-tilehorizontally.png
+    \o TileHorizontally
+    \qml
+    Image {
+        width: 120; height: 120
+        fillMode: Image.TileHorizontally
+        source: "qtlogo.png"
+    }
+    \endqml
+
+    \endtable
+*/
+QDeclarativeImage::FillMode QDeclarativeImage::fillMode() const
+{
+    Q_D(const QDeclarativeImage);
+    return d->fillMode;
+}
+
+void QDeclarativeImage::setFillMode(FillMode mode)
+{
+    Q_D(QDeclarativeImage);
+    if (d->fillMode == mode)
+        return;
+    d->fillMode = mode;
+    update();
+    updatePaintedGeometry();
+    emit fillModeChanged();
+}
+
+qreal QDeclarativeImage::paintedWidth() const
+{
+    Q_D(const QDeclarativeImage);
+    return d->paintedWidth;
+}
+
+qreal QDeclarativeImage::paintedHeight() const
+{
+    Q_D(const QDeclarativeImage);
+    return d->paintedHeight;
+}
+
+/*!
+    \qmlproperty enumeration Image::status
+
+    This property holds the status of image loading.  It can be one of:
+    \list
+    \o Image.Null - no image has been set
+    \o Image.Ready - the image has been loaded
+    \o Image.Loading - the image is currently being loaded
+    \o Image.Error - an error occurred while loading the image
+    \endlist
+
+    Note that a change in the status property does not cause anything to happen
+    (although it reflects what has happened with the image internally). If you wish
+    to react to the change in status you need to do it yourself, for example in one
+    of the following ways:
+
+    Use this status to provide an update or respond to the status change in some way.
+    For example, you could:
+
+    \e {Trigger a state change:}
+    \qml 
+        State { name: 'loaded'; when: image.status = Image.Ready }
+    \endqml
+
+    \e {Implement an \c onStatusChanged signal handler:}
+    \qml 
+        Image {
+            id: image
+            onStatusChanged: if (image.status == Image.Ready) console.log('Loaded')
+        }
+    \endqml
+
+    \e {Bind to the status value:}
+    \qml
+        Text { text: image.status != Image.Ready ? 'Not Loaded' : 'Loaded' }
+    \endqml
+
+    \sa progress
+*/
+
+/*!
+    \qmlproperty real Image::progress
+
+    This property holds the progress of image loading, from 0.0 (nothing loaded)
+    to 1.0 (finished).
+
+    \sa status
+*/
+
+/*!
+    \qmlproperty bool Image::smooth
+
+    Set this property if you want the image to be smoothly filtered when scaled or
+    transformed.  Smooth filtering gives better visual quality, but is slower.  If
+    the image is displayed at its natural size, this property has no visual or
+    performance effect.
+
+    \note Generally scaling artifacts are only visible if the image is stationary on
+    the screen.  A common pattern when animating an image is to disable smooth
+    filtering at the beginning of the animation and reenable it at the conclusion.
+*/
+
+/*!
+    \qmlproperty QSize Image::sourceSize
+
+    This property holds the size of the loaded image, in pixels.
+
+    This is used to control the storage used by a loaded image. Unlike
+    the width and height properties, which scale the painting of the image, this property
+    affects the number of pixels stored.
+
+    If the image's actual size is larger than the sourceSize, the image is scaled down.
+    If only one dimension of the size is set to greater than 0, the
+    other dimension is set in proportion to preserve the source image's aspect ratio.
+    (The \l fillMode is independent of this.)
+   
+    If the source is an instrinsically scalable image (eg. SVG), this property
+    determines the size of the loaded image regardless of intrinsic size.
+    Avoid changing this property dynamically; rendering an SVG is \e slow compared
+    to an image.
+
+    If the source is a non-scalable image (eg. JPEG), the loaded image will
+    be no greater than this property specifies. For some formats (currently only JPEG),
+    the whole image will never actually be loaded into memory.
+ 
+    \note \e{Changing this property dynamically will lead to the image source being reloaded,
+    potentially even from the network if it is not in the disk cache.}
+
+    Here is an example that ensures the size of the image in memory is
+    no larger than 1024x1024 pixels, regardless of the size of the Image element.
+
+    \code
+    Image {
+       anchors.fill: parent
+       source: "images/reallyBigImage.jpg"
+       sourceSize.width: 1024
+       sourceSize.height: 1024
+    }
+    \endcode
+
+    The example below ensures the memory used by the image is no more than necessary 
+    to display the image at the size of the Image element.
+    Of course if the Image element is resized a costly reload will be required, so
+    use this technique \e only when the Image size is fixed.
+
+    \code
+    Image {
+       anchors.fill: parent
+       source: "images/reallyBigImage.jpg"
+       sourceSize.width: width
+       sourceSize.height: height
+    }
+    \endcode
+*/
+
+void QDeclarativeImage::updatePaintedGeometry()
+{
+    Q_D(QDeclarativeImage);
+
+    if (d->fillMode == PreserveAspectFit) {
+        if (!d->pix.width() || !d->pix.height())
+            return;
+        qreal widthScale = width() / qreal(d->pix.width());
+        qreal heightScale = height() / qreal(d->pix.height());
+        if (widthScale <= heightScale) {
+            d->paintedWidth = width();
+            d->paintedHeight = widthScale * qreal(d->pix.height());
+        } else if(heightScale < widthScale) {
+            d->paintedWidth = heightScale * qreal(d->pix.width());
+            d->paintedHeight = height();
+        }
+        if (widthValid() && !heightValid()) {
+            setImplicitHeight(d->paintedHeight);
+        }
+        if (heightValid() && !widthValid()) {
+            setImplicitWidth(d->paintedWidth);
+        }
+    } else {
+        d->paintedWidth = width();
+        d->paintedHeight = height();
+    }
+    emit paintedGeometryChanged();
+}
+
+void QDeclarativeImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+    QDeclarativeImageBase::geometryChanged(newGeometry, oldGeometry);
+    updatePaintedGeometry();
+}
+
+/*!
+    \qmlproperty url Image::source
+
+    Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt.
+
+    The URL may be absolute, or relative to the URL of the component.
+*/
+
+/*!
+    \qmlproperty bool Image::asynchronous
+
+    Specifies that images on the local filesystem should be loaded
+    asynchronously in a separate thread.  The default value is
+    false, causing the user interface thread to block while the
+    image is loaded.  Setting \a asynchronous to true is useful where
+    maintaining a responsive user interface is more desireable
+    than having images immediately visible.
+
+    Note that this property is only valid for images read from the
+    local filesystem.  Images loaded via a network resource (e.g. HTTP)
+    are always loaded asynchonously.
+*/
+
+void QDeclarativeImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
+{
+    Q_D(QDeclarativeImage);
+    if (d->pix.isNull())
+        return;
+
+    bool oldAA = p->testRenderHint(QPainter::Antialiasing);
+    bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform);
+    if (d->smooth)
+        p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
+
+    if (width() != d->pix.width() || height() != d->pix.height()) {
+        if (d->fillMode >= Tile) {
+            if (d->fillMode == Tile) {
+                p->drawTiledPixmap(QRectF(0,0,width(),height()), d->pix);
+            } else {
+                qreal widthScale = width() / qreal(d->pix.width());
+                qreal heightScale = height() / qreal(d->pix.height());
+
+                QTransform scale;
+                if (d->fillMode == TileVertically) {
+                    scale.scale(widthScale, 1.0);
+                    QTransform old = p->transform();
+                    p->setWorldTransform(scale * old);
+                    p->drawTiledPixmap(QRectF(0,0,d->pix.width(),height()), d->pix);
+                    p->setWorldTransform(old);
+                } else {
+                    scale.scale(1.0, heightScale);
+                    QTransform old = p->transform();
+                    p->setWorldTransform(scale * old);
+                    p->drawTiledPixmap(QRectF(0,0,width(),d->pix.height()), d->pix);
+                    p->setWorldTransform(old);
+                }
+            }
+        } else {
+            qreal widthScale = width() / qreal(d->pix.width());
+            qreal heightScale = height() / qreal(d->pix.height());
+
+            QTransform scale;
+
+            if (d->fillMode == PreserveAspectFit) {
+                if (widthScale <= heightScale) {
+                    heightScale = widthScale;
+                    scale.translate(0, (height() - heightScale * d->pix.height()) / 2);
+                } else if(heightScale < widthScale) {
+                    widthScale = heightScale;
+                    scale.translate((width() - widthScale * d->pix.width()) / 2, 0);
+                }
+            } else if (d->fillMode == PreserveAspectCrop) {
+                if (widthScale < heightScale) {
+                    widthScale = heightScale;
+                    scale.translate((width() - widthScale * d->pix.width()) / 2, 0);
+                } else if(heightScale < widthScale) {
+                    heightScale = widthScale;
+                    scale.translate(0, (height() - heightScale * d->pix.height()) / 2);
+                }
+            }
+            if (clip()) {
+                p->save();
+                p->setClipRect(boundingRect(), Qt::IntersectClip);
+            }
+            scale.scale(widthScale, heightScale);
+            QTransform old = p->transform();
+            p->setWorldTransform(scale * old);
+            p->drawPixmap(0, 0, d->pix);
+            p->setWorldTransform(old);
+            if (clip()) {
+                p->restore();
+            }
+        }
+    } else {
+        p->drawPixmap(0, 0, d->pix);
+    }
+
+    if (d->smooth) {
+        p->setRenderHint(QPainter::Antialiasing, oldAA);
+        p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
+    }
+}
+
+void QDeclarativeImage::pixmapChange()
+{
+    updatePaintedGeometry();
+    emit pixmapChanged();
+}
+
+QT_END_NAMESPACE