/**************************************************************************** | |
** | |
** Copyright (C) 2011 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$ | |
** GNU Lesser General Public License Usage | |
** 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. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $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 | |
\ingroup qml-basic-visual-elements | |
\brief The Image element displays an image in a declarative user interface | |
\inherits Item | |
The Image element is used to display images in a declarative user interface. | |
The source of the image is specified as a URL using the \l source property. | |
Images can be supplied in any of the standard image formats supported by Qt, | |
including bitmap formats such as PNG and JPEG, and vector graphics formats | |
such as SVG. If you need to display animated images, use the \l AnimatedImage | |
element. | |
If the \l{Item::width}{width} and \l{Item::height}{height} properties are not | |
specified, the Image element automatically uses the size of the loaded image. | |
By default, specifying the width and height of the element causes the image | |
to be scaled to that size. This behavior can be changed by setting the | |
\l fillMode property, allowing the image to be stretched and tiled instead. | |
\section1 Example Usage | |
The following example shows the simplest usage of the Image element. | |
\snippet doc/src/snippets/declarative/image.qml document | |
\beginfloatleft | |
\image declarative-qtlogo.png | |
\endfloat | |
\clearfloat | |
\section1 Performance | |
By default, locally available images are loaded immediately, and the user interface | |
is blocked until loading is complete. If a large image is to be loaded, it may be | |
preferable to load the image in a low priority thread, by enabling the \l asynchronous | |
property. | |
If the image is obtained from a network rather than a local resource, it is | |
automatically loaded asynchronously, and the \l progress and \l status properties | |
are updated as appropriate. | |
Images are cached and shared internally, so if several Image elements have the same \l 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. | |
\sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider | |
*/ | |
QDeclarativeImage::QDeclarativeImage(QDeclarativeItem *parent) | |
: QDeclarativeImageBase(*(new QDeclarativeImagePrivate), parent) | |
{ | |
} | |
QDeclarativeImage::QDeclarativeImage(QDeclarativeImagePrivate &dd, QDeclarativeItem *parent) | |
: QDeclarativeImageBase(dd, parent) | |
{ | |
} | |
QDeclarativeImage::~QDeclarativeImage() | |
{ | |
} | |
QPixmap QDeclarativeImage::pixmap() const | |
{ | |
Q_D(const QDeclarativeImage); | |
return d->pix.pixmap(); | |
} | |
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.setPixmap(pixmap); | |
q->pixmapChange(); | |
status = pix.isNull() ? QDeclarativeImageBase::Null : QDeclarativeImageBase::Ready; | |
q->update(); | |
} | |
/*! | |
\qmlproperty enumeration Image::fillMode | |
Set this property to define what happens when the source image has a different size | |
than 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" | |
clip: true | |
} | |
\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 | |
smooth: true | |
source: "qtlogo.png" | |
} | |
\endqml | |
\row | |
\o \image declarative-qtlogo-tilehorizontally.png | |
\o TileHorizontally | |
\qml | |
Image { | |
width: 120; height: 120 | |
fillMode: Image.TileHorizontally | |
smooth: true | |
source: "qtlogo.png" | |
} | |
\endqml | |
\endtable | |
Note that \c clip is \c false by default which means that the element might | |
paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop. | |
\sa {declarative/imageelements/image}{Image example} | |
*/ | |
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(); | |
} | |
/*! | |
\qmlproperty real Image::paintedWidth | |
\qmlproperty real Image::paintedHeight | |
These properties hold the size of the image that is actually painted. | |
In most cases it is the same as \c width and \c height, but when using a | |
\c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop | |
\c paintedWidth or \c paintedHeight can be smaller or larger than | |
\c width and \c height of the Image element. | |
*/ | |
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 | |
Use this status to provide an update or respond to the status change in some way. | |
For example, you could: | |
\list | |
\o Trigger a state change: | |
\qml | |
State { name: 'loaded'; when: image.status == Image.Ready } | |
\endqml | |
\o Implement an \c onStatusChanged signal handler: | |
\qml | |
Image { | |
id: image | |
onStatusChanged: if (image.status == Image.Ready) console.log('Loaded') | |
} | |
\endqml | |
\o Bind to the status value: | |
\qml | |
Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' } | |
\endqml | |
\endlist | |
\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 actual width and height of the loaded image. | |
Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale | |
the painting of the image, this property sets the actual number of pixels | |
stored for the loaded image so that large images do not use more | |
memory than necessary. For example, this ensures the image in memory is no | |
larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and | |
\l {Item::}{height} values: | |
\code | |
Rectangle { | |
width: ... | |
height: ... | |
Image { | |
anchors.fill: parent | |
source: "reallyBigImage.jpg" | |
sourceSize.width: 1024 | |
sourceSize.height: 1024 | |
} | |
} | |
\endcode | |
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. | |
Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image | |
by setting sourceSize to \c undefined. | |
\note \e {Changing this property dynamically causes the image source to be reloaded, | |
potentially even from the network, if it is not in the disk cache.} | |
*/ | |
void QDeclarativeImage::updatePaintedGeometry() | |
{ | |
Q_D(QDeclarativeImage); | |
if (d->fillMode == PreserveAspectFit) { | |
if (!d->pix.width() || !d->pix.height()) { | |
setImplicitWidth(0); | |
setImplicitHeight(0); | |
return; | |
} | |
qreal w = widthValid() ? width() : d->pix.width(); | |
qreal widthScale = w / qreal(d->pix.width()); | |
qreal h = heightValid() ? height() : d->pix.height(); | |
qreal heightScale = h / qreal(d->pix.height()); | |
if (widthScale <= heightScale) { | |
d->paintedWidth = w; | |
d->paintedHeight = widthScale * qreal(d->pix.height()); | |
} else if(heightScale < widthScale) { | |
d->paintedWidth = heightScale * qreal(d->pix.width()); | |
d->paintedHeight = h; | |
} | |
if (widthValid() && !heightValid()) { | |
setImplicitHeight(d->paintedHeight); | |
} else { | |
setImplicitHeight(d->pix.height()); | |
} | |
if (heightValid() && !widthValid()) { | |
setImplicitWidth(d->paintedWidth); | |
} else { | |
setImplicitWidth(d->pix.width()); | |
} | |
} else if (d->fillMode == PreserveAspectCrop) { | |
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) { | |
widthScale = heightScale; | |
} else if(heightScale < widthScale) { | |
heightScale = widthScale; | |
} | |
d->paintedHeight = heightScale * qreal(d->pix.height()); | |
d->paintedWidth = widthScale * qreal(d->pix.width()); | |
} else { | |
d->paintedWidth = width(); | |
d->paintedHeight = height(); | |
} | |
emit paintedGeometryChanged(); | |
} | |
void QDeclarativeImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) | |
{ | |
QDeclarativeImageBase::geometryChanged(newGeometry, oldGeometry); | |
updatePaintedGeometry(); | |
} | |
QRectF QDeclarativeImage::boundingRect() const | |
{ | |
Q_D(const QDeclarativeImage); | |
return QRectF(0, 0, qMax(d->mWidth, d->paintedWidth), qMax(d->mHeight, d->paintedHeight)); | |
} | |
/*! | |
\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. | |
\sa QDeclarativeImageProvider | |
*/ | |
/*! | |
\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 desirable | |
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. | |
*/ | |
/*! | |
\qmlproperty bool Image::cache | |
\since QtQuick 1.1 | |
Specifies whether the image should be cached. The default value is | |
true. Setting \a cache to false is useful when dealing with large images, | |
to make sure that they aren't cached at the expense of small 'ui element' images. | |
*/ | |
/*! | |
\qmlproperty bool Image::mirror | |
\since QtQuick 1.1 | |
This property holds whether the image should be horizontally inverted | |
(effectively displaying a mirrored image). | |
The default value is false. | |
*/ | |
void QDeclarativeImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) | |
{ | |
Q_D(QDeclarativeImage); | |
if (d->pix.pixmap().isNull() ) | |
return; | |
int drawWidth = width(); | |
int drawHeight = height(); | |
bool doClip = false; | |
QTransform transform; | |
qreal widthScale = width() / qreal(d->pix.width()); | |
qreal heightScale = height() / qreal(d->pix.height()); | |
if (width() != d->pix.width() || height() != d->pix.height()) { | |
if (d->fillMode >= Tile) { | |
if (d->fillMode == TileVertically) { | |
transform.scale(widthScale, 1.0); | |
drawWidth = d->pix.width(); | |
} else if (d->fillMode == TileHorizontally) { | |
transform.scale(1.0, heightScale); | |
drawHeight = d->pix.height(); | |
} | |
} else { | |
if (d->fillMode == PreserveAspectFit) { | |
if (widthScale <= heightScale) { | |
heightScale = widthScale; | |
transform.translate(0, (height() - heightScale * d->pix.height()) / 2); | |
} else if(heightScale < widthScale) { | |
widthScale = heightScale; | |
transform.translate((width() - widthScale * d->pix.width()) / 2, 0); | |
} | |
} else if (d->fillMode == PreserveAspectCrop) { | |
if (widthScale < heightScale) { | |
widthScale = heightScale; | |
transform.translate((width() - widthScale * d->pix.width()) / 2, 0); | |
} else if(heightScale < widthScale) { | |
heightScale = widthScale; | |
transform.translate(0, (height() - heightScale * d->pix.height()) / 2); | |
} | |
} | |
transform.scale(widthScale, heightScale); | |
drawWidth = d->pix.width(); | |
drawHeight = d->pix.height(); | |
doClip = clip(); | |
} | |
} | |
QTransform oldTransform; | |
bool oldAA = p->testRenderHint(QPainter::Antialiasing); | |
bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); | |
if (d->smooth) | |
p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); | |
if (doClip) { | |
p->save(); | |
p->setClipRect(QRectF(0, 0, d->mWidth, d->mHeight), Qt::IntersectClip); | |
} | |
if (d->mirror) | |
transform.translate(drawWidth, 0).scale(-1.0, 1.0); | |
if (!transform.isIdentity()) { | |
oldTransform = p->transform(); | |
p->setWorldTransform(transform * oldTransform); | |
} | |
if (d->fillMode >= Tile) | |
p->drawTiledPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix); | |
else | |
p->drawPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix, QRectF(0, 0, drawWidth, drawHeight)); | |
if (d->smooth) { | |
p->setRenderHint(QPainter::Antialiasing, oldAA); | |
p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); | |
} | |
if (doClip) | |
p->restore(); | |
if (!transform.isIdentity()) | |
p->setWorldTransform(oldTransform); | |
} | |
void QDeclarativeImage::pixmapChange() | |
{ | |
Q_D(QDeclarativeImage); | |
// PreserveAspectFit calculates the implicit size differently so we | |
// don't call our superclass pixmapChange(), since that would | |
// result in the implicit size being set incorrectly, then updated | |
// in updatePaintedGeometry() | |
if (d->fillMode != PreserveAspectFit) | |
QDeclarativeImageBase::pixmapChange(); | |
updatePaintedGeometry(); | |
} | |
QT_END_NAMESPACE |