From 09a40e13174b9f34007ce9fbd98e06b4e48c1954 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 26 Mar 2010 14:33:04 +1000 Subject: Control of image rendered size (esp. SVG). Add Translate transform. Image::sourceWidth and Image::sourceHeight read/write properties. Task-number: QTBUG-8984 --- examples/declarative/images/content/lemonade.jpg | Bin 0 -> 6645 bytes examples/declarative/images/images.qml | 70 +++++++++ src/declarative/graphicsitems/graphicsitems.pri | 2 + .../graphicsitems/qdeclarativeborderimage.cpp | 23 +-- .../graphicsitems/qdeclarativeimage.cpp | 26 ++++ .../graphicsitems/qdeclarativeimagebase.cpp | 58 ++++++- .../graphicsitems/qdeclarativeimagebase_p.h | 9 ++ .../graphicsitems/qdeclarativeimagebase_p_p.h | 2 + src/declarative/graphicsitems/qdeclarativeitem.cpp | 38 ++++- .../graphicsitems/qdeclarativeitemsmodule.cpp | 2 + .../graphicsitems/qdeclarativetranslate.cpp | 159 +++++++++++++++++++ .../graphicsitems/qdeclarativetranslate_p.h | 92 +++++++++++ src/declarative/util/qdeclarativepixmapcache.cpp | 172 ++++++++++++++------- src/declarative/util/qdeclarativepixmapcache_p.h | 9 +- .../declarative/qdeclarativeimage/data/big.jpeg | Bin 0 -> 1700081 bytes .../declarative/qdeclarativeimage/data/big256.png | Bin 0 -> 3566 bytes .../declarative/qdeclarativeimage/data/heart.png | Bin 0 -> 12577 bytes .../declarative/qdeclarativeimage/data/heart.svg | 55 +++++++ .../qdeclarativeimage/data/heart200.png | Bin 0 -> 8063 bytes .../qdeclarativeimage/tst_qdeclarativeimage.cpp | 65 +++++++- 20 files changed, 696 insertions(+), 86 deletions(-) create mode 100644 examples/declarative/images/content/lemonade.jpg create mode 100644 examples/declarative/images/images.qml create mode 100644 src/declarative/graphicsitems/qdeclarativetranslate.cpp create mode 100644 src/declarative/graphicsitems/qdeclarativetranslate_p.h create mode 100644 tests/auto/declarative/qdeclarativeimage/data/big.jpeg create mode 100644 tests/auto/declarative/qdeclarativeimage/data/big256.png create mode 100644 tests/auto/declarative/qdeclarativeimage/data/heart.png create mode 100644 tests/auto/declarative/qdeclarativeimage/data/heart.svg create mode 100644 tests/auto/declarative/qdeclarativeimage/data/heart200.png diff --git a/examples/declarative/images/content/lemonade.jpg b/examples/declarative/images/content/lemonade.jpg new file mode 100644 index 0000000..db445c9 Binary files /dev/null and b/examples/declarative/images/content/lemonade.jpg differ diff --git a/examples/declarative/images/images.qml b/examples/declarative/images/images.qml new file mode 100644 index 0000000..82848df --- /dev/null +++ b/examples/declarative/images/images.qml @@ -0,0 +1,70 @@ +import Qt 4.6 + +Rectangle { + color: "white" + width: grid.width + 50 + height: grid.height + 50 + + Grid { + x: 25; y: 25 + id: grid + columns: 3 + + Image { + source: "content/lemonade.jpg" + } + + Image { + sourceWidth: 50 + sourceHeight: 50 + source: "content/lemonade.jpg" + } + + Image { + sourceWidth: 50 + sourceHeight: 50 + smooth: true + source: "content/lemonade.jpg" + } + + Image { + scale: 1/3 + source: "content/lemonade.jpg" + } + + Image { + scale: 1/3 + sourceWidth: 50 + sourceHeight: 50 + source: "content/lemonade.jpg" + } + + Image { + scale: 1/3 + sourceWidth: 50 + sourceHeight: 50 + smooth: true + source: "content/lemonade.jpg" + } + + Image { + width: 50; height: 50; transform: Translate { x: 50 } + source: "content/lemonade.jpg" + } + + Image { + width: 50; height: 50; transform: Translate { x: 50 } + sourceWidth: 50 + sourceHeight: 50 + source: "content/lemonade.jpg" + } + + Image { + width: 50; height: 50; transform: Translate { x: 50 } + sourceWidth: 50 + sourceHeight: 50 + smooth: true + source: "content/lemonade.jpg" + } + } +} diff --git a/src/declarative/graphicsitems/graphicsitems.pri b/src/declarative/graphicsitems/graphicsitems.pri index d30651b..af76a67 100644 --- a/src/declarative/graphicsitems/graphicsitems.pri +++ b/src/declarative/graphicsitems/graphicsitems.pri @@ -39,6 +39,7 @@ HEADERS += \ $$PWD/qdeclarativerepeater_p.h \ $$PWD/qdeclarativerepeater_p_p.h \ $$PWD/qdeclarativescalegrid_p_p.h \ + $$PWD/qdeclarativetranslate_p.h \ $$PWD/qdeclarativetextinput_p.h \ $$PWD/qdeclarativetextinput_p_p.h \ $$PWD/qdeclarativetextedit_p.h \ @@ -75,6 +76,7 @@ SOURCES += \ $$PWD/qdeclarativerectangle.cpp \ $$PWD/qdeclarativerepeater.cpp \ $$PWD/qdeclarativescalegrid.cpp \ + $$PWD/qdeclarativetranslate.cpp \ $$PWD/qdeclarativetextinput.cpp \ $$PWD/qdeclarativetext.cpp \ $$PWD/qdeclarativetextedit.cpp \ diff --git a/src/declarative/graphicsitems/qdeclarativeborderimage.cpp b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp index f92c207..96f95f2 100644 --- a/src/declarative/graphicsitems/qdeclarativeborderimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp @@ -217,7 +217,8 @@ void QDeclarativeBorderImage::load() thisSciRequestFinished, Qt::DirectConnection); } } else { - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->pix, d->async); + QSize impsize; + QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->pix, &impsize, d->async); if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) { QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->url); d->pendingPixmapCache = true; @@ -226,8 +227,8 @@ void QDeclarativeBorderImage::load() this, SLOT(requestProgress(qint64,qint64))); } else { //### should be unified with requestFinished - setImplicitWidth(d->pix.width()); - setImplicitHeight(d->pix.height()); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); if (d->pix.isNull()) d->status = Error; @@ -336,7 +337,8 @@ void QDeclarativeBorderImage::setGridScaledImage(const QDeclarativeGridScaledIma d->verticalTileMode = sci.verticalTileRule(); d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl())); - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->sciurl, &d->pix, d->async); + QSize impsize; + QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->sciurl, &d->pix, &impsize, d->async); if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) { QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->sciurl); d->sciPendingPixmapCache = true; @@ -362,8 +364,8 @@ void QDeclarativeBorderImage::setGridScaledImage(const QDeclarativeGridScaledIma thisRequestProgress, Qt::DirectConnection); } else { //### should be unified with requestFinished - setImplicitWidth(d->pix.width()); - setImplicitHeight(d->pix.height()); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); if (d->pix.isNull()) d->status = Error; @@ -381,16 +383,17 @@ void QDeclarativeBorderImage::requestFinished() { Q_D(QDeclarativeBorderImage); + QSize impsize; if (d->url.path().endsWith(QLatin1String(".sci"))) { d->sciPendingPixmapCache = false; - QDeclarativePixmapCache::get(d->sciurl, &d->pix, d->async); + QDeclarativePixmapCache::get(d->sciurl, &d->pix, &impsize, d->async); } else { d->pendingPixmapCache = false; - if (QDeclarativePixmapCache::get(d->url, &d->pix, d->async) != QDeclarativePixmapReply::Ready) + if (QDeclarativePixmapCache::get(d->url, &d->pix, &impsize, d->async) != QDeclarativePixmapReply::Ready) d->status = Error; } - setImplicitWidth(d->pix.width()); - setImplicitHeight(d->pix.height()); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); if (d->status == Loading) d->status = Ready; diff --git a/src/declarative/graphicsitems/qdeclarativeimage.cpp b/src/declarative/graphicsitems/qdeclarativeimage.cpp index 425976f..8b93063 100644 --- a/src/declarative/graphicsitems/qdeclarativeimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeimage.cpp @@ -266,6 +266,32 @@ qreal QDeclarativeImage::paintedHeight() const filtering at the beginning of the animation and reenable it at the conclusion. */ +/*! + \qmlproperty int Image::sourceWidth + \qmlproperty int Image::sourceHeight + + These properties are the size of the loaded image, in pixels. + + If you set these properties explicitly, you can to control the storage + used by a loaded image. The image will be scaled down if its intrinsic size + is greater than these values. + + Unlike setting the width and height properties, which merely scale the painting + of the image, these properties affect the number of pixels stored. + + \e{Changing these properties dynamically will lead to the image source being reloaded, + potentially even from the network if it is not in the disk cache.} + + If the source is an instrinsically scalable image (eg. SVG), these properties + determine the size of the loaded image regardless of intrinsic size. You should + avoid changing these properties 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 these properties specify. For some formats (currently only JPEG), + the whole image will never actually be loaded into memory. +*/ + void QDeclarativeImage::updatePaintedGeometry() { Q_D(QDeclarativeImage); diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase.cpp b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp index e65c9d1..3b75a85 100644 --- a/src/declarative/graphicsitems/qdeclarativeimagebase.cpp +++ b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp @@ -116,6 +116,41 @@ void QDeclarativeImageBase::setSource(const QUrl &url) load(); } +void QDeclarativeImageBase::setSourceWidth(int w) +{ + Q_D(QDeclarativeImageBase); + if (d->sourcewidth == w) + return; + d->sourcewidth = w; + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +int QDeclarativeImageBase::sourceWidth() const +{ + Q_D(const QDeclarativeImageBase); + return d->sourcewidth <= 0 ? implicitWidth() : d->sourcewidth; +} + +void QDeclarativeImageBase::setSourceHeight(int h) +{ + Q_D(QDeclarativeImageBase); + if (d->sourceheight == h) + return; + d->sourceheight = h; + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +int QDeclarativeImageBase::sourceHeight() const +{ + Q_D(const QDeclarativeImageBase); + return d->sourceheight <= 0 ? implicitHeight() : d->sourceheight; +} + + void QDeclarativeImageBase::load() { Q_D(QDeclarativeImageBase); @@ -134,9 +169,12 @@ void QDeclarativeImageBase::load() update(); } else { d->status = Loading; - QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->pix, d->async); + int reqwidth = d->sourcewidth; + int reqheight = d->sourceheight; + QSize impsize; + QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(d->url, &d->pix, &impsize, d->async, reqwidth, reqheight); if (status != QDeclarativePixmapReply::Ready && status != QDeclarativePixmapReply::Error) { - QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->url); + QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(this), d->url, reqwidth, reqheight); d->pendingPixmapCache = true; static int replyDownloadProgress = -1; @@ -161,11 +199,14 @@ void QDeclarativeImageBase::load() } else { //### should be unified with requestFinished if (status == QDeclarativePixmapReply::Ready) { - setImplicitWidth(d->pix.width()); - setImplicitHeight(d->pix.height()); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); if (d->status == Loading) d->status = Ready; + + if (d->sourcewidth <= 0 || d->sourceheight <= 0) + emit sourceSizeChanged(); } else { d->status = Error; } @@ -186,16 +227,19 @@ void QDeclarativeImageBase::requestFinished() d->pendingPixmapCache = false; - if (QDeclarativePixmapCache::get(d->url, &d->pix, d->async) != QDeclarativePixmapReply::Ready) + QSize impsize; + if (QDeclarativePixmapCache::get(d->url, &d->pix, &impsize, d->async, d->sourcewidth, d->sourceheight) != QDeclarativePixmapReply::Ready) d->status = Error; - setImplicitWidth(d->pix.width()); - setImplicitHeight(d->pix.height()); + setImplicitWidth(impsize.width()); + setImplicitHeight(impsize.height()); if (d->status == Loading) d->status = Ready; d->progress = 1.0; emit statusChanged(d->status); emit progressChanged(1.0); + if (d->sourcewidth <= 0 || d->sourceheight <= 0) + emit sourceSizeChanged(); pixmapChange(); update(); } diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase_p.h b/src/declarative/graphicsitems/qdeclarativeimagebase_p.h index b215193..2653d0a 100644 --- a/src/declarative/graphicsitems/qdeclarativeimagebase_p.h +++ b/src/declarative/graphicsitems/qdeclarativeimagebase_p.h @@ -59,6 +59,9 @@ class Q_DECLARATIVE_EXPORT QDeclarativeImageBase : public QDeclarativeItem Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) + Q_PROPERTY(int sourceWidth READ sourceWidth WRITE setSourceWidth NOTIFY sourceSizeChanged) + Q_PROPERTY(int sourceHeight READ sourceHeight WRITE setSourceHeight NOTIFY sourceSizeChanged) + public: ~QDeclarativeImageBase(); enum Status { Null, Ready, Loading, Error }; @@ -71,8 +74,14 @@ public: bool asynchronous() const; void setAsynchronous(bool); + void setSourceWidth(int); + int sourceWidth() const; + void setSourceHeight(int); + int sourceHeight() const; + Q_SIGNALS: void sourceChanged(const QUrl &); + void sourceSizeChanged(); void statusChanged(Status); void progressChanged(qreal progress); void asynchronousChanged(); diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h b/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h index c4a61f3..cced228 100644 --- a/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeimagebase_p_p.h @@ -68,6 +68,7 @@ public: QDeclarativeImageBasePrivate() : status(QDeclarativeImageBase::Null), progress(0.0), + sourcewidth(0), sourceheight(0), pendingPixmapCache(false), async(false) { @@ -78,6 +79,7 @@ public: QDeclarativeImageBase::Status status; QUrl url; qreal progress; + int sourcewidth, sourceheight; bool pendingPixmapCache : 1; bool async : 1; }; diff --git a/src/declarative/graphicsitems/qdeclarativeitem.cpp b/src/declarative/graphicsitems/qdeclarativeitem.cpp index dd6056e..c331af7 100644 --- a/src/declarative/graphicsitems/qdeclarativeitem.cpp +++ b/src/declarative/graphicsitems/qdeclarativeitem.cpp @@ -83,7 +83,43 @@ QT_BEGIN_NAMESPACE You can assign any number of Transform elements to an Item. Each Transform is applied in order, one at a time, to the Item it's assigned to. - \sa Rotation, Scale + \sa Rotation, Scale, Translate +*/ + +/*! + \qmlclass Translate QGraphicsTranslate + \since 4.7 + \brief The Translate object provides a way to move an Item without changing its x or y. + + The Translate object independent control over position in addition to the Item's x and y properties. + + The following example moves the X axis of the Rectangle, relative to its interior point 25, 25: + \qml + Row { + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Translate { y: 20 } + } + Rectangle { + width: 100; height: 100 + color: "red" + transform: Translate { y: -20 } + } + } + \endqml +*/ + +/*! + \qmlproperty real Translate::x + + The translation along the X axis. +*/ + +/*! + \qmlproperty real Translate::yTranslate + + The translation along the Y axis. */ /*! diff --git a/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp index 07d7f4d..1c458b1d4 100644 --- a/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp +++ b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp @@ -69,6 +69,7 @@ #include "qdeclarativepathview_p.h" #include "qdeclarativerectangle_p.h" #include "qdeclarativerepeater_p.h" +#include "qdeclarativetranslate_p.h" #include "qdeclarativetext_p.h" #include "qdeclarativetextedit_p.h" #include "qdeclarativetextinput_p.h" @@ -123,6 +124,7 @@ void QDeclarativeItemModule::defineModule() qmlRegisterType("Qt",4,6,"Repeater"); qmlRegisterType("Qt",4,6,"Rotation"); qmlRegisterType("Qt",4,6,"Row"); + qmlRegisterType("Qt",4,6,"Translate"); qmlRegisterType("Qt",4,6,"Scale"); qmlRegisterType("Qt",4,6,"Text"); qmlRegisterType("Qt",4,6,"TextEdit"); diff --git a/src/declarative/graphicsitems/qdeclarativetranslate.cpp b/src/declarative/graphicsitems/qdeclarativetranslate.cpp new file mode 100644 index 0000000..57c47f0 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetranslate.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** 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 "qdeclarativetranslate_p.h" +#include +#include +#include + +class QDeclarativeTranslatePrivate : public QGraphicsTransformPrivate +{ +public: + QDeclarativeTranslatePrivate() + : x(0), y(0), z(0) {} + qreal x; + qreal y; + qreal z; +}; + +/*! + Constructs an empty QDeclarativeTranslate object with the given \a parent. +*/ +QDeclarativeTranslate::QDeclarativeTranslate(QObject *parent) + : QGraphicsTransform(*new QDeclarativeTranslatePrivate, parent) +{ +} + +/*! + Destroys the graphics scale. +*/ +QDeclarativeTranslate::~QDeclarativeTranslate() +{ +} + +/*! + \property QDeclarativeTranslate::x + \brief the horizontal translation. + + The translation can be any real number; the default value is 0.0. + + \sa y, z +*/ +qreal QDeclarativeTranslate::x() const +{ + Q_D(const QDeclarativeTranslate); + return d->x; +} +void QDeclarativeTranslate::setX(qreal x) +{ + Q_D(QDeclarativeTranslate); + if (d->x == x) + return; + d->x = x; + update(); + emit positionChanged(); +} + +/*! + \property QDeclarativeTranslate::y + \brief the vertical translation. + + The translation can be any real number; the default value is 0.0. + + \sa x, z +*/ +qreal QDeclarativeTranslate::y() const +{ + Q_D(const QDeclarativeTranslate); + return d->y; +} +void QDeclarativeTranslate::setY(qreal y) +{ + Q_D(QDeclarativeTranslate); + if (d->y == y) + return; + d->y = y; + update(); + emit positionChanged(); +} + +/*! + \property QDeclarativeTranslate::z + \brief the depth translation. + + The translation can be any real number; the default value is 0.0. + + \sa x, y +*/ +qreal QDeclarativeTranslate::z() const +{ + Q_D(const QDeclarativeTranslate); + return d->z; +} +void QDeclarativeTranslate::setZ(qreal z) +{ + Q_D(QDeclarativeTranslate); + if (d->z == z) + return; + d->z = z; + update(); + emit positionChanged(); +} + +/*! + \reimp +*/ +void QDeclarativeTranslate::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QDeclarativeTranslate); + matrix->translate(d->x, d->y, d->z); +} + +/*! + \fn QDeclarativeTranslate::positionChanged() + + QDeclarativeTranslate emits this signal when its position changes. + + \sa QDeclarativeTranslate::x, QDeclarativeTranslate::y + \sa QDeclarativeTranslate::z +*/ + +//#include "moc_qdeclarativetranslate.cpp" diff --git a/src/declarative/graphicsitems/qdeclarativetranslate_p.h b/src/declarative/graphicsitems/qdeclarativetranslate_p.h new file mode 100644 index 0000000..54bfc3d --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetranslate_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSLATE_H +#define QDECLARATIVETRANSLATE_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeTranslatePrivate; + +class Q_GUI_EXPORT QDeclarativeTranslate : public QGraphicsTransform +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY positionChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY positionChanged) + Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY positionChanged) + +public: + QDeclarativeTranslate(QObject *parent = 0); + ~QDeclarativeTranslate(); + + qreal x() const; + void setX(qreal); + + qreal y() const; + void setY(qreal); + + qreal z() const; + void setZ(qreal); + + void applyTo(QMatrix4x4 *matrix) const; + +Q_SIGNALS: + void positionChanged(); + +private: + Q_DECLARE_PRIVATE(QDeclarativeTranslate) + Q_DISABLE_COPY(QDeclarativeTranslate) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeTranslate) + +QT_END_HEADER + +#endif diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index e78fdf1..bbe86e7 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -106,7 +106,7 @@ public: QDeclarativeImageReader(QDeclarativeEngine *eng); ~QDeclarativeImageReader(); - QDeclarativePixmapReply *getImage(const QUrl &url); + QDeclarativePixmapReply *getImage(const QUrl &url, int req_width, int req_height); void cancel(QDeclarativePixmapReply *rep); static QDeclarativeImageReader *instance(QDeclarativeEngine *engine); @@ -140,7 +140,7 @@ public: QCoreApplication::postEvent(this, new QEvent(QEvent::User)); } - QDeclarativePixmapReply *getImage(const QUrl &url); + QDeclarativePixmapReply *getImage(const QUrl &url, int req_width, int req_height); void cancel(QDeclarativePixmapReply *reply); protected: @@ -170,11 +170,50 @@ private: //=========================================================================== +static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int req_width, int req_height) +{ + QImageReader imgio(dev); + + bool force_scale = false; + if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) { + imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053 + force_scale = true; + } + + bool scaled = false; + if (req_width > 0 || req_height > 0) { + QSize s = imgio.size(); + if (req_width && (force_scale || req_width < s.width())) { s.setWidth(req_width); scaled = true; } + if (req_height && (force_scale || req_height < s.height())) { s.setHeight(req_height); scaled = true; } + if (scaled) { imgio.setScaledSize(s); } + } + + if (impsize) + *impsize = imgio.size(); + + if (imgio.read(image)) { + if (impsize && impsize->width() < 0) + *impsize = image->size(); + return true; + } else { + if (errorString) + *errorString = QLatin1String("Error decoding: ") + url.toString() + + QLatin1String(" \"") + imgio.errorString() + QLatin1String("\""); + return false; + } +} + + +//=========================================================================== + int QDeclarativeImageRequestHandler::replyDownloadProgress = -1; int QDeclarativeImageRequestHandler::replyFinished = -1; int QDeclarativeImageRequestHandler::downloadProgress = -1; int QDeclarativeImageRequestHandler::thisNetworkRequestDone = -1; +typedef QHash QDeclarativePixmapSizeHash; +Q_GLOBAL_STATIC(QDeclarativePixmapSizeHash, qmlOriginalSizes); + bool QDeclarativeImageRequestHandler::event(QEvent *event) { if (event->type() == QEvent::User) { @@ -238,10 +277,10 @@ bool QDeclarativeImageRequestHandler::event(QEvent *event) QString errorStr; QFile f(lf); if (f.open(QIODevice::ReadOnly)) { - QImageReader imgio(&f); - if (!imgio.read(&image)) { - errorStr = QLatin1String("Error decoding: ") + url.toString() - + QLatin1String(" \"") + imgio.errorString() + QLatin1String("\""); + QSize read_impsize; + if (readImage(url, &f, &image, &errorStr, &read_impsize, runningJob->forcedWidth(),runningJob->forcedHeight())) { + qmlOriginalSizes()->insert(url, read_impsize); + } else { errorCode = QDeclarativeImageReaderEvent::Loading; } } else { @@ -303,12 +342,11 @@ void QDeclarativeImageRequestHandler::networkRequestDone() error = QDeclarativeImageReaderEvent::Loading; errorString = reply->errorString(); } else { - QImageReader imgio(reply); - if (imgio.read(&image)) { + QSize read_impsize; + if (readImage(reply->url(), reply, &image, &errorString, &read_impsize, job->forcedWidth(), job->forcedHeight())) { + qmlOriginalSizes()->insert(reply->url(), read_impsize); error = QDeclarativeImageReaderEvent::NoError; } else { - errorString = QLatin1String("Error decoding: ") + reply->url().toString() - + QLatin1String(" \"") + imgio.errorString() + QLatin1String("\""); error = QDeclarativeImageReaderEvent::Decoding; } } @@ -352,10 +390,10 @@ QDeclarativeImageReader *QDeclarativeImageReader::instance(QDeclarativeEngine *e return reader; } -QDeclarativePixmapReply *QDeclarativeImageReader::getImage(const QUrl &url) +QDeclarativePixmapReply *QDeclarativeImageReader::getImage(const QUrl &url, int req_width, int req_height) { mutex.lock(); - QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(this, url); + QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(this, url, req_width, req_height); reply->addRef(); reply->setLoading(); jobs.append(reply); @@ -397,40 +435,6 @@ void QDeclarativeImageReader::run() //=========================================================================== -static bool readImage(QIODevice *dev, QPixmap *pixmap, QString &errorString) -{ - QImageReader imgio(dev); - -//#define QT_TEST_SCALED_SIZE -#ifdef QT_TEST_SCALED_SIZE - /* - Some mechanism is needed for loading images at a limited size, especially - for remote images. Loading only thumbnails of remote progressive JPEG - images can be efficient. (Qt jpeg handler does not do so currently) - */ - - QSize limit(60,60); - QSize sz = imgio.size(); - if (sz.width() > limit.width() || sz.height() > limit.height()) { - sz.scale(limit,Qt::KeepAspectRatio); - imgio.setScaledSize(sz); - } -#endif - - QImage img; - if (imgio.read(&img)) { -#ifdef QT_TEST_SCALED_SIZE - if (!sz.isValid()) - img = img.scaled(limit,Qt::KeepAspectRatio); -#endif - *pixmap = QPixmap::fromImage(img); - return true; - } else { - errorString = imgio.errorString(); - return false; - } -} - /*! \internal \class QDeclarativePixmapCache @@ -447,8 +451,10 @@ class QDeclarativePixmapReplyPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QDeclarativePixmapReply) public: - QDeclarativePixmapReplyPrivate(QDeclarativeImageReader *r, const QUrl &u) - : QObjectPrivate(), refCount(1), url(u), status(QDeclarativePixmapReply::Loading), loading(false), reader(r) { + QDeclarativePixmapReplyPrivate(QDeclarativeImageReader *r, const QUrl &u, int req_width, int req_height) + : QObjectPrivate(), refCount(1), url(u), status(QDeclarativePixmapReply::Loading), loading(false), reader(r), + forced_width(req_width), forced_height(req_height) + { } int refCount; @@ -457,11 +463,12 @@ public: QDeclarativePixmapReply::Status status; bool loading; QDeclarativeImageReader *reader; + int forced_width, forced_height; }; -QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativeImageReader *reader, const QUrl &url) - : QObject(*new QDeclarativePixmapReplyPrivate(reader, url), 0) +QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativeImageReader *reader, const QUrl &url, int req_width, int req_height) + : QObject(*new QDeclarativePixmapReplyPrivate(reader, url, req_width, req_height), 0) { } @@ -475,6 +482,28 @@ const QUrl &QDeclarativePixmapReply::url() const return d->url; } +int QDeclarativePixmapReply::forcedWidth() const +{ + Q_D(const QDeclarativePixmapReply); + return d->forced_width; +} + +int QDeclarativePixmapReply::forcedHeight() const +{ + Q_D(const QDeclarativePixmapReply); + return d->forced_height; +} + +QSize QDeclarativePixmapReply::implicitSize() const +{ + Q_D(const QDeclarativePixmapReply); + QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(d->url); + if (iter != qmlOriginalSizes()->end()) + return *iter; + else + return QSize(); +} + bool QDeclarativePixmapReply::event(QEvent *event) { Q_D(QDeclarativePixmapReply); @@ -554,13 +583,25 @@ bool QDeclarativePixmapReply::release(bool defer) If \a async is false the image will be loaded and decoded immediately; otherwise the image will be loaded and decoded in a separate thread. + If \a req_width and \a req_height are non-zero, they are used for + the size of the rendered pixmap rather than the intrinsic size of the image. + Different request sizes add different cache items. + Note that images sourced from the network will always be loaded and decoded asynchonously. */ -QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QPixmap *pixmap, bool async) +QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QPixmap *pixmap, QSize *impsize, bool async, int req_width, int req_height) { QDeclarativePixmapReply::Status status = QDeclarativePixmapReply::Unrequested; QByteArray key = url.toEncoded(QUrl::FormattingOption(0x100)); + + if (req_width > 0 && req_height > 0) { + key += ':'; + key += QByteArray::number(req_width); + key += 'x'; + key += QByteArray::number(req_height); + } + QString strKey = QString::fromLatin1(key.constData(), key.count()); #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML @@ -570,11 +611,13 @@ QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QP status = QDeclarativePixmapReply::Ready; if (!QPixmapCache::find(strKey,pixmap)) { QFile f(lf); + QSize read_impsize; if (f.open(QIODevice::ReadOnly)) { QString errorString; - if (!readImage(&f, pixmap, errorString)) { - errorString = QLatin1String("Error decoding: ") + url.toString() - + QLatin1String(" \"") + errorString + QLatin1String("\""); + QImage image; + if (readImage(url, &f, &image, &errorString, &read_impsize, req_width, req_height)) { + *pixmap = QPixmap::fromImage(image); + } else { qWarning() << errorString; *pixmap = QPixmap(); status = QDeclarativePixmapReply::Error; @@ -584,8 +627,18 @@ QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QP *pixmap = QPixmap(); status = QDeclarativePixmapReply::Error; } - if (status == QDeclarativePixmapReply::Ready) + if (status == QDeclarativePixmapReply::Ready) { QPixmapCache::insert(strKey, *pixmap); + qmlOriginalSizes()->insert(url, read_impsize); + } + if (impsize) + *impsize = read_impsize; + } else { + if (impsize) { + QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(url); + if (iter != qmlOriginalSizes()->end()) + *impsize = *iter; + } } return status; } @@ -608,6 +661,11 @@ QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QP } else if (iter != qmlActivePixmapReplies()->end()) { status = QDeclarativePixmapReply::Loading; } + if (impsize) { + QDeclarativePixmapSizeHash::Iterator iter = qmlOriginalSizes()->find(url); + if (iter != qmlOriginalSizes()->end()) + *impsize = *iter; + } return status; } @@ -621,12 +679,12 @@ QDeclarativePixmapReply::Status QDeclarativePixmapCache::get(const QUrl& url, QP The returned QDeclarativePixmapReply will be deleted when all request() calls are matched by a corresponding get() call. */ -QDeclarativePixmapReply *QDeclarativePixmapCache::request(QDeclarativeEngine *engine, const QUrl &url) +QDeclarativePixmapReply *QDeclarativePixmapCache::request(QDeclarativeEngine *engine, const QUrl &url, int req_width, int req_height) { QDeclarativePixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); if (iter == qmlActivePixmapReplies()->end()) { QDeclarativeImageReader *reader = QDeclarativeImageReader::instance(engine); - QDeclarativePixmapReply *item = reader->getImage(url); + QDeclarativePixmapReply *item = reader->getImage(url, req_width, req_height); iter = qmlActivePixmapReplies()->insert(url, item); } else { (*iter)->addRef(); diff --git a/src/declarative/util/qdeclarativepixmapcache_p.h b/src/declarative/util/qdeclarativepixmapcache_p.h index b8949db..0ccf469 100644 --- a/src/declarative/util/qdeclarativepixmapcache_p.h +++ b/src/declarative/util/qdeclarativepixmapcache_p.h @@ -66,6 +66,9 @@ public: Status status() const; const QUrl &url() const; + int forcedWidth() const; + int forcedHeight() const; + QSize implicitSize() const; Q_SIGNALS: void finished(); @@ -81,7 +84,7 @@ private: void setLoading(); private: - QDeclarativePixmapReply(QDeclarativeImageReader *reader, const QUrl &url); + QDeclarativePixmapReply(QDeclarativeImageReader *reader, const QUrl &url, int req_width, int req_height); Q_DISABLE_COPY(QDeclarativePixmapReply) Q_DECLARE_PRIVATE(QDeclarativePixmapReply) friend class QDeclarativeImageRequestHandler; @@ -92,8 +95,8 @@ private: class Q_DECLARATIVE_EXPORT QDeclarativePixmapCache { public: - static QDeclarativePixmapReply::Status get(const QUrl& url, QPixmap *pixmap, bool async=false); - static QDeclarativePixmapReply *request(QDeclarativeEngine *, const QUrl& url); + static QDeclarativePixmapReply::Status get(const QUrl& url, QPixmap *pixmap, QSize *impsize, bool async=false, int req_width=0, int req_height=0); + static QDeclarativePixmapReply *request(QDeclarativeEngine *, const QUrl& url, int req_width=0, int req_height=0); static void cancel(const QUrl& url, QObject *obj); static int pendingRequests(); }; diff --git a/tests/auto/declarative/qdeclarativeimage/data/big.jpeg b/tests/auto/declarative/qdeclarativeimage/data/big.jpeg new file mode 100644 index 0000000..bed7bd6 Binary files /dev/null and b/tests/auto/declarative/qdeclarativeimage/data/big.jpeg differ diff --git a/tests/auto/declarative/qdeclarativeimage/data/big256.png b/tests/auto/declarative/qdeclarativeimage/data/big256.png new file mode 100644 index 0000000..1dc1596 Binary files /dev/null and b/tests/auto/declarative/qdeclarativeimage/data/big256.png differ diff --git a/tests/auto/declarative/qdeclarativeimage/data/heart.png b/tests/auto/declarative/qdeclarativeimage/data/heart.png new file mode 100644 index 0000000..372b224 Binary files /dev/null and b/tests/auto/declarative/qdeclarativeimage/data/heart.png differ diff --git a/tests/auto/declarative/qdeclarativeimage/data/heart.svg b/tests/auto/declarative/qdeclarativeimage/data/heart.svg new file mode 100644 index 0000000..8c982cd --- /dev/null +++ b/tests/auto/declarative/qdeclarativeimage/data/heart.svg @@ -0,0 +1,55 @@ + + + + + +Heart Left-Highlight +This is a normal valentines day heart. + + +holiday +valentines + +valentine +hash(0x8a091c0) +hash(0x8a0916c) +signs_and_symbols +hash(0x8a091f0) +day + + + + +Jon Phillips + + + + +Jon Phillips + + + + +Jon Phillips + + + +image/svg+xml + + +en + + + + + + + + + + + + + + + diff --git a/tests/auto/declarative/qdeclarativeimage/data/heart200.png b/tests/auto/declarative/qdeclarativeimage/data/heart200.png new file mode 100644 index 0000000..786e75d Binary files /dev/null and b/tests/auto/declarative/qdeclarativeimage/data/heart200.png differ diff --git a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp index ed2095b..c7dcbea 100644 --- a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp +++ b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp @@ -82,6 +82,8 @@ private slots: void resized(); void smooth(); void pixmap(); + void svg(); + void big(); private: QDeclarativeEngine engine; @@ -111,24 +113,29 @@ void tst_qdeclarativeimage::noSource() void tst_qdeclarativeimage::imageSource_data() { QTest::addColumn("source"); + QTest::addColumn("width"); + QTest::addColumn("height"); QTest::addColumn("remote"); QTest::addColumn("async"); QTest::addColumn("error"); - QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/colors.png").toString() << false << false << ""; - QTest::newRow("local async") << QUrl::fromLocalFile(SRCDIR "/data/colors1.png").toString() << false << true << ""; - QTest::newRow("local not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file.png").toString() << false + QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/colors.png").toString() << 120.0 << 120.0 << false << false << ""; + QTest::newRow("local async") << QUrl::fromLocalFile(SRCDIR "/data/colors1.png").toString() << 120.0 << 120.0 << false << true << ""; + QTest::newRow("local not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file.png").toString() << 0.0 << 0.0 << false << false << "Cannot open QUrl( \"" + QUrl::fromLocalFile(SRCDIR "/data/no-such-file.png").toString() + "\" ) "; - QTest::newRow("local async not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file-1.png").toString() << false + QTest::newRow("local async not found") << QUrl::fromLocalFile(SRCDIR "/data/no-such-file-1.png").toString() << 0.0 << 0.0 << false << true << "\"Cannot open: " + QUrl::fromLocalFile(SRCDIR "/data/no-such-file-1.png").toString() + "\" "; - QTest::newRow("remote") << SERVER_ADDR "/colors.png" << true << false << ""; - QTest::newRow("remote not found") << SERVER_ADDR "/no-such-file.png" << true << false + QTest::newRow("remote") << SERVER_ADDR "/colors.png" << 120.0 << 120.0 << true << false << ""; + QTest::newRow("remote svg") << SERVER_ADDR "/heart.svg" << 550.0 << 500.0 << true << false << ""; + QTest::newRow("remote not found") << SERVER_ADDR "/no-such-file.png" << 0.0 << 0.0 << true << false << "\"Error downloading " SERVER_ADDR "/no-such-file.png - server replied: Not found\" "; } void tst_qdeclarativeimage::imageSource() { QFETCH(QString, source); + QFETCH(qreal, width); + QFETCH(qreal, height); QFETCH(bool, remote); QFETCH(bool, async); QFETCH(QString, error); @@ -156,8 +163,8 @@ void tst_qdeclarativeimage::imageSource() if (error.isEmpty()) { TRY_WAIT(obj->status() == QDeclarativeImage::Ready); - QCOMPARE(obj->width(), 120.); - QCOMPARE(obj->height(), 120.); + QCOMPARE(obj->width(), width); + QCOMPARE(obj->height(), height); QCOMPARE(obj->fillMode(), QDeclarativeImage::Stretch); QCOMPARE(obj->progress(), 1.0); } else { @@ -248,6 +255,48 @@ void tst_qdeclarativeimage::pixmap() delete obj; } +void tst_qdeclarativeimage::svg() +{ + QString componentStr = "import Qt 4.6\nImage { source: \"" SRCDIR "/data/heart.svg\"; sourceWidth: 300; sourceHeight: 300 }"; + QDeclarativeComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QDeclarativeImage *obj = qobject_cast(component.create()); + QVERIFY(obj != 0); + QCOMPARE(obj->pixmap().width(), 300); + QCOMPARE(obj->pixmap().height(), 300); + QCOMPARE(obj->width(), 550.0); + QCOMPARE(obj->height(), 500.0); + QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart.png")); + + obj->setSourceWidth(200); + obj->setSourceHeight(200); + + QCOMPARE(obj->pixmap().width(), 200); + QCOMPARE(obj->pixmap().height(), 200); + QCOMPARE(obj->width(), 550.0); + QCOMPARE(obj->height(), 500.0); + QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/heart200.png")); + + delete obj; +} + +void tst_qdeclarativeimage::big() +{ + QString componentStr = "import Qt 4.6\nImage { source: \"" SRCDIR "/data/big.jpeg\"; sourceWidth: 256; sourceHeight: 256 }"; + QDeclarativeComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QDeclarativeImage *obj = qobject_cast(component.create()); + QVERIFY(obj != 0); + QCOMPARE(obj->pixmap().width(), 256); + QCOMPARE(obj->pixmap().height(), 256); + QCOMPARE(obj->width(), 10240.0); + QCOMPARE(obj->height(), 10240.0); + QCOMPARE(obj->pixmap(), QPixmap(SRCDIR "/data/big256.png")); + + delete obj; +} + + QTEST_MAIN(tst_qdeclarativeimage) #include "tst_qdeclarativeimage.moc" -- cgit v0.12