5#include <private/qsgadaptationlayer_p.h>
7#include <private/qquickitem_p.h>
8#include <private/qquickcanvascontext_p.h>
9#include <private/qquickcontext2d_p.h>
10#include <private/qquickcontext2dtexture_p.h>
11#include <private/qsgadaptationlayer_p.h>
12#include <qsgtextureprovider.h>
13#include <QtQuick/private/qquickpixmap_p.h>
14#include <QtGui/QGuiApplication>
15#include <qsgtextureprovider.h>
18#include <private/qqmlengine_p.h>
19#include <QtCore/QBuffer>
20#include <QtCore/qdatetime.h>
22#include <private/qv4value_p.h>
23#include <private/qv4functionobject_p.h>
24#include <private/qv4scopedvalue_p.h>
25#include <private/qv4jscall_p.h>
26#include <private/qv4qobjectwrapper_p.h>
27#include <private/qjsvalue_p.h>
60 return m_pixmap->width();
62 return m_image.width();
68 return m_pixmap->height();
70 return m_image.height();
76 return m_pixmap->isReady();
77 return !m_image.isNull();
82 if (m_image.isNull() && m_pixmap)
83 m_image = m_pixmap->image();
92 : QThread(eng), m_engine(eng), m_eventLoopQuitHack(
nullptr)
95 m_eventLoopQuitHack =
new QObject;
96 m_eventLoopQuitHack->moveToThread(
this);
97 connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
98 start(QThread::IdlePriority);
103 renderThreadsMutex.lock();
104 renderThreads.remove(m_engine);
105 renderThreadsMutex.unlock();
107 m_eventLoopQuitHack->deleteLater();
114 renderThreadsMutex.lock();
115 if (renderThreads.contains(engine))
116 thread = renderThreads.value(engine);
118 thread =
new QQuickContext2DRenderThread(engine);
119 renderThreads.insert(engine, thread);
121 renderThreadsMutex.unlock();
165 implicitAntialiasing =
true;
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
257
260 : QQuickItem(*(
new QQuickCanvasItemPrivate), parent)
262 setFlag(ItemHasContents);
267 Q_D(QQuickCanvasItem);
269 if (d->textureProvider)
270 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
274
275
276
277
281 return d_func()->available;
285
286
287
288
289
290
291
292
293
294
295
296
297
301 return d_func()->contextType;
306 Q_D(QQuickCanvasItem);
308 if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
312 qmlWarning(
this) <<
"Canvas already initialized with a different context type";
316 d->contextType = contextType;
319 createContext(contextType);
321 emit contextTypeChanged();
325
326
327
328
329
330
331
335 Q_D(
const QQuickCanvasItem);
336 return d->context ? QJSValuePrivate::fromReturnedValue(d->context->v4value()) : QJSValue();
340
341
342
343
344
345
346
347
348
349
350
351
352
355 Q_D(
const QQuickCanvasItem);
356 return d->canvasSize;
361 Q_D(QQuickCanvasItem);
362 if (d->canvasSize != size) {
363 d->hasCanvasSize =
true;
364 d->canvasSize = size;
365 emit canvasSizeChanged();
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
391 Q_D(
const QQuickCanvasItem);
397 Q_D(QQuickCanvasItem);
398 if (d->tileSize != size) {
399 d->hasTileSize =
true;
402 emit tileSizeChanged();
410
411
412
413
414
415
416
417
418
419
420
421
422
423
426 Q_D(
const QQuickCanvasItem);
427 return d->canvasWindow;
432 Q_D(QQuickCanvasItem);
433 if (d->canvasWindow != rect) {
434 d->canvasWindow = rect;
436 d->hasCanvasWindow =
true;
437 emit canvasWindowChanged();
445
446
447
448
449
450
451
452
453
454
455
456
457
458
461 Q_D(
const QQuickCanvasItem);
462 return d->renderTarget;
467 Q_D(QQuickCanvasItem);
468 if (d->renderTarget != target) {
470 qmlWarning(
this) <<
"Canvas:renderTarget not changeble once context is active.";
474 d->renderTarget = target;
475 emit renderTargetChanged();
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
504 return d_func()->renderStrategy;
509 Q_D(QQuickCanvasItem);
510 if (d->renderStrategy != strategy) {
512 qmlWarning(
this) <<
"Canvas:renderStrategy not changeable once context is active.";
515 d->renderStrategy = strategy;
516 emit renderStrategyChanged();
522 return d_func()->context;
527 IS_SIGNAL_CONNECTED(
this, QQuickCanvasItem, paint, (
const QRect &));
532 Q_D(QQuickCanvasItem);
535 connect(
this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
536 QMetaObject::invokeMethod(
this,
"availableChanged", Qt::QueuedConnection);
538 if (!d->contextType.isNull())
539 QMetaObject::invokeMethod(
this,
"delayedCreate", Qt::QueuedConnection);
540 else if (isPaintConnected())
541 QMetaObject::invokeMethod(
this,
"requestPaint", Qt::QueuedConnection);
546 Q_D(QQuickCanvasItem);
548 QQuickItem::geometryChange(newGeometry, oldGeometry);
552 QSizeF newSize = QSizeF(width(), height());
553 if (!d->hasCanvasSize && d->canvasSize != newSize) {
554 d->canvasSize = newSize;
555 emit canvasSizeChanged();
558 if (!d->hasTileSize && d->tileSize != newSize) {
559 d->tileSize = newSize.toSize();
560 emit tileSizeChanged();
563 const QRectF rect = QRectF(QPointF(0, 0), newSize);
565 if (!d->hasCanvasWindow && d->canvasWindow != rect) {
566 d->canvasWindow = rect;
567 emit canvasWindowChanged();
570 if (d->available && newSize != oldGeometry.size()) {
571 if (isVisible() || (d->extra.isAllocated() && d->extra->effectRefCount > 0))
578 Q_D(QQuickCanvasItem);
582 d->context =
nullptr;
585 if (d->textureProvider) {
586 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
587 d->textureProvider =
nullptr;
589 if (d->nodeTexture) {
590 QQuickWindowQObjectCleanupJob::schedule(window(), d->nodeTexture);
591 d->nodeTexture =
nullptr;
597 switch (event->type()) {
598 case QEvent::PolishRequest:
602 return QQuickItem::event(event);
608 Q_D(QQuickCanvasItem);
610 d->context->deleteLater();
611 d->context =
nullptr;
613 delete d->textureProvider;
614 d->textureProvider =
nullptr;
615 delete d->nodeTexture;
616 d->nodeTexture =
nullptr;
625 auto polishRequestEvent =
new QEvent(QEvent::PolishRequest);
626 QCoreApplication::postEvent(
this, polishRequestEvent);
631 QQuickItem::componentComplete();
633 Q_D(QQuickCanvasItem);
634 d->baseUrl = qmlEngine(
this)->contextForObject(
this)->baseUrl();
639 QQuickItem::itemChange(change, value);
640 if (change != QQuickItem::ItemSceneChange)
643 Q_D(QQuickCanvasItem);
645 if (d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask)
650 if (value.window==
nullptr)
653 d->window = value.window;
654 QSGRenderContext *context = QQuickWindowPrivate::get(d->window)->context;
657 if (context !=
nullptr && (d->renderTarget != FramebufferObject || context->isValid())) {
661 QMetaObject::invokeMethod(
this,
"sceneGraphInitialized", Qt::QueuedConnection);
663 connect(d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
669 QQuickItem::updatePolish();
671 Q_D(QQuickCanvasItem);
672 if (d->context && d->renderStrategy != QQuickCanvasItem::Cooperative)
673 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth, antialiasing());
675 if (d->animationCallbacks.size() > 0 && isVisible()) {
676 QMap<
int, QV4::PersistentValue> animationCallbacks = d->animationCallbacks;
677 d->animationCallbacks.clear();
679 QV4::ExecutionEngine *v4 = qmlEngine(
this)->handle();
680 QV4::Scope scope(v4);
681 QV4::ScopedFunctionObject function(scope);
682 QV4::JSCallArguments jsCall(scope, 1);
683 *jsCall.thisObject = QV4::QObjectWrapper::wrap(v4,
this);
685 for (
auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
686 function = it.value().value();
687 jsCall.args[0] = QV4::Value::fromUInt32(QDateTime::currentMSecsSinceEpoch());
688 function->call(jsCall);
692 if (d->dirtyRect.isValid()) {
693 if (d->hasTileSize && d->hasCanvasWindow)
694 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
696 emit paint(d->dirtyRect.toRect());
697 d->dirtyRect = QRectF();
702 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
711 Q_D(QQuickCanvasItem);
713 if (!d->context || d->canvasWindow.size().isEmpty()) {
714 if (d->textureProvider) {
715 d->textureProvider->tex =
nullptr;
716 d->textureProvider->fireTextureChanged();
722 QSGInternalImageNode *node =
static_cast<QSGInternalImageNode *>(oldNode);
724 QSGRenderContext *rc = QQuickWindowPrivate::get(window())->context;
725 node = rc->sceneGraphContext()->createInternalImageNode(rc);
731 node->setFiltering(QSGTexture::Linear);
733 node->setFiltering(QSGTexture::Nearest);
735 if (d->renderStrategy == QQuickCanvasItem::Cooperative) {
736 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth, antialiasing());
740 QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(d->context);
741 QQuickContext2DTexture *factory = ctx->texture();
742 QSGTexture *texture = factory->textureForNextFrame(d->nodeTexture, window());
746 d->nodeTexture =
nullptr;
747 if (d->textureProvider) {
748 d->textureProvider->tex =
nullptr;
749 d->textureProvider->fireTextureChanged();
754 d->nodeTexture = texture;
755 node->setTexture(texture);
756 node->setTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
757 node->setInnerTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
760 if (d->textureProvider) {
761 d->textureProvider->tex = d->nodeTexture;
762 d->textureProvider->fireTextureChanged();
777 if (QQuickItem::isTextureProvider())
778 return QQuickItem::textureProvider();
780 Q_D(
const QQuickCanvasItem);
782 QQuickWindow *w = window();
783 if (!w || !w->isSceneGraphInitialized()
784 || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) {
785 qWarning(
"QQuickCanvasItem::textureProvider: can only be queried on the rendering thread of an exposed window");
789 if (!d->textureProvider)
790 d->textureProvider =
new QQuickCanvasTextureProvider;
791 d->textureProvider->tex = d->nodeTexture;
792 return d->textureProvider;
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
816 Q_D(QQuickCanvasItem);
818 QV4::Scope scope(args->v4engine());
819 QV4::ScopedString str(scope, (*args)[0]);
821 qmlWarning(
this) <<
"getContext should be called with a string naming the required context type";
822 args->setReturnValue(QV4::Encode::null());
827 qmlWarning(
this) <<
"Unable to use getContext() at this time, please wait for available: true";
828 args->setReturnValue(QV4::Encode::null());
832 QString contextId = str->toQString();
834 if (d->context !=
nullptr) {
835 if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
836 args->setReturnValue(d->context->v4value());
840 qmlWarning(
this) <<
"Canvas already initialized with a different context type";
841 args->setReturnValue(QV4::Encode::null());
845 if (createContext(contextId))
846 args->setReturnValue(d->context->v4value());
848 args->setReturnValue(QV4::Encode::null());
852
853
854
855
856
860 QV4::Scope scope(args->v4engine());
861 QV4::ScopedFunctionObject f(scope, (*args)[0]);
863 qmlWarning(
this) <<
"requestAnimationFrame should be called with an animation callback function";
864 args->setReturnValue(QV4::Encode::null());
868 Q_D(QQuickCanvasItem);
872 d->animationCallbacks.insert(++id, QV4::PersistentValue(scope.engine, f->asReturnedValue()));
878 args->setReturnValue(QV4::Encode(id));
882
883
884
885
889 QV4::Scope scope(args->v4engine());
890 QV4::ScopedValue v(scope, (*args)[0]);
891 if (!v->isInteger()) {
892 qmlWarning(
this) <<
"cancelRequestAnimationFrame should be called with an animation callback id";
893 args->setReturnValue(QV4::Encode::null());
897 d_func()->animationCallbacks.remove(v->integerValue());
902
903
904
905
906
907
911 markDirty(d_func()->canvasWindow);
915
916
917
918
919
920
921
925 Q_D(QQuickCanvasItem);
929 d->dirtyRect |= rect;
936 if (d_func()->animationCallbacks.size() > 0 && isVisible())
941
942
943
944
945
946
947
948
949
950
951
952
953
954
957 Q_D(
const QQuickCanvasItem);
958 QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
959 return toImage(QRectF(QPointF(0, 0), imageSize)).save(url.toLocalFile());
964 Q_D(QQuickCanvasItem);
965 QUrl fullPathUrl = d->baseUrl.resolved(url);
966 if (!d->pixmaps.contains(fullPathUrl)) {
967 loadImage(url, sourceSize);
969 return d->pixmaps.value(fullPathUrl);
973
974
975
976
977
978
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
999 Q_D(QQuickCanvasItem);
1000 QUrl fullPathUrl = d->baseUrl.resolved(url);
1001 if (!d->pixmaps.contains(fullPathUrl)) {
1002 QQuickPixmap* pix =
new QQuickPixmap();
1003 QQmlRefPointer<QQuickCanvasPixmap> canvasPix;
1004 canvasPix.adopt(
new QQuickCanvasPixmap
(pix
));
1005 d->pixmaps.insert(fullPathUrl, canvasPix);
1007 pix->load(qmlEngine(
this)
1010 , sourceSize.toSize()
1011 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
1012 if (pix->isLoading())
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1029 Q_D(QQuickCanvasItem);
1030 d->pixmaps.remove(d->baseUrl.resolved(url));
1034
1035
1036
1037
1038
1039
1042 Q_D(
const QQuickCanvasItem);
1043 QUrl fullPathUrl = d->baseUrl.resolved(url);
1044 return d->pixmaps.contains(fullPathUrl)
1045 && d->pixmaps.value(fullPathUrl)->pixmap()->isError();
1049
1050
1051
1052
1053
1056 Q_D(
const QQuickCanvasItem);
1057 QUrl fullPathUrl = d->baseUrl.resolved(url);
1058 return d->pixmaps.contains(fullPathUrl)
1059 && d->pixmaps.value(fullPathUrl)->pixmap()->isLoading();
1062
1063
1064
1065
1066
1069 Q_D(
const QQuickCanvasItem);
1070 QUrl fullPathUrl = d->baseUrl.resolved(url);
1071 return d->pixmaps.contains(fullPathUrl)
1072 && d->pixmaps.value(fullPathUrl)->pixmap()->isReady();
1076
1077
1078
1079
1083 Q_D(
const QQuickCanvasItem);
1088 const QRectF &rectSource = rect.isEmpty() ? canvasWindow() : rect;
1089 const qreal dpr = window() && rect.isEmpty() ? window()->effectiveDevicePixelRatio() : qreal(1);
1090 const QRectF rectScaled(rectSource.topLeft() * dpr, rectSource.size() * dpr);
1092 QImage image = d->context->toImage(rectScaled);
1093 image.setDevicePixelRatio(dpr);
1099 const QLatin1String imagePrefix(
"image/");
1100 if (!mime.startsWith(imagePrefix))
1102 const QStringView mimeExt = QStringView{mime}.mid(imagePrefix.size());
1103 if (mimeExt == QLatin1String(
"png"))
1105 else if (mimeExt == QLatin1String(
"bmp"))
1107 else if (mimeExt == QLatin1String(
"jpeg"))
1109 else if (mimeExt == QLatin1String(
"x-portable-pixmap"))
1111 else if (mimeExt == QLatin1String(
"tiff"))
1113 else if (mimeExt == QLatin1String(
"xpm"))
1119
1120
1121
1122
1123
1124
1125
1126
1129 QImage image = toImage();
1131 if (!image.isNull()) {
1133 QBuffer buffer(&ba);
1134 buffer.open(QIODevice::WriteOnly);
1135 const QString mime = mimeType.toLower();
1136 const char* type = mimeToType(mime);
1138 return QStringLiteral(
"data:,");
1140 image.save(&buffer, type);
1142 return QLatin1String(
"data:") + mime + QLatin1String(
";base64,") + QLatin1String(ba.toBase64().constData());
1144 return QStringLiteral(
"data:,");
1149 Q_D(QQuickCanvasItem);
1151 if (!d->context && !d->contextType.isNull())
1152 createContext(d->contextType);
1159 Q_D(QQuickCanvasItem);
1164 if (contextType == QLatin1String(
"2d")) {
1165 if (d->contextType.compare(QLatin1String(
"2d"), Qt::CaseInsensitive) != 0) {
1166 d->contextType = QLatin1String(
"2d");
1167 emit contextTypeChanged();
1169 initializeContext(
new QQuickContext2D(
this));
1178 Q_D(QQuickCanvasItem);
1180 d->context = context;
1181 d->context->init(
this, args);
1182 d->context->setV4Engine(qmlEngine(
this)->handle());
1183 connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
1184 connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted()));
1185 emit contextChanged();
1188QRect
QQuickCanvasItem::tiledRect(
const QRectF &window,
const QSize &tileSize)
1190 if (window.isEmpty())
1193 const int tw = tileSize.width();
1194 const int th = tileSize.height();
1195 const int h1 = window.left() / tw;
1196 const int v1 = window.top() / th;
1198 const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1199 const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1201 return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1205
1206
1207
1208
1209
1210
1211
1212
1215
1216
1217
1218
1219
1223#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()
static const char * mimeToType(const QString &mime)