diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-01-31 01:00:06 +0100 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-01-31 01:00:06 +0100 |
commit | c6c3613af3229b818b37482bfe285bbdb2e09e85 (patch) | |
tree | 9ba2466de0f426b5e7c666c053e9a0aa6a32d3b8 /src | |
parent | e898490b4e6d6f85ea826a4e864b602e361fb7e5 (diff) | |
parent | f7647d5adaaed4c651cb2e65c7ce66d7ef6639f1 (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Change-Id: I55a13797b753d2598f6f7cf817bba0d7768e37a3
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/jsruntime/qv4dateobject.cpp | 6 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 190 | ||||
-rw-r--r-- | src/quick/items/qquickanimatedimage.cpp | 2 | ||||
-rw-r--r-- | src/quick/items/qquickimage.cpp | 54 | ||||
-rw-r--r-- | src/quick/items/qquickimage_p.h | 1 | ||||
-rw-r--r-- | src/quick/items/qquickimagebase.cpp | 24 | ||||
-rw-r--r-- | src/quick/items/qquickimagebase_p.h | 5 | ||||
-rw-r--r-- | src/quick/items/qquickimagebase_p_p.h | 1 | ||||
-rw-r--r-- | src/quick/items/qquicktext.cpp | 2 | ||||
-rw-r--r-- | src/quick/util/qquickpixmapcache.cpp | 127 | ||||
-rw-r--r-- | src/quick/util/qquickpixmapcache_p.h | 13 | ||||
-rw-r--r-- | src/quick/util/qquickstyledtext.cpp | 2 |
12 files changed, 290 insertions, 137 deletions
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index c2f48ffeac..bebcbd7e44 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -682,17 +682,17 @@ static inline QString ToTimeString(double t) static inline QString ToLocaleString(double t) { - return ToDateTime(t, Qt::LocalTime).toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, Qt::LocalTime), QLocale::ShortFormat); } static inline QString ToLocaleDateString(double t) { - return ToDateTime(t, Qt::LocalTime).date().toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, Qt::LocalTime).date(), QLocale::ShortFormat); } static inline QString ToLocaleTimeString(double t) { - return ToDateTime(t, Qt::LocalTime).time().toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, Qt::LocalTime).time(), QLocale::ShortFormat); } static double getLocalTZA() diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 02032142ee..d47c2604ae 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -722,70 +722,127 @@ ReturnedValue QtObject::method_tint(const FunctionObject *b, const Value *, cons return scope.engine->fromVariant(QQml_colorProvider()->tint(v1, v2)); } +namespace { +template <typename T> +QString formatDateTimeObjectUsingDateFormat(T formatThis, Qt::DateFormat format) { + switch (format) { + case Qt::TextDate: + case Qt::ISODate: + case Qt::RFC2822Date: + case Qt::ISODateWithMs: + return formatThis.toString(format); + // ### Qt 6: Remove all locale dependent cases + QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + case Qt::SystemLocaleDate: + // case Qt::LocalDate: covered by SystemLocaleDate + return QLocale::system().toString(formatThis); + case Qt::LocaleDate: + case Qt::DefaultLocaleShortDate: + return QLocale().toString(formatThis, QLocale::ShortFormat); + case Qt::SystemLocaleShortDate: + return QLocale::system().toString(formatThis, QLocale::ShortFormat); + case Qt::SystemLocaleLongDate: + return QLocale::system().toString(formatThis, QLocale::LongFormat); + case Qt::DefaultLocaleLongDate: + return QLocale().toString(formatThis, QLocale::LongFormat); + } + QT_WARNING_POP + Q_UNREACHABLE(); + return QString(); +} + +template <typename T> +ReturnedValue formatDateTimeObject(const T &formatThis, const QV4::Scope &scope, const QString &functionName, int argc, const Value *argv) { + + QString formatted; + if (argc >= 2) { + QV4::ScopedString s(scope, argv[1]); + if (s) { + if (argc == 3) + scope.engine->throwError(QLatin1String("%1(): Stay argument, third argument can only be used if second argument is a locale").arg(functionName)); + QString format = s->toQString(); + formatted = formatThis.toString(format); + } else if (argv[1].isNumber()) { + if (argc == 3) + scope.engine->throwError(QLatin1String("%1(): Stay argument, third argument can only be used if second argument is a locale").arg(functionName)); + quint32 intFormat = argv[1].asDouble(); + Qt::DateFormat format = Qt::DateFormat(intFormat); + formatted = formatDateTimeObjectUsingDateFormat(formatThis, format); + } else { + QLocale::FormatType formatOptions = QLocale::ShortFormat; + if (argc == 3) { + if (argv[2].isNumber()) + formatOptions = QLocale::FormatType(quint32(argv[2].asDouble())); + else + scope.engine->throwError(QLatin1String("%1(): Third argument must be a Locale format option").arg(functionName)); + } + auto enginePriv = QQmlEnginePrivate::get(scope.engine->qmlEngine()); + auto localeMetaTypeId = qMetaTypeId<QLocale>(); + QVariant locale = enginePriv->v4engine()->toVariant(argv[1], localeMetaTypeId); + if (!locale.canConvert(localeMetaTypeId)) + scope.engine->throwError(QLatin1String("%1(): Bad second argument (must be either string, number or locale)").arg(functionName)); + formatted = locale.value<QLocale>().toString(formatThis, formatOptions); + } + } else { + formatted = QLocale().toString(formatThis, QLocale::ShortFormat); + } + + return Encode(scope.engine->newString(formatted)); +} + +} + /*! -\qmlmethod string Qt::formatDate(datetime date, variant format) +\qmlmethod string Qt::formatDate(datetime date, variant format, variant localeFormatOption) -Returns a string representation of \a date, optionally formatted according -to \a format. +Returns a string representation of \a date, optionally formatted using \a format. The \a date parameter may be a JavaScript \c Date object, a \l{date}{date} -property, a QDate, or QDateTime value. The \a format parameter may be any of -the possible format values as described for +property, a QDate, or QDateTime value. The \a format and \a localeFormatOption +parameter may be any of the possible format values as described for \l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}. If \a format is not specified, \a date is formatted using -\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. +\l {QLocale::FormatType::ShortFormat}{Locale.ShortFormat} using the +default locale. \sa Locale */ ReturnedValue QtObject::method_formatDate(const FunctionObject *b, const Value *, const Value *argv, int argc) { QV4::Scope scope(b); - if (argc < 1 || argc > 2) - THROW_GENERIC_ERROR("Qt.formatDate(): Invalid arguments"); + if (argc < 1) + THROW_GENERIC_ERROR("Qt.formatDate(): Missing argument"); + if (argc > 3) + THROW_GENERIC_ERROR("Qt.formatDate(): Stray arguments; formatDate takes at most 3 arguments."); - Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; QDate date = scope.engine->toVariant(argv[0], -1).toDateTime().date(); - QString formattedDate; - if (argc == 2) { - QV4::ScopedString s(scope, argv[1]); - if (s) { - QString format = s->toQString(); - formattedDate = date.toString(format); - } else if (argv[1].isNumber()) { - quint32 intFormat = argv[1].asDouble(); - Qt::DateFormat format = Qt::DateFormat(intFormat); - formattedDate = date.toString(format); - } else { - THROW_GENERIC_ERROR("Qt.formatDate(): Invalid date format"); - } - } else { - formattedDate = date.toString(enumFormat); - } - - return Encode(scope.engine->newString(formattedDate)); + return formatDateTimeObject(date, scope, QLatin1String("Qt.formatDate"), argc, argv); } /*! -\qmlmethod string Qt::formatTime(datetime time, variant format) +\qmlmethod string Qt::formatTime(datetime time, variant format, variant localeFormatOption) -Returns a string representation of \a time, optionally formatted according to -\a format. +Returns a string representation of \a time, optionally formatted using +\a format, and, if provided, \a localeFormatOption. The \a time parameter may be a JavaScript \c Date object, a QTime, or QDateTime -value. The \a format parameter may be any of the possible format values as -described for \l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}. +value. The \a format and \a localeFormatOption parameter may be any of the +possible format values as described for +\l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}. If \a format is not specified, \a time is formatted using -\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. +\l {QLocale::FormatType::ShortFormat}{Locale.ShortFormat} using the default locale. \sa Locale */ ReturnedValue QtObject::method_formatTime(const FunctionObject *b, const Value *, const Value *argv, int argc) { QV4::Scope scope(b); - if (argc < 1 || argc > 2) - THROW_GENERIC_ERROR("Qt.formatTime(): Invalid arguments"); + if (argc < 1) + THROW_GENERIC_ERROR("Qt.formatTime(): Missing argument"); + if (argc > 3) + THROW_GENERIC_ERROR("Qt.formatTime(): Stray arguments; formatTime takes at most 3 arguments."); QVariant argVariant = scope.engine->toVariant(argv[0], -1); QTime time; @@ -793,47 +850,34 @@ ReturnedValue QtObject::method_formatTime(const FunctionObject *b, const Value * time = argVariant.toDateTime().time(); else // if (argVariant.type() == QVariant::Time), or invalid. time = argVariant.toTime(); - - Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; - QString formattedTime; - if (argc == 2) { - QV4::ScopedString s(scope, argv[1]); - if (s) { - QString format = s->toQString(); - formattedTime = time.toString(format); - } else if (argv[1].isNumber()) { - quint32 intFormat = argv[1].asDouble(); - Qt::DateFormat format = Qt::DateFormat(intFormat); - formattedTime = time.toString(format); - } else { - THROW_GENERIC_ERROR("Qt.formatTime(): Invalid time format"); - } - } else { - formattedTime = time.toString(enumFormat); - } - - return Encode(scope.engine->newString(formattedTime)); + return formatDateTimeObject(time, scope, QLatin1String("Qt.formatTime"), argc, argv); } /*! -\qmlmethod string Qt::formatDateTime(datetime dateTime, variant format) +\qmlmethod string Qt::formatDateTime(datetime dateTime, variant format, variant localeFormatOption) -Returns a string representation of \a dateTime, optionally formatted according to +Returns a string representation of \a dateTime, optionally formatted using \a format. The \a dateTime parameter may be a JavaScript \c Date object, a \l{date}{date} property, a QDate, QTime, or QDateTime value. If \a format is not provided, \a dateTime is formatted using -\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. Otherwise, -\a format should be either: +\l {QLocale::FormatType::ShortFormat}{Locale.ShortFormat} using the +default locale. Otherwise, \a format should be either: \list \li One of the Qt::DateFormat enumeration values, such as - \c Qt.DefaultLocaleShortDate or \c Qt.ISODate + \c Qt.RFC2822Date or \c Qt.ISODate. \li A string that specifies the format of the returned string, as detailed below. +\li A \c locale object. \endlist +If \a format specifies a locale object, \dateTime is formatted +with \li{QLocale::toString}. In this case, localeFormatType can hold a value +of type \l {QLocale::FormatType} to further tune the formatting. If none is +provided, \l {QLocale::FormatType::ShortFormat}{Locale.ShortFormat} is used. + If \a format specifies a format string, it should use the following expressions to specify the date: @@ -910,29 +954,13 @@ with the \a format values below to produce the following results: ReturnedValue QtObject::method_formatDateTime(const FunctionObject *b, const Value *, const Value *argv, int argc) { QV4::Scope scope(b); - if (argc < 1 || argc > 2) - THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid arguments"); + if (argc < 1) + THROW_GENERIC_ERROR("Qt.formatDateTime(): Missing argument"); + if (argc > 3) + THROW_GENERIC_ERROR("Qt.formatDateTime(): Stray arguments; formatDate takes at most 3 arguments."); - Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; QDateTime dt = scope.engine->toVariant(argv[0], -1).toDateTime(); - QString formattedDt; - if (argc == 2) { - QV4::ScopedString s(scope, argv[1]); - if (s) { - QString format = s->toQString(); - formattedDt = dt.toString(format); - } else if (argv[1].isNumber()) { - quint32 intFormat = argv[1].asDouble(); - Qt::DateFormat format = Qt::DateFormat(intFormat); - formattedDt = dt.toString(format); - } else { - THROW_GENERIC_ERROR("Qt.formatDateTime(): Invalid datetime format"); - } - } else { - formattedDt = dt.toString(enumFormat); - } - - return Encode(scope.engine->newString(formattedDt)); + return formatDateTimeObject(dt, scope, QLatin1String("Qt.formatDateTime"), argc, argv); } /*! diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp index 18d492cd68..192ed289e2 100644 --- a/src/quick/items/qquickanimatedimage.cpp +++ b/src/quick/items/qquickanimatedimage.cpp @@ -67,7 +67,7 @@ QQuickPixmap* QQuickAnimatedImagePrivate::infoForCurrentFrame(QQmlEngine *engine .arg(current)); } if (!requestedUrl.isEmpty()) { - if (QQuickPixmap::isCached(requestedUrl, QSize(), 0, QQuickImageProviderOptions())) + if (QQuickPixmap::isCached(requestedUrl, QRect(), QSize(), 0, QQuickImageProviderOptions())) pixmap = new QQuickPixmap(engine, requestedUrl); else pixmap = new QQuickPixmap(requestedUrl, movie->currentImage()); diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index fccacdcce8..1882ec8997 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -427,10 +427,10 @@ qreal QQuickImage::paintedHeight() const /*! \qmlproperty QSize QtQuick::Image::sourceSize - This property holds the actual width and height of the loaded image. + This property holds the scaled width and height of the full-frame 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 + the painting of the image, this property sets the maximum 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 @@ -455,7 +455,7 @@ qreal QQuickImage::paintedHeight() const other dimension is set in proportion to preserve the source image's aspect ratio. (The \l fillMode is independent of this.) - If both the sourceSize.width and sourceSize.height are set the image will be scaled + If both the sourceSize.width and sourceSize.height are set, the image will be scaled down to fit within the specified size (unless PreserveAspectCrop or PreserveAspectFit are used, then it will be scaled to match the optimal size for cropping/fitting), maintaining the image's aspect ratio. The actual @@ -470,6 +470,9 @@ qreal QQuickImage::paintedHeight() const be no greater than this property specifies. For some formats (currently only JPEG), the whole image will never actually be loaded into memory. + If the \l sourceClipRect property is also set, \c sourceSize determines the scale, + but it will be clipped to the size of the clip rectangle. + sourceSize can be cleared to the natural size of the image by setting sourceSize to \c undefined. @@ -478,6 +481,51 @@ qreal QQuickImage::paintedHeight() const */ /*! + \qmlproperty rect QtQuick::Image::sourceClipRect + \since 5.15 + + This property, if set, holds the rectangular region of the source image to + be loaded. + + The \c sourceClipRect works together with the \l sourceSize property to + conserve system resources when only a portion of an image needs to be + loaded. + + \code + Rectangle { + width: ... + height: ... + + Image { + anchors.fill: parent + source: "reallyBigImage.svg" + sourceSize.width: 1024 + sourceSize.height: 1024 + sourceClipRect: Qt.rect(100, 100, 512, 512) + } + } + \endcode + + In the above example, we conceptually scale the SVG graphic to 1024x1024 + first, and then cut out a region of interest that is 512x512 pixels from a + location 100 pixels from the top and left edges. Thus \c sourceSize + determines the scale, but the actual output image is 512x512 pixels. + + Some image formats are able to conserve CPU time by rendering only the + specified region. Others will need to load the entire image first and then + clip it to the specified region. + + This property can be cleared to reload the entire image by setting + \c sourceClipRect 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.} + + \note Sub-pixel clipping is not supported: the given rectangle will be + passed to \l QImageReader::setScaledClipRect(). +*/ + +/*! \qmlproperty url QtQuick::Image::source Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt. diff --git a/src/quick/items/qquickimage_p.h b/src/quick/items/qquickimage_p.h index f7e652cdcc..e22a94c5af 100644 --- a/src/quick/items/qquickimage_p.h +++ b/src/quick/items/qquickimage_p.h @@ -68,6 +68,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImage : public QQuickImageBase Q_PROPERTY(VAlignment verticalAlignment READ verticalAlignment WRITE setVerticalAlignment NOTIFY verticalAlignmentChanged) Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged REVISION 3) Q_PROPERTY(bool autoTransform READ autoTransform WRITE setAutoTransform NOTIFY autoTransformChanged REVISION 5) + Q_PROPERTY(QRectF sourceClipRect READ sourceClipRect WRITE setSourceClipRect RESET resetSourceClipRect NOTIFY sourceClipRectChanged REVISION 15) QML_NAMED_ELEMENT(Image) public: diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 55b29d7a70..2118f67387 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -167,6 +167,29 @@ void QQuickImageBase::resetSourceSize() setSourceSize(QSize()); } +QRectF QQuickImageBase::sourceClipRect() const +{ + Q_D(const QQuickImageBase); + return d->sourceClipRect; +} + +void QQuickImageBase::setSourceClipRect(const QRectF &r) +{ + Q_D(QQuickImageBase); + if (d->sourceClipRect == r) + return; + + d->sourceClipRect = r; + emit sourceClipRectChanged(); + if (isComponentComplete()) + load(); +} + +void QQuickImageBase::resetSourceClipRect() +{ + setSourceClipRect(QRect()); +} + bool QQuickImageBase::cache() const { Q_D(const QQuickImageBase); @@ -295,6 +318,7 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions) d->pix.load(qmlEngine(this), loadUrl, + d->sourceClipRect.toRect(), (loadOptions & HandleDPR) ? d->sourcesize * d->devicePixelRatio : QSize(), options, (loadOptions & UseProviderOptions) ? d->providerOptions : QQuickImageProviderOptions(), diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h index 9308230bfa..238b31b2e5 100644 --- a/src/quick/items/qquickimagebase_p.h +++ b/src/quick/items/qquickimagebase_p.h @@ -107,6 +107,10 @@ public: QSize sourceSize() const; void resetSourceSize(); + QRectF sourceClipRect() const; + void setSourceClipRect(const QRectF &r); + void resetSourceClipRect(); + virtual void setMirror(bool mirror); bool mirror() const; @@ -134,6 +138,7 @@ Q_SIGNALS: void mirrorChanged(); Q_REVISION(14) void currentFrameChanged(); Q_REVISION(14) void frameCountChanged(); + Q_REVISION(15) void sourceClipRectChanged(); protected: void loadEmptyUrl(); diff --git a/src/quick/items/qquickimagebase_p_p.h b/src/quick/items/qquickimagebase_p_p.h index 88e18ba32e..00a9295ef0 100644 --- a/src/quick/items/qquickimagebase_p_p.h +++ b/src/quick/items/qquickimagebase_p_p.h @@ -86,6 +86,7 @@ public: QSize sourcesize; QSize oldSourceSize; qreal devicePixelRatio; + QRectF sourceClipRect; QQuickImageProviderOptions providerOptions; int currentFrame; int frameCount; diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 2a6337cdb3..f89e7d970f 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1212,7 +1212,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal if (!image->pix) { QUrl url = q->baseUrl().resolved(image->url); - image->pix = new QQuickPixmap(qmlEngine(q), url, image->size); + image->pix = new QQuickPixmap(qmlEngine(q), url, QRect(), image->size); if (image->pix->isLoading()) { image->pix->connectFinished(q, SLOT(imageDownloadFinished())); if (!extra.isAllocated() || !extra->nbActiveDownloads) diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index d47719389b..bdd1e2c514 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -86,6 +86,8 @@ QT_BEGIN_NAMESPACE const QLatin1String QQuickPixmap::itemGrabberScheme = QLatin1String("itemgrabber"); +Q_LOGGING_CATEGORY(lcImg, "qt.quick.image") + #ifndef QT_NO_DEBUG static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); #endif @@ -137,6 +139,7 @@ public: QQuickPixmapData *data; QQmlEngine *engineForReader; // always access reader inside readerMutex + QRect requestRegion; QSize requestSize; QUrl url; @@ -240,9 +243,10 @@ public: class QQuickPixmapData { public: - QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &s, const QQuickImageProviderOptions &po, const QString &e) + QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QRect &r, const QSize &rs, + const QQuickImageProviderOptions &po, const QString &e) : refCount(1), frameCount(1), frame(0), inCache(false), pixmapStatus(QQuickPixmap::Error), - url(u), errorString(e), requestSize(s), + url(u), errorString(e), requestRegion(r), requestSize(rs), providerOptions(po), appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform), textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr), prevUnreferencedPtr(nullptr), nextUnreferenced(nullptr) @@ -250,9 +254,10 @@ public: declarativePixmaps.insert(pixmap); } - QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1) + QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QRect &r, const QSize &s, const QQuickImageProviderOptions &po, + QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1) : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Loading), - url(u), requestSize(r), + url(u), requestRegion(r), requestSize(s), providerOptions(po), appliedTransform(aTransform), textureFactory(nullptr), reply(nullptr), prevUnreferenced(nullptr), prevUnreferencedPtr(nullptr), nextUnreferenced(nullptr) @@ -261,9 +266,10 @@ public: } QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, QQuickTextureFactory *texture, - const QSize &s, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1) + const QSize &s, const QRect &r, const QSize &rs, const QQuickImageProviderOptions &po, + QQuickImageProviderOptions::AutoTransform aTransform, int frame=0, int frameCount=1) : refCount(1), frameCount(frameCount), frame(frame), inCache(false), pixmapStatus(QQuickPixmap::Ready), - url(u), implicitSize(s), requestSize(r), + url(u), implicitSize(s), requestRegion(r), requestSize(rs), providerOptions(po), appliedTransform(aTransform), textureFactory(texture), reply(nullptr), prevUnreferenced(nullptr), prevUnreferencedPtr(nullptr), nextUnreferenced(nullptr) @@ -308,6 +314,7 @@ public: QUrl url; QString errorString; QSize implicitSize; + QRect requestRegion; QSize requestSize; QQuickImageProviderOptions providerOptions; QQuickImageProviderOptions::AutoTransform appliedTransform; @@ -399,7 +406,7 @@ static void maybeRemoveAlpha(QImage *image) } static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int *frameCount, - const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, + const QRect &requestRegion, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr, int frame = 0) { QImageReader imgio(dev); @@ -417,6 +424,10 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions); if (scSize.isValid()) imgio.setScaledSize(scSize); + if (!requestRegion.isNull()) + imgio.setScaledClipRect(requestRegion); + qCDebug(lcImg) << url << "frame" << frame << "of" << imgio.imageCount() + << "requestRegion" << requestRegion << "QImageReader size" << imgio.size() << "-> scSize" << scSize; if (impsize) *impsize = imgio.size(); @@ -568,7 +579,8 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) buff.open(QIODevice::ReadOnly); int frameCount; int const frame = job->data ? job->data->frame : 0; - if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount, job->requestSize, job->providerOptions, nullptr, frame)) + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, &frameCount, + job->requestRegion, job->requestSize, job->providerOptions, nullptr, frame)) error = QQuickPixmapReply::Decoding; else if (job->data) job->data->frameCount = frameCount; @@ -884,7 +896,8 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u } else { int frameCount; int const frame = runningJob->data ? runningJob->data->frame : 0; - if ( !readImage(url, &f, &image, &errorStr, &readSize, &frameCount, runningJob->requestSize, runningJob->providerOptions, nullptr, frame)) { + if (!readImage(url, &f, &image, &errorStr, &readSize, &frameCount, runningJob->requestRegion, runningJob->requestSize, + runningJob->providerOptions, nullptr, frame)) { errorCode = QQuickPixmapReply::Loading; if (f.fileName() != localFile) errorStr += QString::fromLatin1(" (%1)").arg(f.fileName()); @@ -997,6 +1010,7 @@ class QQuickPixmapKey { public: const QUrl *url; + const QRect *region; const QSize *size; int frame; QQuickImageProviderOptions options; @@ -1004,12 +1018,17 @@ public: inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs) { - return *lhs.size == *rhs.size && *lhs.url == *rhs.url && lhs.options == rhs.options && lhs.frame == rhs.frame; + return *lhs.region == *rhs.region && *lhs.size == *rhs.size && *lhs.url == *rhs.url && + lhs.options == rhs.options && lhs.frame == rhs.frame; } inline uint qHash(const QQuickPixmapKey &key) { - return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.frame*23) ^ (key.options.autoTransform() * 0x5c5c5c5c); + return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.frame*23) ^ + (key.region->x()*29) ^ (key.region->y()*31) ^ (key.options.autoTransform() * 0x5c5c5c5c); + // key.region.width() and height() are not included, because the hash function should be simple, + // and they are more likely to be held constant for some batches of images + // (e.g. tiles, or repeatedly cropping to the same viewport at different positions). } class QQuickPixmapStore : public QObject @@ -1176,7 +1195,8 @@ void QQuickPixmap::purgeCache() } QQuickPixmapReply::QQuickPixmapReply(QQuickPixmapData *d) -: data(d), engineForReader(nullptr), requestSize(d->requestSize), url(d->url), loading(false), providerOptions(d->providerOptions), redirectCount(0) + : data(d), engineForReader(nullptr), requestRegion(d->requestRegion), requestSize(d->requestSize), + url(d->url), loading(false), providerOptions(d->providerOptions), redirectCount(0) { if (finishedIndex == -1) { finishedIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex(); @@ -1271,7 +1291,7 @@ void QQuickPixmapData::release() void QQuickPixmapData::addToCache() { if (!inCache) { - QQuickPixmapKey key = { &url, &requestSize, frame, providerOptions }; + QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions }; pixmapStore()->m_cache.insert(key, this); inCache = true; PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>( @@ -1282,7 +1302,7 @@ void QQuickPixmapData::addToCache() void QQuickPixmapData::removeFromCache() { if (inCache) { - QQuickPixmapKey key = { &url, &requestSize, frame, providerOptions }; + QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions }; pixmapStore()->m_cache.remove(key); inCache = false; PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>( @@ -1290,7 +1310,9 @@ void QQuickPixmapData::removeFromCache() } } -static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, int frame, bool *ok) +static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url, + const QRect &requestRegion, const QSize &requestSize, + const QQuickImageProviderOptions &providerOptions, int frame, bool *ok) { if (url.scheme() == QLatin1String("image")) { QSize readSize; @@ -1305,7 +1327,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q switch (imageType) { case QQuickImageProvider::Invalid: - return new QQuickPixmapData(declarativePixmap, url, requestSize, providerOptions, + return new QQuickPixmapData(declarativePixmap, url, requestRegion, requestSize, providerOptions, QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString())); case QQuickImageProvider::Texture: { @@ -1313,7 +1335,8 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q : provider->requestTexture(imageId(url), &readSize, requestSize); if (texture) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame); + return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestRegion, requestSize, + providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame); } break; } @@ -1324,7 +1347,9 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q : provider->requestImage(imageId(url), &readSize, requestSize); if (!image.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), + readSize, requestRegion, requestSize, providerOptions, + QQuickImageProviderOptions::UsePluginDefaultTransform, frame); } break; } @@ -1334,7 +1359,9 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q : provider->requestPixmap(imageId(url), &readSize, requestSize); if (!pixmap.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), + readSize, requestRegion, requestSize, providerOptions, + QQuickImageProviderOptions::UsePluginDefaultTransform, frame); } break; } @@ -1346,7 +1373,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q } // provider has bad image type, or provider returned null image - return new QQuickPixmapData(declarativePixmap, url, requestSize, providerOptions, + return new QQuickPixmapData(declarativePixmap, url, requestRegion, requestSize, providerOptions, QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString())); } @@ -1364,7 +1391,8 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QQuickTextureFactory *factory = texReader.read(); if (factory) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, factory, factory->textureSize(), requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame); + return new QQuickPixmapData(declarativePixmap, url, factory, factory->textureSize(), requestRegion, requestSize, + providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame); } else { errorString = QQuickPixmap::tr("Error decoding: %1").arg(url.toString()); if (f.fileName() != localFile) @@ -1374,9 +1402,10 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QImage image; QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform(); int frameCount; - if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestSize, providerOptions, &appliedTransform, frame)) { + if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize, providerOptions, &appliedTransform, frame)) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform, frame, frameCount); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize, + providerOptions, appliedTransform, frame, frameCount); } else if (f.fileName() != localFile) { errorString += QString::fromLatin1(" (%1)").arg(f.fileName()); } @@ -1384,12 +1413,13 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q } else { errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); } - return new QQuickPixmapData(declarativePixmap, url, requestSize, providerOptions, errorString); + return new QQuickPixmapData(declarativePixmap, url, requestRegion, requestSize, providerOptions, errorString); } struct QQuickPixmapNull { QUrl url; + QRect region; QSize size; }; Q_GLOBAL_STATIC(QQuickPixmapNull, nullPixmap); @@ -1405,15 +1435,16 @@ QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url) load(engine, url); } -QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QSize &size) +QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QRect ®ion, const QSize &size) : d(nullptr) { - load(engine, url, size); + load(engine, url, region, size); } QQuickPixmap::QQuickPixmap(const QUrl &url, const QImage &image) { - d = new QQuickPixmapData(this, url, new QQuickDefaultTextureFactory(image), image.size(), QSize(), QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform); + d = new QQuickPixmapData(this, url, new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(), + QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform); d->addToCache(); } @@ -1486,6 +1517,14 @@ const QSize &QQuickPixmap::requestSize() const return nullPixmap()->size; } +const QRect &QQuickPixmap::requestRegion() const +{ + if (d) + return d->requestRegion; + else + return nullPixmap()->region; +} + QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform() const { if (d) @@ -1561,25 +1600,26 @@ QRect QQuickPixmap::rect() const void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url) { - load(engine, url, QSize(), QQuickPixmap::Cache); + load(engine, url, QRect(), QSize(), QQuickPixmap::Cache); } void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, QQuickPixmap::Options options) { - load(engine, url, QSize(), options); + load(engine, url, QRect(), QSize(), options); } -void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &size) +void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize) { - load(engine, url, size, QQuickPixmap::Cache); + load(engine, url, requestRegion, requestSize, QQuickPixmap::Cache); } -void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options) +void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize, QQuickPixmap::Options options) { - load(engine, url, requestSize, options, QQuickImageProviderOptions()); + load(engine, url, requestRegion, requestSize, options, QQuickImageProviderOptions()); } -void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame, int frameCount) +void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize, + QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame, int frameCount) { if (d) { d->declarativePixmaps.remove(this); @@ -1587,7 +1627,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques d = nullptr; } - QQuickPixmapKey key = { &url, &requestSize, frame, providerOptions }; + QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, providerOptions }; QQuickPixmapStore *store = pixmapStore(); QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end(); @@ -1596,10 +1636,11 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques // cached version. Unless it's an itemgrabber url, since the cache is used to pass // the result between QQuickItemGrabResult and QQuickImage. if (url.scheme() == itemGrabberScheme) { - QSize dummy; - if (requestSize != dummy) + QRect dummyRegion; + QSize dummySize; + if (requestSize != dummySize) qWarning() << "Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead."; - const QQuickPixmapKey grabberKey = { &url, &dummy, 0, QQuickImageProviderOptions() }; + const QQuickPixmapKey grabberKey = { &url, &dummyRegion, &dummySize, 0, QQuickImageProviderOptions() }; iter = store->m_cache.find(grabberKey); } else if (options & QQuickPixmap::Cache) iter = store->m_cache.find(key); @@ -1621,7 +1662,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques if (!(options & QQuickPixmap::Asynchronous)) { bool ok = false; PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url)); - d = createPixmapDataSync(this, engine, url, requestSize, providerOptions, frame, &ok); + d = createPixmapDataSync(this, engine, url, requestRegion, requestSize, providerOptions, frame, &ok); if (ok) { PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height()))); if (options & QQuickPixmap::Cache) @@ -1638,7 +1679,8 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques return; - d = new QQuickPixmapData(this, url, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount); + d = new QQuickPixmapData(this, url, requestRegion, requestSize, providerOptions, + QQuickImageProviderOptions::UsePluginDefaultTransform, frame, frameCount); if (options & QQuickPixmap::Cache) d->addToCache(); @@ -1672,9 +1714,10 @@ void QQuickPixmap::clear(QObject *obj) } } -bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize, const int frame, const QQuickImageProviderOptions &options) +bool QQuickPixmap::isCached(const QUrl &url, const QRect &requestRegion, const QSize &requestSize, + const int frame, const QQuickImageProviderOptions &options) { - QQuickPixmapKey key = { &url, &requestSize, frame, options }; + QQuickPixmapKey key = { &url, &requestRegion, &requestSize, frame, options }; QQuickPixmapStore *store = pixmapStore(); return store->m_cache.contains(key); diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h index c4f4d1a101..65d102af71 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -127,7 +127,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPixmap public: QQuickPixmap(); QQuickPixmap(QQmlEngine *, const QUrl &); - QQuickPixmap(QQmlEngine *, const QUrl &, const QSize &); + QQuickPixmap(QQmlEngine *, const QUrl &, const QRect ®ion, const QSize &); QQuickPixmap(const QUrl &, const QImage &image); ~QQuickPixmap(); @@ -148,6 +148,7 @@ public: QString error() const; const QUrl &url() const; const QSize &implicitSize() const; + const QRect &requestRegion() const; const QSize &requestSize() const; QQuickImageProviderOptions::AutoTransform autoTransform() const; int frameCount() const; @@ -163,9 +164,10 @@ public: void load(QQmlEngine *, const QUrl &); void load(QQmlEngine *, const QUrl &, QQuickPixmap::Options options); - void load(QQmlEngine *, const QUrl &, const QSize &); - void load(QQmlEngine *, const QUrl &, const QSize &, QQuickPixmap::Options options); - void load(QQmlEngine *, const QUrl &, const QSize &, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1); + void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize); + void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize, QQuickPixmap::Options options); + void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize, + QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1); void clear(); void clear(QObject *); @@ -176,7 +178,8 @@ public: bool connectDownloadProgress(QObject *, int); static void purgeCache(); - static bool isCached(const QUrl &url, const QSize &requestSize, const int frame, const QQuickImageProviderOptions &options); + static bool isCached(const QUrl &url, const QRect &requestRegion, const QSize &requestSize, + const int frame, const QQuickImageProviderOptions &options); static const QLatin1String itemGrabberScheme; diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp index 08d06c66ab..660852ba83 100644 --- a/src/quick/util/qquickstyledtext.cpp +++ b/src/quick/util/qquickstyledtext.cpp @@ -706,7 +706,7 @@ void QQuickStyledTextPrivate::parseImageAttributes(const QChar *&ch, const QStri // to avoid a relayout later on. QUrl url = baseUrl.resolved(image->url); if (url.isLocalFile()) { - image->pix = new QQuickPixmap(context->engine(), url, image->size); + image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size); if (image->pix && image->pix->isReady()) { image->size = image->pix->implicitSize(); } else { |