diff -r b72c6db6890b -r 5dc02b23752f src/declarative/graphicsitems/qdeclarativeimage.cpp --- /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 +#include + +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