Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquickimagebase.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickimagebase_p.h"
6
7#include <QtGui/qguiapplication.h>
8#include <QtGui/qscreen.h>
9#include <QtGui/qicon.h>
10
11#include <QtQml/qqmlinfo.h>
12#include <QtQml/qqmlfile.h>
13#include <QtQml/qqmlabstracturlinterceptor.h>
14
15
17
19{
20 if (url.scheme() == QLatin1String("image"))
21 return true;
22
23 const QString stringUrl = url.path(QUrl::PrettyDecoded);
24 return stringUrl.endsWith(QLatin1String("svg"))
25 || stringUrl.endsWith(QLatin1String("svgz"))
26 || stringUrl.endsWith(QLatin1String("pdf"));
27}
28
29// This function gives derived classes the chance set the devicePixelRatio
30// if they're not happy with our implementation of it.
32{
33 // QQuickImageProvider and SVG and PDF can generate a high resolution image when
34 // sourceSize is set. If sourceSize is not set then the provider default size will
35 // be used, as usual.
36 const bool setDevicePixelRatio = isScalableImageFormat(url);
37
38 if (setDevicePixelRatio)
39 devicePixelRatio = targetDevicePixelRatio;
40
41 return setDevicePixelRatio;
42}
43
45{
46 Q_Q(QQuickImageBase);
47
48 if (status == value)
49 return;
50
51 status = value;
52 emit q->statusChanged(status);
53}
54
56{
57 Q_Q(QQuickImageBase);
58
60 return;
61
63 emit q->progressChanged(progress);
64}
65
68{
70}
71
74{
76}
77
79{
80}
81
83{
84 Q_D(const QQuickImageBase);
85 return d->status;
86}
87
89{
90 Q_D(const QQuickImageBase);
91 return d->progress;
92}
93
95{
96 Q_D(const QQuickImageBase);
97 return d->async;
98}
99
101{
102 Q_D(QQuickImageBase);
103 if (d->async != async) {
104 d->async = async;
106 }
107}
108
110{
111 Q_D(const QQuickImageBase);
112 return d->url;
113}
114
116{
117 Q_D(QQuickImageBase);
118
119 if (url == d->url)
120 return;
121
122 d->url = url;
123 emit sourceChanged(d->url);
124
126 load();
127}
128
130{
131 Q_D(QQuickImageBase);
132 if (d->sourcesize == size)
133 return;
134
135 d->sourcesize = size;
138 load();
139}
140
142{
143 Q_D(const QQuickImageBase);
144
145 int width = d->sourcesize.width();
146 int height = d->sourcesize.height();
147 return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height());
148}
149
151{
153}
154
156{
157 Q_D(const QQuickImageBase);
158 return d->sourceClipRect;
159}
160
162{
163 Q_D(QQuickImageBase);
164 if (d->sourceClipRect == r)
165 return;
166
167 d->sourceClipRect = r;
168 emit sourceClipRectChanged();
170 load();
171}
172
174{
176}
177
179{
180 Q_D(const QQuickImageBase);
181 return d->cache;
182}
183
185{
186 Q_D(QQuickImageBase);
187 if (d->cache == cache)
188 return;
189
190 d->cache = cache;
193 load();
194}
195
197{
198 Q_D(const QQuickImageBase);
199 return d->pix.image();
200}
201
203{
204 Q_D(QQuickImageBase);
205 if (mirror == d->mirrorHorizontally)
206 return;
207
208 d->mirrorHorizontally = mirror;
209
211 update();
212
214}
215
217{
218 Q_D(const QQuickImageBase);
219 return d->mirrorHorizontally;
220}
221
223{
224 Q_D(QQuickImageBase);
225 if (mirror == d->mirrorVertically)
226 return;
227
228 d->mirrorVertically = mirror;
229
231 update();
232
233 emit mirrorVerticallyChanged();
234}
235
237{
238 Q_D(const QQuickImageBase);
239 return d->mirrorVertically;
240}
241
243{
244 Q_D(QQuickImageBase);
245 if (frame == d->currentFrame || frame < 0 || (isComponentComplete() && frame >= d->pix.frameCount()))
246 return;
247
248 d->currentFrame = frame;
249
250 if (isComponentComplete()) {
251 if (frame > 0)
252 d->cache = false;
253 load();
254 update();
255 }
256
257 emit currentFrameChanged();
258}
259
261{
262 Q_D(const QQuickImageBase);
263 return d->currentFrame;
264}
265
267{
268 Q_D(const QQuickImageBase);
269 return d->frameCount;
270}
271
273{
274 Q_D(QQuickImageBase);
275 d->pix.clear(this);
276 d->setProgress(0);
277 d->setStatus(Null);
278 setImplicitSize(0, 0); // also called in QQuickImageBase::pixmapChange, but not QQuickImage/QQuickBorderImage overrides
279 pixmapChange(); // This calls update() in QQuickBorderImage and QQuickImage, not in QQuickImageBase...
280
281 if (sourceSize() != d->oldSourceSize) {
282 d->oldSourceSize = sourceSize();
284 }
285 if (autoTransform() != d->oldAutoTransform) {
286 d->oldAutoTransform = autoTransform();
288 }
289 update(); // .. but double updating should be harmless
290}
291
292void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
293{
294 Q_D(QQuickImageBase);
295 QQuickPixmap::Options options;
296 if (d->async)
298 if (d->cache)
299 options |= QQuickPixmap::Cache;
300 d->pix.clear(this);
301 QUrl loadUrl = url;
302 const QQmlContext *context = qmlContext(this);
303 if (context)
304 loadUrl = context->resolvedUrl(url);
305
306 if (loadOptions & HandleDPR) {
307 const qreal targetDevicePixelRatio = (window() ? window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
308 d->devicePixelRatio = 1.0;
309 bool updatedDevicePixelRatio = false;
310 if (d->sourcesize.isValid()
311 || (isScalableImageFormat(d->url) && d->url.scheme() != QLatin1String("image"))) {
312 updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio);
313 }
314
315 if (!updatedDevicePixelRatio) {
316 // (possible) local file: loadUrl and d->devicePixelRatio will be modified if
317 // an "@2x" file is found.
318 resolve2xLocalFile(context ? context->resolvedUrl(d->url) : d->url,
319 targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio);
320 }
321 }
322
323 d->status = Null; // reset status, no emit
324
325 d->pix.load(qmlEngine(this),
326 loadUrl,
327 d->sourceClipRect.toRect(),
328 (loadOptions & HandleDPR) ? d->sourcesize * d->devicePixelRatio : QSize(),
329 options,
330 (loadOptions & UseProviderOptions) ? d->providerOptions : QQuickImageProviderOptions(),
331 d->currentFrame, d->frameCount,
332 d->devicePixelRatio);
333
334 if (d->pix.isLoading()) {
335 d->setProgress(0);
336 d->setStatus(Loading);
337
338 static int thisRequestProgress = -1;
339 static int thisRequestFinished = -1;
340 if (thisRequestProgress == -1) {
341 thisRequestProgress =
342 QQuickImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)");
343 thisRequestFinished =
344 QQuickImageBase::staticMetaObject.indexOfSlot("requestFinished()");
345 }
346
347 d->pix.connectFinished(this, thisRequestFinished);
348 d->pix.connectDownloadProgress(this, thisRequestProgress);
349 update(); //pixmap may have invalidated texture, updatePaintNode needs to be called before the next repaint
350 } else {
352 }
353}
354
356{
357 Q_D(QQuickImageBase);
358
359 if (d->url.isEmpty()) {
360 loadEmptyUrl();
361 update();
362 } else {
363 loadPixmap(d->url, LoadPixmapOptions(HandleDPR | UseProviderOptions));
364 }
365}
366
368{
369 Q_D(QQuickImageBase);
370
371 if (d->pix.isError()) {
372 qmlWarning(this) << d->pix.error();
373 d->pix.clear(this);
374 d->setStatus(Error);
375 d->setProgress(0);
376 } else {
377 d->setStatus(Ready);
378 d->setProgress(1);
379 }
380 pixmapChange();
381
382 if (sourceSize() != d->oldSourceSize) {
383 d->oldSourceSize = sourceSize();
385 }
386 if (autoTransform() != d->oldAutoTransform) {
387 d->oldAutoTransform = autoTransform();
389 }
390 if (d->frameCount != d->pix.frameCount()) {
391 d->frameCount = d->pix.frameCount();
392 emit frameCountChanged();
393 }
394 if (d->colorSpace != d->pix.colorSpace()) {
395 d->colorSpace = d->pix.colorSpace();
396 emit colorSpaceChanged();
397 }
398
399 update();
400}
401
402void QQuickImageBase::requestProgress(qint64 received, qint64 total)
403{
404 Q_D(QQuickImageBase);
405 if (d->status == Loading && total > 0)
406 d->setProgress(qreal(received) / total);
407}
408
410{
411 Q_D(QQuickImageBase);
412 // If the screen DPI changed, reload image.
413 if (change == ItemDevicePixelRatioHasChanged && value.realValue != d->devicePixelRatio) {
414 const auto oldDpr = d->devicePixelRatio;
415 // ### how can we get here with !qmlEngine(this)? that implies
416 // itemChange() on an item pending deletion, which seems strange.
417 if (qmlEngine(this) && isComponentComplete() && d->url.isValid()) {
418 load();
419 // not changed when loading (sourceSize might not be set)
420 if (d->devicePixelRatio == oldDpr)
421 d->updateDevicePixelRatio(value.realValue);
422 }
423 }
425}
426
428{
429 Q_D(QQuickImageBase);
431 if (d->url.isValid())
432 load();
433}
434
436{
437 Q_D(QQuickImageBase);
438 setImplicitSize(d->pix.width() / d->devicePixelRatio, d->pix.height() / d->devicePixelRatio);
439}
440
441void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixelRatio, QUrl *sourceUrl, qreal *sourceDevicePixelRatio)
442{
443 Q_ASSERT(sourceUrl);
444 Q_ASSERT(sourceDevicePixelRatio);
445
446 // Bail out if "@2x" image loading is disabled, don't change the source url or devicePixelRatio.
447 static const bool disable2xImageLoading = !qEnvironmentVariableIsEmpty("QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
448 if (disable2xImageLoading)
449 return;
450
451 const QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
452
453 // Non-local file path: @2x loading is not supported.
454 if (localFile.isEmpty())
455 return;
456
457 // Special case: the url in the QML source refers directly to an "@2x" file.
458 int atLocation = localFile.lastIndexOf(QLatin1Char('@'));
459 if (atLocation > 0 && atLocation + 3 < localFile.size()) {
460 if (localFile[atLocation + 1].isDigit()
461 && localFile[atLocation + 2] == QLatin1Char('x')
462 && localFile[atLocation + 3] == QLatin1Char('.')) {
463 *sourceDevicePixelRatio = localFile[atLocation + 1].digitValue();
464 return;
465 }
466 }
467
468 // Look for an @2x version
469 QString localFileX = qt_findAtNxFile(localFile, targetDevicePixelRatio, sourceDevicePixelRatio);
470 if (localFileX != localFile)
471 *sourceUrl = QUrl::fromLocalFile(localFileX);
472}
473
475{
476 Q_D(const QQuickImageBase);
477 if (d->providerOptions.autoTransform() == QQuickImageProviderOptions::UsePluginDefaultTransform)
478 return d->pix.autoTransform() == QQuickImageProviderOptions::ApplyTransform;
479 return d->providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform;
480}
481
483{
484 Q_D(QQuickImageBase);
485 if (d->providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform &&
486 transform == (d->providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform))
487 return;
490}
491
493{
494 Q_D(const QQuickImageBase);
495 return d->colorSpace;
496}
497
499{
500 Q_D(QQuickImageBase);
501 if (d->colorSpace == colorSpace)
502 return;
503 d->colorSpace = colorSpace;
504 d->providerOptions.setTargetColorSpace(colorSpace);
505 emit colorSpaceChanged();
506}
507
509
510#include "moc_qquickimagebase_p.cpp"
The QColorSpace class provides a color space abstraction.
Definition qcolorspace.h:21
\inmodule QtGui
Definition qimage.h:37
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to QFile.
Definition qqmlfile.cpp:643
QQuickImageBase::Status status
virtual bool updateDevicePixelRatio(qreal targetDevicePixelRatio)
void setProgress(qreal value)
void setStatus(QQuickImageBase::Status value)
bool autoTransform() const
void itemChange(ItemChange change, const ItemChangeData &value) override
Called when change occurs for this item.
virtual void pixmapChange()
virtual void setMirror(bool mirror)
virtual void setAutoTransform(bool transform)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
virtual void load()
void sourceSizeChanged()
virtual void setSourceSize(const QSize &)
void asynchronousChanged()
virtual void setColorSpace(const QColorSpace &colorSpace)
void loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions=NoOption)
void setSourceClipRect(const QRectF &r)
QImage image() const
static void resolve2xLocalFile(const QUrl &url, qreal targetDevicePixelRatio, QUrl *sourceUrl, qreal *sourceDevicePixelRatio)
QColorSpace colorSpace
QRectF sourceClipRect() const
virtual void setCurrentFrame(int frame)
virtual void requestFinished()
virtual void emitAutoTransformBaseChanged()
virtual void setSource(const QUrl &url)
void sourceChanged(const QUrl &)
void setAsynchronous(bool)
void mirrorChanged()
QQuickImageBase(QQuickItem *parent=nullptr)
virtual void setMirrorVertically(bool mirror)
The QQuickImageProviderOptions class provides options for QQuickImageProviderWithOptions image reques...
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
void setFlag(Flag flag, bool enabled=true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disable...
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
QSizeF size() const
QQuickWindow * window() const
Returns the window in which this item is rendered.
virtual void itemChange(ItemChange, const ItemChangeData &)
Called when change occurs for this item.
bool isComponentComplete() const
Returns true if construction of the QML component is complete; otherwise returns false.
qreal height
This property holds the height of this item.
Definition qquickitem.h:77
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:143
@ ItemDevicePixelRatioHasChanged
Definition qquickitem.h:153
void update()
Schedules a call to updatePaintNode() for this item.
void setImplicitSize(qreal, qreal)
\inmodule QtCore\reentrant
Definition qrect.h:483
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3354
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1983
@ PrettyDecoded
Definition qurl.h:121
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2465
QCache< int, Employee > cache
[0]
Combined button and popup list for selecting options.
static void * context
#define qApp
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio, qreal *sourceDevicePixelRatio)
Definition qicon.cpp:1528
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLint GLsizei width
GLuint GLenum GLenum transform
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:76
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
QT_BEGIN_NAMESPACE bool isScalableImageFormat(const QUrl &url)
static QT_BEGIN_NAMESPACE bool isDigit(ushort ch)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
#define emit
long long qint64
Definition qtypes.h:55
double qreal
Definition qtypes.h:92
QUrl url("example.com")
[constructor-url-reference]
QFrame frame
[0]
\inmodule QtCore \reentrant
Definition qchar.h:17
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent
\inmodule QtQuick
Definition qquickitem.h:158