diff options
author | Albert Astals Cid <albert.astals@canonical.com> | 2016-07-15 14:21:37 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2016-11-11 17:20:10 +0000 |
commit | 9c50216c7bbbdb2bb51d4485286bf09e12fb5b62 (patch) | |
tree | 39b9bd552507482b87fceeaedad42e4bf12eca11 /src | |
parent | 6cb21a9296b34d66d7b50d6b38c09294634202e0 (diff) |
Scale images correctly with sourceSize and PreserveAspectCrop/Fit
It also introduces a private QQuickImageProviderWithOptions to allow
passing options to image providers so that they can return more
fine-tuned images. This private class will disappear in Qt6 and the
functionality will be merged into QQuickImageProvider.
Change-Id: I619065d889d21d3a9e1f8e45fdb6076b9657c7ed
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/quick/items/qquickimage.cpp | 13 | ||||
-rw-r--r-- | src/quick/items/qquickimagebase.cpp | 13 | ||||
-rw-r--r-- | src/quick/items/qquickimagebase_p_p.h | 3 | ||||
-rw-r--r-- | src/quick/util/qquickimageprovider.cpp | 165 | ||||
-rw-r--r-- | src/quick/util/qquickimageprovider.h | 13 | ||||
-rw-r--r-- | src/quick/util/qquickpixmapcache.cpp | 172 | ||||
-rw-r--r-- | src/quick/util/qquickpixmapcache_p.h | 68 |
7 files changed, 346 insertions, 101 deletions
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index a53d068597..f71a2fbdbd 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -304,6 +304,15 @@ void QQuickImage::setFillMode(FillMode mode) if (d->fillMode == mode) return; d->fillMode = mode; + if ((mode == PreserveAspectCrop) != d->providerOptions.preserveAspectRatioCrop()) { + d->providerOptions.setPreserveAspectRatioCrop(mode == PreserveAspectCrop); + if (isComponentComplete()) + load(); + } else if ((mode == PreserveAspectFit) != d->providerOptions.preserveAspectRatioFit()) { + d->providerOptions.setPreserveAspectRatioFit(mode == PreserveAspectFit); + if (isComponentComplete()) + load(); + } update(); updatePaintedGeometry(); emit fillModeChanged(); @@ -423,7 +432,9 @@ qreal QQuickImage::paintedHeight() const (The \l fillMode is independent of this.) If both the sourceSize.width and sourceSize.height are set the image will be scaled - down to fit within the specified size, maintaining the image's aspect ratio. The actual + 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 size of the image after scaling is available via \l Item::implicitWidth and \l Item::implicitHeight. If the source is an intrinsically scalable image (eg. SVG), this property diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index a2b99b6395..a6bf6b4e8a 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -246,7 +246,7 @@ void QQuickImageBase::load() resolve2xLocalFile(d->url, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio); } - d->pix.load(qmlEngine(this), loadUrl, d->sourcesize * d->devicePixelRatio, options, d->autoTransform); + d->pix.load(qmlEngine(this), loadUrl, d->sourcesize * d->devicePixelRatio, options, d->providerOptions); if (d->pix.isLoading()) { if (d->progress != 0.0) { @@ -381,17 +381,18 @@ void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixe bool QQuickImageBase::autoTransform() const { Q_D(const QQuickImageBase); - if (d->autoTransform == UsePluginDefault) - return d->pix.autoTransform() == ApplyTransform; - return d->autoTransform == ApplyTransform; + if (d->providerOptions.autoTransform() == QQuickImageProviderOptions::UsePluginDefaultTransform) + return d->pix.autoTransform() == QQuickImageProviderOptions::ApplyTransform; + return d->providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform; } void QQuickImageBase::setAutoTransform(bool transform) { Q_D(QQuickImageBase); - if (d->autoTransform != UsePluginDefault && transform == (d->autoTransform == ApplyTransform)) + if (d->providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform && + transform == (d->providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform)) return; - d->autoTransform = transform ? ApplyTransform : DoNotApplyTransform; + d->providerOptions.setAutoTransform(transform ? QQuickImageProviderOptions::ApplyTransform : QQuickImageProviderOptions::DoNotApplyTransform); emitAutoTransformBaseChanged(); } diff --git a/src/quick/items/qquickimagebase_p_p.h b/src/quick/items/qquickimagebase_p_p.h index 1eb566a3c8..d9b609c7fe 100644 --- a/src/quick/items/qquickimagebase_p_p.h +++ b/src/quick/items/qquickimagebase_p_p.h @@ -68,7 +68,6 @@ public: : status(QQuickImageBase::Null), progress(0.0), devicePixelRatio(1.0), - autoTransform(UsePluginDefault), async(false), cache(true), mirror(false), @@ -83,7 +82,7 @@ public: QSize sourcesize; QSize oldSourceSize; qreal devicePixelRatio; - AutoTransform autoTransform; + QQuickImageProviderOptions providerOptions; bool async : 1; bool cache : 1; bool mirror: 1; diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index 0c245d2b23..c4182d9f93 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -44,13 +44,6 @@ QT_BEGIN_NAMESPACE -class QQuickImageProviderPrivate -{ -public: - QQuickImageProvider::ImageType type; - QQuickImageProvider::Flags flags; -}; - /*! \class QQuickTextureFactory \since 5.0 @@ -349,6 +342,7 @@ QQuickImageProvider::QQuickImageProvider(ImageType type, Flags flags) { d->type = type; d->flags = flags; + d->isProviderWithOptions = false; } /*! @@ -502,26 +496,165 @@ QQuickAsyncImageProvider::~QQuickAsyncImageProvider() implementation of this method is reentrant. */ + +class QQuickImageProviderOptionsPrivate : public QSharedData +{ +public: + QQuickImageProviderOptionsPrivate() + : autoTransform(QQuickImageProviderOptions::UsePluginDefaultTransform) + , preserveAspectRatioCrop(false) + , preserveAspectRatioFit(false) + { + } + + QQuickImageProviderOptions::AutoTransform autoTransform; + bool preserveAspectRatioCrop; + bool preserveAspectRatioFit; +}; + /*! - \fn QImage QQuickImageProvider::requestImage(const QString &id, QSize *size, const QSize& requestedSize, bool requestedAutoTransform); + \class QQuickImageProviderOptions + \since 5.9 + \brief The QQuickImageProviderOptions class provides options for QQuickImageProviderWithOptions image requests. + \inmodule QtQuick - \internal - For future reference. + \sa QQuickImageProviderWithOptions */ /*! - \fn QPixmap QQuickImageProvider::requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, bool requestedAutoTransform); + \enum QQuickImageProviderOptions::AutoTransform - \internal - For future reference. + Whether the image provider should apply transformation metadata on read(). + + \value UsePluginDefaultTransform Image provider should do its default behavior on whether applying transformation metadata on read or not + \value ApplyTransform Image provider should apply transformation metadata on read + \value DoNotApplyTransform Image provider should not apply transformation metadata on read */ +QQuickImageProviderOptions::QQuickImageProviderOptions() + : d(new QQuickImageProviderOptionsPrivate()) +{ +} + +QQuickImageProviderOptions::~QQuickImageProviderOptions() +{ +} + +QQuickImageProviderOptions::QQuickImageProviderOptions(const QQuickImageProviderOptions &other) + : d(other.d) +{ +} + +QQuickImageProviderOptions& QQuickImageProviderOptions::operator=(const QQuickImageProviderOptions &other) +{ + d = other.d; + return *this; +} + +bool QQuickImageProviderOptions::operator==(const QQuickImageProviderOptions &other) const +{ + return d->autoTransform == other.d->autoTransform && + d->preserveAspectRatioCrop == other.d->preserveAspectRatioCrop && + d->preserveAspectRatioFit == other.d->preserveAspectRatioFit; +} + /*! - \fn QQuickTextureFactory *QQuickImageProvider::requestTexture(const QString &id, QSize *size, const QSize &requestedSize, bool requestedAutoTransform); + Returns whether the image provider should apply transformation metadata on read(). +*/ +QQuickImageProviderOptions::AutoTransform QQuickImageProviderOptions::autoTransform() const +{ + return d->autoTransform; +} + +void QQuickImageProviderOptions::setAutoTransform(QQuickImageProviderOptions::AutoTransform autoTransform) +{ + d->autoTransform = autoTransform; +} - \internal - For future reference. +/*! + Returns whether the image request is for a PreserveAspectCrop Image. + This allows the provider to better optimize the size of the returned image. */ +bool QQuickImageProviderOptions::preserveAspectRatioCrop() const +{ + return d->preserveAspectRatioCrop; +} + +void QQuickImageProviderOptions::setPreserveAspectRatioCrop(bool preserveAspectRatioCrop) +{ + d->preserveAspectRatioCrop = preserveAspectRatioCrop; +} + +/*! + Returns whether the image request is for a PreserveAspectFit Image. + This allows the provider to better optimize the size of the returned image. +*/ +bool QQuickImageProviderOptions::preserveAspectRatioFit() const +{ + return d->preserveAspectRatioFit; +} + +void QQuickImageProviderOptions::setPreserveAspectRatioFit(bool preserveAspectRatioFit) +{ + d->preserveAspectRatioFit = preserveAspectRatioFit; +} + + +QQuickImageProviderWithOptions::QQuickImageProviderWithOptions(ImageType type, Flags flags) + : QQuickAsyncImageProvider() +{ + QQuickImageProvider::d->type = type; + QQuickImageProvider::d->flags = flags; + QQuickImageProvider::d->isProviderWithOptions = true; +} + +QImage QQuickImageProviderWithOptions::requestImage(const QString &id, QSize *size, const QSize& requestedSize) +{ + return requestImage(id, size, requestedSize, QQuickImageProviderOptions()); +} + +QPixmap QQuickImageProviderWithOptions::requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) +{ + return requestPixmap(id, size, requestedSize, QQuickImageProviderOptions()); +} + +QQuickTextureFactory *QQuickImageProviderWithOptions::requestTexture(const QString &id, QSize *size, const QSize &requestedSize) +{ + return requestTexture(id, size, requestedSize, QQuickImageProviderOptions()); +} + +QImage QQuickImageProviderWithOptions::requestImage(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options) +{ + Q_UNUSED(options); + return QQuickAsyncImageProvider::requestImage(id, size, requestedSize); +} + +QPixmap QQuickImageProviderWithOptions::requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options) +{ + Q_UNUSED(options); + return QQuickAsyncImageProvider::requestPixmap(id, size, requestedSize); +} + +QQuickTextureFactory *QQuickImageProviderWithOptions::requestTexture(const QString &id, QSize *size, const QSize &requestedSize, const QQuickImageProviderOptions &options) +{ + Q_UNUSED(options); + return QQuickAsyncImageProvider::requestTexture(id, size, requestedSize); +} + +QQuickImageResponse *QQuickImageProviderWithOptions::requestImageResponse(const QString &id, const QSize &requestedSize) +{ + Q_UNUSED(id); + Q_UNUSED(requestedSize); + if (imageType() == ImageResponse) + qWarning("ImageProvider is of ImageResponse type but has not implemented requestImageResponse()"); + return nullptr; +} + +QQuickImageResponse *QQuickImageProviderWithOptions::requestImageResponse(const QString &id, const QSize &requestedSize, const QQuickImageProviderOptions &options) +{ + Q_UNUSED(options); + return requestImageResponse(id, requestedSize); +} QT_END_NAMESPACE diff --git a/src/quick/util/qquickimageprovider.h b/src/quick/util/qquickimageprovider.h index 879c4d0fcc..c77ff95f32 100644 --- a/src/quick/util/qquickimageprovider.h +++ b/src/quick/util/qquickimageprovider.h @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE class QQuickImageProviderPrivate; class QQuickAsyncImageProviderPrivate; +class QQuickImageProviderOptionsPrivate; class QSGTexture; class QQuickWindow; @@ -86,6 +87,8 @@ Q_SIGNALS: class Q_QUICK_EXPORT QQuickImageProvider : public QQmlImageProviderBase { + friend class QQuickImageProviderWithOptions; // ### Qt 6 Remove + friend class QQuickPixmapReader; // ### Qt 6 Remove public: QQuickImageProvider(ImageType type, Flags flags = Flags()); virtual ~QQuickImageProvider(); @@ -94,9 +97,9 @@ public: Flags flags() const override; #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) - virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize, bool requestedAutoTransform); - virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, bool requestedAutoTransform); - virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize, bool requestedAutoTransform); + virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options); + virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options); + virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize, const QQuickImageProviderOptions &options); #else virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize); virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize); @@ -113,7 +116,11 @@ public: QQuickAsyncImageProvider(); virtual ~QQuickAsyncImageProvider(); +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize, const QQuickImageProviderOptions &options) = 0; +#else virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) = 0; +#endif private: QQuickAsyncImageProviderPrivate *d; diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 7dffc09ba5..7b369a2d0f 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -140,18 +140,16 @@ public: QUrl url; bool loading; - AutoTransform autoTransform; int redirectCount; class Event : public QEvent { public: - Event(ReadError, const QString &, const QSize &, AutoTransform, QQuickTextureFactory *factory); + Event(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory); ~Event(); ReadError error; QString errorString; QSize implicitSize; - AutoTransform autoTransform; QQuickTextureFactory *textureFactory; }; void postReply(ReadError, const QString &, const QSize &, QQuickTextureFactory *factory); @@ -205,7 +203,7 @@ protected: private: friend class QQuickPixmapReaderThreadObject; void processJobs(); - void processJob(QQuickPixmapReply *, const QUrl &, const QString &, AutoTransform, QQuickImageProvider::ImageType, QQuickImageProvider *); + void processJob(QQuickPixmapReply *, const QUrl &, const QString &, const QQuickImageProviderOptions &, QQuickImageProvider::ImageType, QQuickImageProvider *); #if QT_CONFIG(qml_network) void networkRequestDone(QNetworkReply *); #endif @@ -239,20 +237,20 @@ public: class QQuickPixmapData { public: - QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &s, AutoTransform transform, const QString &e) + QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &s, const QQuickImageProviderOptions &po, const QString &e) : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Error), url(u), errorString(e), requestSize(s), - requestedTransform(transform), appliedTransform(UsePluginDefault), + providerOptions(po), appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform), textureFactory(0), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), nextUnreferenced(0) { declarativePixmaps.insert(pixmap); } - QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &r, AutoTransform rTransform, AutoTransform aTransform) + QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform) : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Loading), url(u), requestSize(r), - requestedTransform(rTransform), appliedTransform(aTransform), + providerOptions(po), appliedTransform(aTransform), textureFactory(0), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), nextUnreferenced(0) { @@ -260,10 +258,10 @@ public: } QQuickPixmapData(QQuickPixmap *pixmap, const QUrl &u, QQuickTextureFactory *texture, - const QSize &s, const QSize &r, AutoTransform rTransform, AutoTransform aTransform) + const QSize &s, const QSize &r, const QQuickImageProviderOptions &po, QQuickImageProviderOptions::AutoTransform aTransform) : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Ready), url(u), implicitSize(s), requestSize(r), - requestedTransform(rTransform), appliedTransform(aTransform), + providerOptions(po), appliedTransform(aTransform), textureFactory(texture), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), nextUnreferenced(0) { @@ -272,7 +270,7 @@ public: QQuickPixmapData(QQuickPixmap *pixmap, QQuickTextureFactory *texture) : refCount(1), inCache(false), pixmapStatus(QQuickPixmap::Ready), - requestedTransform(UsePluginDefault), appliedTransform(UsePluginDefault), + appliedTransform(QQuickImageProviderOptions::UsePluginDefaultTransform), textureFactory(texture), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), nextUnreferenced(0) { @@ -306,8 +304,8 @@ public: QString errorString; QSize implicitSize; QSize requestSize; - AutoTransform requestedTransform; - AutoTransform appliedTransform; + QQuickImageProviderOptions providerOptions; + QQuickImageProviderOptions::AutoTransform appliedTransform; QQuickTextureFactory *textureFactory; @@ -336,11 +334,11 @@ void QQuickPixmapReply::postReply(ReadError error, const QString &errorString, const QSize &implicitSize, QQuickTextureFactory *factory) { loading = false; - QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, autoTransform, factory)); + QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, factory)); } -QQuickPixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, AutoTransform iTransformed, QQuickTextureFactory *factory) - : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), autoTransform(iTransformed), textureFactory(factory) +QQuickPixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, QQuickTextureFactory *factory) + : QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), textureFactory(factory) { } @@ -384,25 +382,32 @@ static void maybeRemoveAlpha(QImage *image) } static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, - const QSize &requestSize, AutoTransform &autoTransform) + const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, + QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr) { + const bool preserveAspectCropOrFit = providerOptions.preserveAspectRatioCrop() || providerOptions.preserveAspectRatioFit(); + QImageReader imgio(dev); - if (autoTransform != UsePluginDefault) - imgio.setAutoTransform(autoTransform == ApplyTransform); - else - autoTransform = imgio.autoTransform() ? ApplyTransform : DoNotApplyTransform; + if (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform) + imgio.setAutoTransform(providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform); + else if (appliedTransform) + *appliedTransform = imgio.autoTransform() ? QQuickImageProviderOptions::ApplyTransform : QQuickImageProviderOptions::DoNotApplyTransform; const bool force_scale = imgio.format() == "svg" || imgio.format() == "svgz"; if (requestSize.width() > 0 || requestSize.height() > 0) { QSize s = imgio.size(); qreal ratio = 0.0; - if (requestSize.width() && (force_scale || requestSize.width() < s.width())) { + if (requestSize.width() && (preserveAspectCropOrFit || force_scale || requestSize.width() < s.width())) { ratio = qreal(requestSize.width())/s.width(); } - if (requestSize.height() && (force_scale || requestSize.height() < s.height())) { + if (requestSize.height() && (preserveAspectCropOrFit || force_scale || requestSize.height() < s.height())) { qreal hr = qreal(requestSize.height())/s.height(); - if (ratio == 0.0 || hr < ratio) + if (ratio == 0.0) + ratio = hr; + else if (!preserveAspectCropOrFit && (hr < ratio)) + ratio = hr; + else if (preserveAspectCropOrFit && (hr > ratio)) ratio = hr; } if (ratio > 0.0) { @@ -512,7 +517,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) QByteArray all = reply->readAll(); QBuffer buff(&all); buff.open(QIODevice::ReadOnly); - if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize, job->autoTransform)) + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize, job->data->providerOptions)) error = QQuickPixmapReply::Decoding; } // send completion event to the QQuickPixmapReply @@ -657,9 +662,8 @@ void QQuickPixmapReader::processJobs() PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url)); - AutoTransform autoTransform = job->autoTransform; locker.unlock(); - processJob(job, url, localFile, autoTransform, imageType, provider); + processJob(job, url, localFile, job->data->providerOptions, imageType, provider); locker.relock(); } } @@ -671,27 +675,50 @@ void QQuickPixmapReader::processJobs() } void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, const QString &localFile, - AutoTransform autoTransform, QQuickImageProvider::ImageType imageType, QQuickImageProvider *provider) + const QQuickImageProviderOptions &providerOptions, + QQuickImageProvider::ImageType imageType, QQuickImageProvider *provider) { // fetch if (url.scheme() == QLatin1String("image")) { // Use QQuickImageProvider QSize readSize; + if (imageType == QQuickImageProvider::Invalid) { + QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()); + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize, 0); + mutex.unlock(); + return; + } + + QQuickImageProviderWithOptions *providerV2 = provider->d->isProviderWithOptions ? static_cast<QQuickImageProviderWithOptions *>(provider) + : nullptr; + + if (!provider->d->isProviderWithOptions && + (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform + || providerOptions.preserveAspectRatioCrop() + || providerOptions.preserveAspectRatioFit()) + ) + { + qWarning() << "Trying to pass extra request flags to provider but it is not a QQuickImageProviderWithOptions"; + } + switch (imageType) { case QQuickImageProvider::Invalid: { - QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()); - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize, 0); - mutex.unlock(); + // Already handled break; } case QQuickImageProvider::Image: { - QImage image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize); + QImage image; + if (providerV2) { + image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, providerOptions); + } else { + image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize); + } QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; QString errorStr; if (image.isNull()) { @@ -707,7 +734,12 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u case QQuickImageProvider::Pixmap: { - const QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize); + QPixmap pixmap; + if (providerV2) { + pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, providerOptions); + } else { + pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize); + } QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; QString errorStr; if (pixmap.isNull()) { @@ -723,7 +755,12 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u case QQuickImageProvider::Texture: { - QQuickTextureFactory *t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize); + QQuickTextureFactory *t; + if (providerV2) { + t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, providerOptions); + } else { + t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize); + } QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; QString errorStr; if (!t) { @@ -741,8 +778,13 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u case QQuickImageProvider::ImageResponse: { - QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider); - QQuickImageResponse *response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize); + QQuickImageResponse *response; + if (providerV2) { + response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, providerOptions); + } else { + QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider); + response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize); + } QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished())); @@ -760,7 +802,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u QFile f(localFile); QSize readSize; if (f.open(QIODevice::ReadOnly)) { - if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, autoTransform)) + if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, providerOptions)) errorCode = QQuickPixmapReply::Loading; } else { errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); @@ -865,17 +907,17 @@ class QQuickPixmapKey public: const QUrl *url; const QSize *size; - AutoTransform autoTransform; + QQuickImageProviderOptions options; }; inline bool operator==(const QQuickPixmapKey &lhs, const QQuickPixmapKey &rhs) { - return *lhs.size == *rhs.size && *lhs.url == *rhs.url && lhs.autoTransform == rhs.autoTransform; + return *lhs.size == *rhs.size && *lhs.url == *rhs.url && lhs.options == rhs.options; } inline uint qHash(const QQuickPixmapKey &key) { - return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.autoTransform * 0x5c5c5c5c); + return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.options.autoTransform() * 0x5c5c5c5c); } class QQuickPixmapStore : public QObject @@ -1041,7 +1083,7 @@ void QQuickPixmap::purgeCache() } QQuickPixmapReply::QQuickPixmapReply(QQuickPixmapData *d) -: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), autoTransform(d->appliedTransform), redirectCount(0) +: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) { if (finishedIndex == -1) { finishedIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex(); @@ -1066,7 +1108,6 @@ bool QQuickPixmapReply::event(QEvent *event) data->textureFactory = de->textureFactory; de->textureFactory = 0; data->implicitSize = de->implicitSize; - data->appliedTransform = de->autoTransform; PIXMAP_PROFILE(pixmapLoadingFinished(data->url, data->textureFactory != 0 && data->textureFactory->textureSize().isValid() ? data->textureFactory->textureSize() : @@ -1137,7 +1178,7 @@ void QQuickPixmapData::release() void QQuickPixmapData::addToCache() { if (!inCache) { - QQuickPixmapKey key = { &url, &requestSize, requestedTransform }; + QQuickPixmapKey key = { &url, &requestSize, providerOptions }; pixmapStore()->m_cache.insert(key, this); inCache = true; PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>( @@ -1148,7 +1189,7 @@ void QQuickPixmapData::addToCache() void QQuickPixmapData::removeFromCache() { if (inCache) { - QQuickPixmapKey key = { &url, &requestSize, requestedTransform }; + QQuickPixmapKey key = { &url, &requestSize, providerOptions }; pixmapStore()->m_cache.remove(key); inCache = false; PIXMAP_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>( @@ -1156,7 +1197,7 @@ void QQuickPixmapData::removeFromCache() } } -static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url, const QSize &requestSize, AutoTransform autoTransform, bool *ok) +static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, bool *ok) { if (url.scheme() == QLatin1String("image")) { QSize readSize; @@ -1168,14 +1209,14 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q switch (imageType) { case QQuickImageProvider::Invalid: - return new QQuickPixmapData(declarativePixmap, url, requestSize, autoTransform, + return new QQuickPixmapData(declarativePixmap, url, requestSize, providerOptions, QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString())); case QQuickImageProvider::Texture: { QQuickTextureFactory *texture = provider->requestTexture(imageId(url), &readSize, requestSize); if (texture) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestSize, autoTransform, UsePluginDefault); + return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform); } break; } @@ -1185,7 +1226,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QImage image = provider->requestImage(imageId(url), &readSize, requestSize); if (!image.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, autoTransform, UsePluginDefault); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform); } break; } @@ -1194,7 +1235,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, requestSize); if (!pixmap.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize, autoTransform, UsePluginDefault); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform); } break; } @@ -1206,7 +1247,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q } // provider has bad image type, or provider returned null image - return new QQuickPixmapData(declarativePixmap, url, requestSize, autoTransform, + return new QQuickPixmapData(declarativePixmap, url, requestSize, providerOptions, QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString())); } @@ -1220,15 +1261,15 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q if (f.open(QIODevice::ReadOnly)) { QImage image; - AutoTransform appliedTransform = autoTransform; - if (readImage(url, &f, &image, &errorString, &readSize, requestSize, appliedTransform)) { + QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform(); + if (readImage(url, &f, &image, &errorString, &readSize, requestSize, providerOptions, &appliedTransform)) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, autoTransform, appliedTransform); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform); } } else { errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); } - return new QQuickPixmapData(declarativePixmap, url, requestSize, autoTransform, errorString); + return new QQuickPixmapData(declarativePixmap, url, requestSize, providerOptions, errorString); } @@ -1257,7 +1298,7 @@ QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QSize &siz QQuickPixmap::QQuickPixmap(const QUrl &url, const QImage &image) { - d = new QQuickPixmapData(this, url, new QQuickDefaultTextureFactory(image), image.size(), QSize(), UsePluginDefault, UsePluginDefault); + d = new QQuickPixmapData(this, url, new QQuickDefaultTextureFactory(image), image.size(), QSize(), QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform); d->addToCache(); } @@ -1330,12 +1371,12 @@ const QSize &QQuickPixmap::requestSize() const return nullPixmap()->size; } -AutoTransform QQuickPixmap::autoTransform() const +QQuickImageProviderOptions::AutoTransform QQuickPixmap::autoTransform() const { if (d) return d->appliedTransform; else - return UsePluginDefault; + return QQuickImageProviderOptions::UsePluginDefaultTransform; } QQuickTextureFactory *QQuickPixmap::textureFactory() const @@ -1413,10 +1454,10 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &size) void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options) { - load(engine, url, requestSize, options, UsePluginDefault); + load(engine, url, requestSize, options, QQuickImageProviderOptions()); } -void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options, AutoTransform requestAutoTransform) +void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &requestSize, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions) { if (d) { d->declarativePixmaps.remove(this); @@ -1424,7 +1465,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques d = 0; } - QQuickPixmapKey key = { &url, &requestSize, requestAutoTransform }; + QQuickPixmapKey key = { &url, &requestSize, providerOptions }; QQuickPixmapStore *store = pixmapStore(); QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end(); @@ -1450,7 +1491,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, requestAutoTransform, &ok); + d = createPixmapDataSync(this, engine, url, requestSize, providerOptions, &ok); if (ok) { PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height()))); if (options & QQuickPixmap::Cache) @@ -1466,7 +1507,8 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques if (!engine) return; - d = new QQuickPixmapData(this, url, requestSize, requestAutoTransform, requestAutoTransform); + + d = new QQuickPixmapData(this, url, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform); if (options & QQuickPixmap::Cache) d->addToCache(); @@ -1502,7 +1544,7 @@ void QQuickPixmap::clear(QObject *obj) bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize) { - QQuickPixmapKey key = { &url, &requestSize, UsePluginDefault }; + QQuickPixmapKey key = { &url, &requestSize, QQuickImageProviderOptions() }; 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 623c826815..eea6a7a454 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -65,12 +65,7 @@ QT_BEGIN_NAMESPACE class QQmlEngine; class QQuickPixmapData; class QQuickTextureFactory; - -enum AutoTransform { - UsePluginDefault = -1, - ApplyTransform = 0, - DoNotApplyTransform = 1 -}; +class QQuickImageProviderOptionsPrivate; class QQuickDefaultTextureFactory : public QQuickTextureFactory { @@ -87,6 +82,45 @@ private: QSize size; }; +class QQuickImageProviderPrivate +{ +public: + QQuickImageProvider::ImageType type; + QQuickImageProvider::Flags flags; + bool isProviderWithOptions; +}; + +// ### Qt 6: Make public moving to qquickimageprovider.h +class Q_QUICK_EXPORT QQuickImageProviderOptions +{ +public: + enum AutoTransform { + UsePluginDefaultTransform = -1, + ApplyTransform = 0, + DoNotApplyTransform = 1 + }; + + QQuickImageProviderOptions(); + ~QQuickImageProviderOptions(); + + QQuickImageProviderOptions(const QQuickImageProviderOptions&); + QQuickImageProviderOptions& operator=(const QQuickImageProviderOptions&); + + bool operator==(const QQuickImageProviderOptions&) const; + + AutoTransform autoTransform() const; + void setAutoTransform(AutoTransform autoTransform); + + bool preserveAspectRatioCrop() const; + void setPreserveAspectRatioCrop(bool preserveAspectRatioCrop); + + bool preserveAspectRatioFit() const; + void setPreserveAspectRatioFit(bool preserveAspectRatioFit); + +private: + QSharedDataPointer<QQuickImageProviderOptionsPrivate> d; +}; + class Q_QUICK_PRIVATE_EXPORT QQuickPixmap { Q_DECLARE_TR_FUNCTIONS(QQuickPixmap) @@ -115,7 +149,7 @@ public: const QUrl &url() const; const QSize &implicitSize() const; const QSize &requestSize() const; - AutoTransform autoTransform() const; + QQuickImageProviderOptions::AutoTransform autoTransform() const; QImage image() const; void setImage(const QImage &); void setPixmap(const QQuickPixmap &other); @@ -130,7 +164,7 @@ public: 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, AutoTransform autoTransform); + void load(QQmlEngine *, const QUrl &, const QSize &, QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions); void clear(); void clear(QObject *); @@ -152,6 +186,24 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPixmap::Options) +// This class will disappear with Qt6 and will just be the regular QQuickImageProvider +// ### Qt 6: Remove this class and fold it with QQuickImageProvider +class Q_QUICK_EXPORT QQuickImageProviderWithOptions : public QQuickAsyncImageProvider +{ +public: + QQuickImageProviderWithOptions(ImageType type, Flags flags = Flags()); + + QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize) override; + QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) override; + QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize) override; + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; + + virtual QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options); + virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options); + virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize, const QQuickImageProviderOptions &options); + virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize, const QQuickImageProviderOptions &options); +}; + QT_END_NAMESPACE #endif // QQUICKPIXMAPCACHE_H |