4#include <private/qsgadaptationlayer_p.h>
6#include <private/qquickitem_p.h>
7#include <private/qquickcanvascontext_p.h>
8#include <private/qquickcontext2d_p.h>
9#include <private/qquickcontext2dtexture_p.h>
10#include <private/qsgadaptationlayer_p.h>
11#include <qsgtextureprovider.h>
12#include <QtQuick/private/qquickpixmap_p.h>
13#include <QtGui/QGuiApplication>
14#include <qsgtextureprovider.h>
17#include <private/qqmlengine_p.h>
18#include <QtCore/QBuffer>
19#include <QtCore/qdatetime.h>
21#include <private/qv4value_p.h>
22#include <private/qv4functionobject_p.h>
23#include <private/qv4scopedvalue_p.h>
24#include <private/qv4jscall_p.h>
25#include <private/qv4qobjectwrapper_p.h>
26#include <private/qjsvalue_p.h>
59 return m_pixmap->width();
61 return m_image.width();
67 return m_pixmap->height();
69 return m_image.height();
75 return m_pixmap->isReady();
76 return !m_image.isNull();
81 if (m_image.isNull() && m_pixmap)
82 m_image = m_pixmap->image();
91 : QThread(eng), m_engine(eng), m_eventLoopQuitHack(
nullptr)
94 m_eventLoopQuitHack =
new QObject;
95 m_eventLoopQuitHack->moveToThread(
this);
96 connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
97 start(QThread::IdlePriority);
102 renderThreadsMutex.lock();
103 renderThreads.remove(m_engine);
104 renderThreadsMutex.unlock();
106 m_eventLoopQuitHack->deleteLater();
113 renderThreadsMutex.lock();
114 if (renderThreads.contains(engine))
115 thread = renderThreads.value(engine);
117 thread =
new QQuickContext2DRenderThread(engine);
118 renderThreads.insert(engine, thread);
120 renderThreadsMutex.unlock();
164 implicitAntialiasing =
true;
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
259 : QQuickItem(*(
new QQuickCanvasItemPrivate), parent)
261 setFlag(ItemHasContents);
266 Q_D(QQuickCanvasItem);
268 if (d->textureProvider)
269 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
273
274
275
276
280 return d_func()->available;
284
285
286
287
288
289
290
291
292
293
294
295
296
300 return d_func()->contextType;
305 Q_D(QQuickCanvasItem);
307 if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
311 qmlWarning(
this) <<
"Canvas already initialized with a different context type";
315 d->contextType = contextType;
318 createContext(contextType);
320 emit contextTypeChanged();
324
325
326
327
328
329
330
334 Q_D(
const QQuickCanvasItem);
335 return d->context ? QJSValuePrivate::fromReturnedValue(d->context->v4value()) : QJSValue();
339
340
341
342
343
344
345
346
347
348
349
350
351
354 Q_D(
const QQuickCanvasItem);
355 return d->canvasSize;
360 Q_D(QQuickCanvasItem);
361 if (d->canvasSize != size) {
362 d->hasCanvasSize =
true;
363 d->canvasSize = size;
364 emit canvasSizeChanged();
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
390 Q_D(
const QQuickCanvasItem);
396 Q_D(QQuickCanvasItem);
397 if (d->tileSize != size) {
398 d->hasTileSize =
true;
401 emit tileSizeChanged();
409
410
411
412
413
414
415
416
417
418
419
420
421
422
425 Q_D(
const QQuickCanvasItem);
426 return d->canvasWindow;
431 Q_D(QQuickCanvasItem);
432 if (d->canvasWindow != rect) {
433 d->canvasWindow = rect;
435 d->hasCanvasWindow =
true;
436 emit canvasWindowChanged();
444
445
446
447
448
449
450
451
452
453
454
455
456
457
460 Q_D(
const QQuickCanvasItem);
461 return d->renderTarget;
466 Q_D(QQuickCanvasItem);
467 if (d->renderTarget != target) {
469 qmlWarning(
this) <<
"Canvas:renderTarget not changeble once context is active.";
473 d->renderTarget = target;
474 emit renderTargetChanged();
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
503 return d_func()->renderStrategy;
508 Q_D(QQuickCanvasItem);
509 if (d->renderStrategy != strategy) {
511 qmlWarning(
this) <<
"Canvas:renderStrategy not changeable once context is active.";
514 d->renderStrategy = strategy;
515 emit renderStrategyChanged();
521 return d_func()->context;
526 IS_SIGNAL_CONNECTED(
this, QQuickCanvasItem, paint, (
const QRect &));
531 Q_D(QQuickCanvasItem);
534 connect(
this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
535 QMetaObject::invokeMethod(
this,
"availableChanged", Qt::QueuedConnection);
537 if (!d->contextType.isNull())
538 QMetaObject::invokeMethod(
this,
"delayedCreate", Qt::QueuedConnection);
539 else if (isPaintConnected())
540 QMetaObject::invokeMethod(
this,
"requestPaint", Qt::QueuedConnection);
545 Q_D(QQuickCanvasItem);
547 QQuickItem::geometryChange(newGeometry, oldGeometry);
551 QSizeF newSize = QSizeF(width(), height());
552 if (!d->hasCanvasSize && d->canvasSize != newSize) {
553 d->canvasSize = newSize;
554 emit canvasSizeChanged();
557 if (!d->hasTileSize && d->tileSize != newSize) {
558 d->tileSize = newSize.toSize();
559 emit tileSizeChanged();
562 const QRectF rect = QRectF(QPointF(0, 0), newSize);
564 if (!d->hasCanvasWindow && d->canvasWindow != rect) {
565 d->canvasWindow = rect;
566 emit canvasWindowChanged();
569 if (d->available && newSize != oldGeometry.size()) {
570 if (isVisible() || (d->extra.isAllocated() && d->extra->effectRefCount > 0))
577 Q_D(QQuickCanvasItem);
581 d->context =
nullptr;
584 if (d->textureProvider) {
585 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
586 d->textureProvider =
nullptr;
588 if (d->nodeTexture) {
589 QQuickWindowQObjectCleanupJob::schedule(window(), d->nodeTexture);
590 d->nodeTexture =
nullptr;
596 switch (event->type()) {
597 case QEvent::PolishRequest:
601 return QQuickItem::event(event);
607 Q_D(QQuickCanvasItem);
609 d->context->deleteLater();
610 d->context =
nullptr;
612 delete d->textureProvider;
613 d->textureProvider =
nullptr;
614 delete d->nodeTexture;
615 d->nodeTexture =
nullptr;
624 auto polishRequestEvent =
new QEvent(QEvent::PolishRequest);
625 QCoreApplication::postEvent(
this, polishRequestEvent);
630 QQuickItem::componentComplete();
632 Q_D(QQuickCanvasItem);
633 d->baseUrl = qmlEngine(
this)->contextForObject(
this)->baseUrl();
638 QQuickItem::itemChange(change, value);
639 if (change != QQuickItem::ItemSceneChange)
642 Q_D(QQuickCanvasItem);
644 if (d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask)
649 if (value.window==
nullptr)
652 d->window = value.window;
653 QSGRenderContext *context = QQuickWindowPrivate::get(d->window)->context;
656 if (context !=
nullptr && (d->renderTarget != FramebufferObject || context->isValid())) {
660 QMetaObject::invokeMethod(
this,
"sceneGraphInitialized", Qt::QueuedConnection);
662 connect(d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
668 QQuickItem::updatePolish();
670 Q_D(QQuickCanvasItem);
671 if (d->context && d->renderStrategy != QQuickCanvasItem::Cooperative)
672 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth, antialiasing());
674 if (d->animationCallbacks.size() > 0 && isVisible()) {
675 QMap<
int, QV4::PersistentValue> animationCallbacks = d->animationCallbacks;
676 d->animationCallbacks.clear();
678 QV4::ExecutionEngine *v4 = qmlEngine(
this)->handle();
679 QV4::Scope scope(v4);
680 QV4::ScopedFunctionObject function(scope);
681 QV4::JSCallArguments jsCall(scope, 1);
682 *jsCall.thisObject = QV4::QObjectWrapper::wrap(v4,
this);
684 for (
auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
685 function = it.value().value();
686 jsCall.args[0] = QV4::Value::fromUInt32(QDateTime::currentMSecsSinceEpoch());
687 function->call(jsCall);
691 if (d->dirtyRect.isValid()) {
692 if (d->hasTileSize && d->hasCanvasWindow)
693 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
695 emit paint(d->dirtyRect.toRect());
696 d->dirtyRect = QRectF();
701 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
710 Q_D(QQuickCanvasItem);
712 if (!d->context || d->canvasWindow.size().isEmpty()) {
713 if (d->textureProvider) {
714 d->textureProvider->tex =
nullptr;
715 d->textureProvider->fireTextureChanged();
721 QSGInternalImageNode *node =
static_cast<QSGInternalImageNode *>(oldNode);
723 QSGRenderContext *rc = QQuickWindowPrivate::get(window())->context;
724 node = rc->sceneGraphContext()->createInternalImageNode(rc);
730 node->setFiltering(QSGTexture::Linear);
732 node->setFiltering(QSGTexture::Nearest);
734 if (d->renderStrategy == QQuickCanvasItem::Cooperative) {
735 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth, antialiasing());
739 QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(d->context);
740 QQuickContext2DTexture *factory = ctx->texture();
741 QSGTexture *texture = factory->textureForNextFrame(d->nodeTexture, window());
745 d->nodeTexture =
nullptr;
746 if (d->textureProvider) {
747 d->textureProvider->tex =
nullptr;
748 d->textureProvider->fireTextureChanged();
753 d->nodeTexture = texture;
754 node->setTexture(texture);
755 node->setTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
756 node->setInnerTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
759 if (d->textureProvider) {
760 d->textureProvider->tex = d->nodeTexture;
761 d->textureProvider->fireTextureChanged();
776 if (QQuickItem::isTextureProvider())
777 return QQuickItem::textureProvider();
779 Q_D(
const QQuickCanvasItem);
781 QQuickWindow *w = window();
782 if (!w || !w->isSceneGraphInitialized()
783 || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) {
784 qWarning(
"QQuickCanvasItem::textureProvider: can only be queried on the rendering thread of an exposed window");
788 if (!d->textureProvider)
789 d->textureProvider =
new QQuickCanvasTextureProvider;
790 d->textureProvider->tex = d->nodeTexture;
791 return d->textureProvider;
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
815 Q_D(QQuickCanvasItem);
817 QV4::Scope scope(args->v4engine());
818 QV4::ScopedString str(scope, (*args)[0]);
820 qmlWarning(
this) <<
"getContext should be called with a string naming the required context type";
821 args->setReturnValue(QV4::Encode::null());
826 qmlWarning(
this) <<
"Unable to use getContext() at this time, please wait for available: true";
827 args->setReturnValue(QV4::Encode::null());
831 QString contextId = str->toQString();
833 if (d->context !=
nullptr) {
834 if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
835 args->setReturnValue(d->context->v4value());
839 qmlWarning(
this) <<
"Canvas already initialized with a different context type";
840 args->setReturnValue(QV4::Encode::null());
844 if (createContext(contextId))
845 args->setReturnValue(d->context->v4value());
847 args->setReturnValue(QV4::Encode::null());
851
852
853
854
855
859 QV4::Scope scope(args->v4engine());
860 QV4::ScopedFunctionObject f(scope, (*args)[0]);
862 qmlWarning(
this) <<
"requestAnimationFrame should be called with an animation callback function";
863 args->setReturnValue(QV4::Encode::null());
867 Q_D(QQuickCanvasItem);
871 d->animationCallbacks.insert(++id, QV4::PersistentValue(scope.engine, f->asReturnedValue()));
877 args->setReturnValue(QV4::Encode(id));
881
882
883
884
888 QV4::Scope scope(args->v4engine());
889 QV4::ScopedValue v(scope, (*args)[0]);
890 if (!v->isInteger()) {
891 qmlWarning(
this) <<
"cancelRequestAnimationFrame should be called with an animation callback id";
892 args->setReturnValue(QV4::Encode::null());
896 d_func()->animationCallbacks.remove(v->integerValue());
901
902
903
904
905
906
910 markDirty(d_func()->canvasWindow);
914
915
916
917
918
919
920
924 Q_D(QQuickCanvasItem);
928 d->dirtyRect |= rect;
935 if (d_func()->animationCallbacks.size() > 0 && isVisible())
940
941
942
943
944
945
946
947
948
949
950
951
952
953
956 Q_D(
const QQuickCanvasItem);
957 QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
958 return toImage(QRectF(QPointF(0, 0), imageSize)).save(url.toLocalFile());
963 Q_D(QQuickCanvasItem);
964 QUrl fullPathUrl = d->baseUrl.resolved(url);
965 if (!d->pixmaps.contains(fullPathUrl)) {
966 loadImage(url, sourceSize);
968 return d->pixmaps.value(fullPathUrl);
972
973
974
975
976
977
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
998 Q_D(QQuickCanvasItem);
999 QUrl fullPathUrl = d->baseUrl.resolved(url);
1000 if (!d->pixmaps.contains(fullPathUrl)) {
1001 QQuickPixmap* pix =
new QQuickPixmap();
1002 QQmlRefPointer<QQuickCanvasPixmap> canvasPix;
1003 canvasPix.adopt(
new QQuickCanvasPixmap
(pix
));
1004 d->pixmaps.insert(fullPathUrl, canvasPix);
1006 pix->load(qmlEngine(
this)
1009 , sourceSize.toSize()
1010 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
1011 if (pix->isLoading())
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1028 Q_D(QQuickCanvasItem);
1029 d->pixmaps.remove(d->baseUrl.resolved(url));
1033
1034
1035
1036
1037
1038
1041 Q_D(
const QQuickCanvasItem);
1042 QUrl fullPathUrl = d->baseUrl.resolved(url);
1043 return d->pixmaps.contains(fullPathUrl)
1044 && d->pixmaps.value(fullPathUrl)->pixmap()->isError();
1048
1049
1050
1051
1052
1055 Q_D(
const QQuickCanvasItem);
1056 QUrl fullPathUrl = d->baseUrl.resolved(url);
1057 return d->pixmaps.contains(fullPathUrl)
1058 && d->pixmaps.value(fullPathUrl)->pixmap()->isLoading();
1061
1062
1063
1064
1065
1068 Q_D(
const QQuickCanvasItem);
1069 QUrl fullPathUrl = d->baseUrl.resolved(url);
1070 return d->pixmaps.contains(fullPathUrl)
1071 && d->pixmaps.value(fullPathUrl)->pixmap()->isReady();
1075
1076
1077
1078
1082 Q_D(
const QQuickCanvasItem);
1087 const QRectF &rectSource = rect.isEmpty() ? canvasWindow() : rect;
1088 const qreal dpr = window() && rect.isEmpty() ? window()->effectiveDevicePixelRatio() : qreal(1);
1089 const QRectF rectScaled(rectSource.topLeft() * dpr, rectSource.size() * dpr);
1091 QImage image = d->context->toImage(rectScaled);
1092 image.setDevicePixelRatio(dpr);
1098 const QLatin1String imagePrefix(
"image/");
1099 if (!mime.startsWith(imagePrefix))
1101 const QStringView mimeExt = QStringView{mime}.mid(imagePrefix.size());
1102 if (mimeExt == QLatin1String(
"png"))
1104 else if (mimeExt == QLatin1String(
"bmp"))
1106 else if (mimeExt == QLatin1String(
"jpeg"))
1108 else if (mimeExt == QLatin1String(
"x-portable-pixmap"))
1110 else if (mimeExt == QLatin1String(
"tiff"))
1112 else if (mimeExt == QLatin1String(
"xpm"))
1118
1119
1120
1121
1122
1123
1124
1125
1128 QImage image = toImage();
1130 if (!image.isNull()) {
1132 QBuffer buffer(&ba);
1133 buffer.open(QIODevice::WriteOnly);
1134 const QString mime = mimeType.toLower();
1135 const char* type = mimeToType(mime);
1137 return QStringLiteral(
"data:,");
1139 image.save(&buffer, type);
1141 return QLatin1String(
"data:") + mime + QLatin1String(
";base64,") + QLatin1String(ba.toBase64().constData());
1143 return QStringLiteral(
"data:,");
1148 Q_D(QQuickCanvasItem);
1150 if (!d->context && !d->contextType.isNull())
1151 createContext(d->contextType);
1158 Q_D(QQuickCanvasItem);
1163 if (contextType == QLatin1String(
"2d")) {
1164 if (d->contextType.compare(QLatin1String(
"2d"), Qt::CaseInsensitive) != 0) {
1165 d->contextType = QLatin1String(
"2d");
1166 emit contextTypeChanged();
1168 initializeContext(
new QQuickContext2D(
this));
1177 Q_D(QQuickCanvasItem);
1179 d->context = context;
1180 d->context->init(
this, args);
1181 d->context->setV4Engine(qmlEngine(
this)->handle());
1182 connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
1183 connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted()));
1184 emit contextChanged();
1187QRect
QQuickCanvasItem::tiledRect(
const QRectF &window,
const QSize &tileSize)
1189 if (window.isEmpty())
1192 const int tw = tileSize.width();
1193 const int th = tileSize.height();
1194 const int h1 = window.left() / tw;
1195 const int v1 = window.top() / th;
1197 const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1198 const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1200 return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1204
1205
1206
1207
1208
1209
1210
1211
1214
1215
1216
1217
1218
1222#include "moc_qquickcanvasitem_p.cpp"
QQuickCanvasItemPrivate()
QSGInternalImageNode * node
~QQuickCanvasItemPrivate()
QQuickCanvasItem::RenderTarget renderTarget
QMap< int, QV4::PersistentValue > animationCallbacks
QQuickCanvasItem::RenderStrategy renderStrategy
QQuickCanvasContext * context
QQuickCanvasTextureProvider * textureProvider
void setTileSize(const QSize &)
void setCanvasWindow(const QRectF &rect)
bool event(QEvent *event) override
This virtual function receives events to an object and should return true if the event e was recogniz...
bool isTextureProvider() const override
Returns true if this item is a texture provider.
bool isImageLoaded(const QUrl &url) const
\qmlmethod QtQuick::Canvas::isImageLoaded(url image) Returns true if the image is successfully loaded...
void setCanvasSize(const QSizeF &)
QQmlRefPointer< QQuickCanvasPixmap > loadedPixmap(const QUrl &url, QSizeF sourceSize=QSizeF())
Q_INVOKABLE void requestAnimationFrame(QQmlV4FunctionPtr args)
\qmlmethod int QtQuick::Canvas::requestAnimationFrame(callback)
void updatePolish() override
This function should perform any layout as required for this item.
QQuickCanvasContext * rawContext() const
RenderStrategy renderStrategy() const
\qmlproperty enumeration QtQuick::Canvas::renderStrategy Holds the current canvas rendering strategy.
QSGTextureProvider * textureProvider() const override
Returns the texture provider for an item.
RenderTarget renderTarget() const
\qmlproperty enumeration QtQuick::Canvas::renderTarget Holds the current canvas render target.
void setContextType(const QString &contextType)
bool isAvailable() const
\qmlproperty bool QtQuick::Canvas::available
Q_INVOKABLE bool save(const QString &filename, const QSizeF &imageSize=QSizeF()) const
\qmlmethod bool QtQuick::Canvas::save(string filename, size imageSize = undefined)
QRectF canvasWindow() const
\qmlproperty rect QtQuick::Canvas::canvasWindow Holds the current canvas visible window.
void unloadImage(const QUrl &url)
\qmlmethod QtQuick::Canvas::unloadImage(url image)
bool isImageError(const QUrl &url) const
\qmlmethod QtQuick::Canvas::isImageError(url image)
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
QJSValue context() const
\qmlproperty object QtQuick::Canvas::context Holds the active drawing context.
QImage toImage(const QRectF &rect=QRectF()) const
QSize tileSize() const
\qmlproperty size QtQuick::Canvas::tileSize Holds the canvas rendering tile size.
QSGNode * updatePaintNode(QSGNode *, UpdatePaintNodeData *) override
Called on the render thread when it is time to sync the state of the item with the scene graph.
QSizeF canvasSize() const
\qmlproperty size QtQuick::Canvas::canvasSize Holds the logical canvas size that the context paints o...
void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &) override
Called when change occurs for this item.
void releaseResources() override
This function is called when an item should release graphics resources which are not already managed ...
Q_INVOKABLE void cancelRequestAnimationFrame(QQmlV4FunctionPtr args)
\qmlmethod QtQuick::Canvas::cancelRequestAnimationFrame(int handle)
Q_INVOKABLE void getContext(QQmlV4FunctionPtr args)
\qmlmethod object QtQuick::Canvas::getContext(string contextId, ... args)
bool isImageLoading(const QUrl &url) const
\qmlmethod QtQuick::Canvas::isImageLoading(url image) Returns true if the image is currently loading.
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
Q_INVOKABLE void markDirty(const QRectF &dirtyRect=QRectF())
\qmlmethod QtQuick::Canvas::markDirty(rect area)
QString contextType() const
\qmlproperty string QtQuick::Canvas::contextType The type of drawing context to use.
Q_INVOKABLE void requestPaint()
\qmlmethod QtQuick::Canvas::requestPaint()
QQuickCanvasPixmap(const QImage &image)
QQuickCanvasPixmap(QQuickPixmap *pixmap)
QSGTexture * texture() const override
Returns a pointer to the texture object.
void fireTextureChanged()
static QQuickContext2DRenderThread * instance(QQmlEngine *engine)
~QQuickContext2DRenderThread()
Combined button and popup list for selecting options.
static const char * mimeToType(const QString &mime)