diff options
Diffstat (limited to 'src/quick/items/qquickimagebase.cpp')
-rw-r--r-- | src/quick/items/qquickimagebase.cpp | 390 |
1 files changed, 258 insertions, 132 deletions
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 74ee859f77..b00baf7f7d 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qquickimagebase_p.h" #include "qquickimagebase_p_p.h" @@ -46,26 +10,21 @@ #include <QtQml/qqmlinfo.h> #include <QtQml/qqmlfile.h> +#include <QtQml/qqmlabstracturlinterceptor.h> + QT_BEGIN_NAMESPACE +using namespace Qt::Literals::StringLiterals; + // This function gives derived classes the chance set the devicePixelRatio // if they're not happy with our implementation of it. bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio) { - // QQuickImageProvider and SVG can generate a high resolution image when - // sourceSize is set (this function is only called if it's set). - // If sourceSize is not set then the provider default size will be used, as usual. - bool setDevicePixelRatio = false; - if (url.scheme() == QLatin1String("image")) { - setDevicePixelRatio = true; - } else { - QString stringUrl = url.path(QUrl::PrettyDecoded); - if (stringUrl.endsWith(QLatin1String("svg")) || - stringUrl.endsWith(QLatin1String("svgz"))) { - setDevicePixelRatio = true; - } - } + // QQuickImageProvider and SVG and PDF can generate a high resolution image when + // sourceSize is set. If sourceSize is not set then the provider default size will + // be used, as usual. + const bool setDevicePixelRatio = QQuickPixmap::isScalableImageFormat(url); if (setDevicePixelRatio) devicePixelRatio = targetDevicePixelRatio; @@ -73,6 +32,28 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio return setDevicePixelRatio; } +void QQuickImageBasePrivate::setStatus(QQuickImageBase::Status value) +{ + Q_Q(QQuickImageBase); + + if (status == value) + return; + + status = value; + emit q->statusChanged(status); +} + +void QQuickImageBasePrivate::setProgress(qreal value) +{ + Q_Q(QQuickImageBase); + + if (qFuzzyCompare(progress, value)) + return; + + progress = value; + emit q->progressChanged(progress); +} + QQuickImageBase::QQuickImageBase(QQuickItem *parent) : QQuickImplicitSizeItem(*(new QQuickImageBasePrivate), parent) { @@ -95,14 +76,12 @@ QQuickImageBase::Status QQuickImageBase::status() const return d->status; } - qreal QQuickImageBase::progress() const { Q_D(const QQuickImageBase); return d->progress; } - bool QQuickImageBase::asynchronous() const { Q_D(const QQuickImageBase); @@ -156,7 +135,7 @@ QSize QQuickImageBase::sourceSize() const int width = d->sourcesize.width(); int height = d->sourcesize.height(); - return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height()); + return QSize(width != -1 ? width : d->currentPix->width(), height != -1 ? height : d->currentPix->height()); } void QQuickImageBase::resetSourceSize() @@ -164,6 +143,30 @@ 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; + d->providerOptions.setSourceClipRect(r); + emit sourceClipRectChanged(); + if (isComponentComplete()) + load(); +} + +void QQuickImageBase::resetSourceClipRect() +{ + setSourceClipRect(QRect()); +} + bool QQuickImageBase::cache() const { Q_D(const QQuickImageBase); @@ -185,16 +188,16 @@ void QQuickImageBase::setCache(bool cache) QImage QQuickImageBase::image() const { Q_D(const QQuickImageBase); - return d->pix.image(); + return d->currentPix->image(); } void QQuickImageBase::setMirror(bool mirror) { Q_D(QQuickImageBase); - if (mirror == d->mirror) + if (mirror == d->mirrorHorizontally) return; - d->mirror = mirror; + d->mirrorHorizontally = mirror; if (isComponentComplete()) update(); @@ -205,107 +208,179 @@ void QQuickImageBase::setMirror(bool mirror) bool QQuickImageBase::mirror() const { Q_D(const QQuickImageBase); - return d->mirror; + return d->mirrorHorizontally; } -void QQuickImageBase::load() +void QQuickImageBase::setMirrorVertically(bool mirror) { Q_D(QQuickImageBase); + if (mirror == d->mirrorVertically) + return; - if (d->url.isEmpty()) { - d->pix.clear(this); - if (d->progress != 0.0) { - d->progress = 0.0; - emit progressChanged(d->progress); - } - pixmapChange(); - d->status = Null; - emit statusChanged(d->status); + d->mirrorVertically = mirror; - if (sourceSize() != d->oldSourceSize) { - d->oldSourceSize = sourceSize(); - emit sourceSizeChanged(); - } - if (autoTransform() != d->oldAutoTransform) { - d->oldAutoTransform = autoTransform(); - emitAutoTransformBaseChanged(); - } + if (isComponentComplete()) update(); - } else { - QQuickPixmap::Options options; - if (d->async) - options |= QQuickPixmap::Asynchronous; - if (d->cache) - options |= QQuickPixmap::Cache; - d->pix.clear(this); + emit mirrorVerticallyChanged(); +} - const qreal targetDevicePixelRatio = (window() ? window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio()); - d->devicePixelRatio = 1.0; +bool QQuickImageBase::mirrorVertically() const +{ + Q_D(const QQuickImageBase); + return d->mirrorVertically; +} + +void QQuickImageBase::setCurrentFrame(int frame) +{ + Q_D(QQuickImageBase); + if (frame == d->currentFrame || frame < 0 || (isComponentComplete() && frame >= d->currentPix->frameCount())) + return; + + d->currentFrame = frame; + + if (isComponentComplete()) { + if (frame > 0) + d->cache = false; + load(); + update(); + } + + emit currentFrameChanged(); +} + +int QQuickImageBase::currentFrame() const +{ + Q_D(const QQuickImageBase); + return d->currentFrame; +} + +int QQuickImageBase::frameCount() const +{ + Q_D(const QQuickImageBase); + return d->frameCount; +} - QUrl loadUrl = d->url; +void QQuickImageBase::loadEmptyUrl() +{ + Q_D(QQuickImageBase); + d->currentPix->clear(this); + d->pendingPix->clear(this); + d->setProgress(0); + d->status = Null; // do not emit statusChanged until after setImplicitSize + setImplicitSize(0, 0); // also called in QQuickImageBase::pixmapChange, but not QQuickImage/QQuickBorderImage overrides + pixmapChange(); // This calls update() in QQuickBorderImage and QQuickImage, not in QQuickImageBase... + emit statusChanged(d->status); + if (sourceSize() != d->oldSourceSize) { + d->oldSourceSize = sourceSize(); + emit sourceSizeChanged(); + } + if (autoTransform() != d->oldAutoTransform) { + d->oldAutoTransform = autoTransform(); + emitAutoTransformBaseChanged(); + } + update(); // .. but double updating should be harmless +} + +void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions) +{ + Q_D(QQuickImageBase); + QQuickPixmap::Options options; + if (d->async) + options |= QQuickPixmap::Asynchronous; + if (d->cache) + options |= QQuickPixmap::Cache; + d->pendingPix->clear(this); + QUrl loadUrl = url; + const QQmlContext *context = qmlContext(this); + if (context) + loadUrl = context->resolvedUrl(url); + + if (loadOptions & HandleDPR) { + const qreal targetDevicePixelRatio = (window() ? window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio()); + d->devicePixelRatio = 1.0; bool updatedDevicePixelRatio = false; - if (d->sourcesize.isValid()) + if (d->sourcesize.isValid() + || (QQuickPixmap::isScalableImageFormat(d->url) && d->url.scheme() != "image"_L1)) { updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio); + } if (!updatedDevicePixelRatio) { // (possible) local file: loadUrl and d->devicePixelRatio will be modified if // an "@2x" file is found. - resolve2xLocalFile(d->url, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio); + resolve2xLocalFile(context ? context->resolvedUrl(d->url) : d->url, + targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio); + } + } + + d->status = Null; // reset status, no emit + + d->pendingPix->load(qmlEngine(this), + loadUrl, + d->sourceClipRect.toRect(), + (loadOptions & HandleDPR) ? d->sourcesize * d->devicePixelRatio : QSize(), + options, + (loadOptions & UseProviderOptions) ? d->providerOptions : QQuickImageProviderOptions(), + d->currentFrame, d->frameCount, + d->devicePixelRatio); + + if (d->pendingPix->isLoading()) { + d->setProgress(0); + d->setStatus(Loading); + + static int thisRequestProgress = -1; + static int thisRequestFinished = -1; + if (thisRequestProgress == -1) { + thisRequestProgress = + QQuickImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); + thisRequestFinished = + QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()"); } - d->pix.load(qmlEngine(this), loadUrl, d->sourcesize * d->devicePixelRatio, options, d->providerOptions); - - if (d->pix.isLoading()) { - if (d->progress != 0.0) { - d->progress = 0.0; - emit progressChanged(d->progress); - } - if (d->status != Loading) { - d->status = Loading; - emit statusChanged(d->status); - } - - static int thisRequestProgress = -1; - static int thisRequestFinished = -1; - if (thisRequestProgress == -1) { - thisRequestProgress = - QQuickImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); - thisRequestFinished = - QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()"); - } - - d->pix.connectFinished(this, thisRequestFinished); - d->pix.connectDownloadProgress(this, thisRequestProgress); + d->pendingPix->connectFinished(this, thisRequestFinished); + d->pendingPix->connectDownloadProgress(this, thisRequestProgress); + if (!d->retainWhileLoading) update(); //pixmap may have invalidated texture, updatePaintNode needs to be called before the next repaint - } else { - requestFinished(); - } + } else { + requestFinished(); + } +} + +void QQuickImageBase::load() +{ + Q_D(QQuickImageBase); + + if (d->url.isEmpty()) { + loadEmptyUrl(); + update(); + } else { + loadPixmap(d->url, LoadPixmapOptions(HandleDPR | UseProviderOptions)); } } void QQuickImageBase::requestFinished() { Q_D(QQuickImageBase); + if (d->pendingPix != d->currentPix + && d->pendingPix->status() != QQuickPixmap::Null + && d->pendingPix->status() != QQuickPixmap::Loading) { + std::swap(d->pendingPix, d->currentPix); + d->pendingPix->clear(this); // Clear the old image + } - if (d->pix.isError()) { - qmlWarning(this) << d->pix.error(); - d->pix.clear(this); + if (d->currentPix->isError()) { + qmlWarning(this) << d->currentPix->error(); d->status = Error; - if (d->progress != 0.0) { - d->progress = 0.0; - emit progressChanged(d->progress); - } + d->setProgress(0); } else { - d->status = Ready; - if (d->progress != 1.0) { - d->progress = 1.0; - emit progressChanged(d->progress); - } + d->status = Ready; // do not emit statusChanged until after setImplicitSize + d->setProgress(1); } + pixmapChange(); emit statusChanged(d->status); + if (sourceSize() != d->oldSourceSize) { d->oldSourceSize = sourceSize(); emit sourceSizeChanged(); @@ -314,16 +389,23 @@ void QQuickImageBase::requestFinished() d->oldAutoTransform = autoTransform(); emitAutoTransformBaseChanged(); } + if (d->frameCount != d->currentPix->frameCount()) { + d->frameCount = d->currentPix->frameCount(); + emit frameCountChanged(); + } + if (d->colorSpace != d->currentPix->colorSpace()) { + d->colorSpace = d->currentPix->colorSpace(); + emit colorSpaceChanged(); + } + update(); } void QQuickImageBase::requestProgress(qint64 received, qint64 total) { Q_D(QQuickImageBase); - if (d->status == Loading && total > 0) { - d->progress = qreal(received)/total; - emit progressChanged(d->progress); - } + if (d->status == Loading && total > 0) + d->setProgress(qreal(received) / total); } void QQuickImageBase::itemChange(ItemChange change, const ItemChangeData &value) @@ -331,10 +413,14 @@ void QQuickImageBase::itemChange(ItemChange change, const ItemChangeData &value) Q_D(QQuickImageBase); // If the screen DPI changed, reload image. if (change == ItemDevicePixelRatioHasChanged && value.realValue != d->devicePixelRatio) { + const auto oldDpr = d->devicePixelRatio; // ### how can we get here with !qmlEngine(this)? that implies // itemChange() on an item pending deletion, which seems strange. if (qmlEngine(this) && isComponentComplete() && d->url.isValid()) { load(); + // not changed when loading (sourceSize might not be set) + if (d->devicePixelRatio == oldDpr) + d->updateDevicePixelRatio(value.realValue); } } QQuickItem::itemChange(change, value); @@ -351,7 +437,7 @@ void QQuickImageBase::componentComplete() void QQuickImageBase::pixmapChange() { Q_D(QQuickImageBase); - setImplicitSize(d->pix.width() / d->devicePixelRatio, d->pix.height() / d->devicePixelRatio); + setImplicitSize(d->currentPix->width() / d->devicePixelRatio, d->currentPix->height() / d->devicePixelRatio); } void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixelRatio, QUrl *sourceUrl, qreal *sourceDevicePixelRatio) @@ -391,7 +477,7 @@ bool QQuickImageBase::autoTransform() const { Q_D(const QQuickImageBase); if (d->providerOptions.autoTransform() == QQuickImageProviderOptions::UsePluginDefaultTransform) - return d->pix.autoTransform() == QQuickImageProviderOptions::ApplyTransform; + return d->currentPix->autoTransform() == QQuickImageProviderOptions::ApplyTransform; return d->providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform; } @@ -405,6 +491,46 @@ void QQuickImageBase::setAutoTransform(bool transform) emitAutoTransformBaseChanged(); } +QColorSpace QQuickImageBase::colorSpace() const +{ + Q_D(const QQuickImageBase); + return d->colorSpace; +} + +void QQuickImageBase::setColorSpace(const QColorSpace &colorSpace) +{ + Q_D(QQuickImageBase); + if (d->colorSpace == colorSpace) + return; + d->colorSpace = colorSpace; + d->providerOptions.setTargetColorSpace(colorSpace); + emit colorSpaceChanged(); +} + +bool QQuickImageBase::retainWhileLoading() const +{ + Q_D(const QQuickImageBase); + return d->retainWhileLoading; +} + +void QQuickImageBase::setRetainWhileLoading(bool retainWhileLoading) +{ + Q_D(QQuickImageBase); + if (d->retainWhileLoading == retainWhileLoading) + return; + + d->retainWhileLoading = retainWhileLoading; + if (d->retainWhileLoading) { + if (d->currentPix == &d->pix1) + d->pendingPix = &d->pix2; + else + d->pendingPix = &d->pix1; + } else { + d->pendingPix->clear(); + d->pendingPix = d->currentPix; + } +} + QT_END_NAMESPACE #include "moc_qquickimagebase_p.cpp" |