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);
959 url.setPath(filename);
960 url = d->baseUrl.resolved(url);
961 return toImage(QRectF(QPointF(0, 0), imageSize)).save(url.toLocalFile());
966 Q_D(QQuickCanvasItem);
967 QUrl fullPathUrl = d->baseUrl.resolved(url);
968 if (!d->pixmaps.contains(fullPathUrl)) {
969 loadImage(url, sourceSize);
971 return d->pixmaps.value(fullPathUrl);
975
976
977
978
979
980
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
1001 Q_D(QQuickCanvasItem);
1002 QUrl fullPathUrl = d->baseUrl.resolved(url);
1003 if (!d->pixmaps.contains(fullPathUrl)) {
1004 QQuickPixmap* pix =
new QQuickPixmap();
1005 QQmlRefPointer<QQuickCanvasPixmap> canvasPix;
1006 canvasPix.adopt(
new QQuickCanvasPixmap
(pix
));
1007 d->pixmaps.insert(fullPathUrl, canvasPix);
1009 pix->load(qmlEngine(
this)
1012 , sourceSize.toSize()
1013 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
1014 if (pix->isLoading())
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1031 Q_D(QQuickCanvasItem);
1032 d->pixmaps.remove(d->baseUrl.resolved(url));
1036
1037
1038
1039
1040
1041
1044 Q_D(
const QQuickCanvasItem);
1045 QUrl fullPathUrl = d->baseUrl.resolved(url);
1046 return d->pixmaps.contains(fullPathUrl)
1047 && d->pixmaps.value(fullPathUrl)->pixmap()->isError();
1051
1052
1053
1054
1055
1058 Q_D(
const QQuickCanvasItem);
1059 QUrl fullPathUrl = d->baseUrl.resolved(url);
1060 return d->pixmaps.contains(fullPathUrl)
1061 && d->pixmaps.value(fullPathUrl)->pixmap()->isLoading();
1064
1065
1066
1067
1068
1071 Q_D(
const QQuickCanvasItem);
1072 QUrl fullPathUrl = d->baseUrl.resolved(url);
1073 return d->pixmaps.contains(fullPathUrl)
1074 && d->pixmaps.value(fullPathUrl)->pixmap()->isReady();
1078
1079
1080
1081
1085 Q_D(
const QQuickCanvasItem);
1090 const QRectF &rectSource = rect.isEmpty() ? canvasWindow() : rect;
1091 const qreal dpr = window() && rect.isEmpty() ? window()->effectiveDevicePixelRatio() : qreal(1);
1092 const QRectF rectScaled(rectSource.topLeft() * dpr, rectSource.size() * dpr);
1094 QImage image = d->context->toImage(rectScaled);
1095 image.setDevicePixelRatio(dpr);
1101 const QLatin1String imagePrefix(
"image/");
1102 if (!mime.startsWith(imagePrefix))
1104 const QStringView mimeExt = QStringView{mime}.mid(imagePrefix.size());
1105 if (mimeExt == QLatin1String(
"png"))
1107 else if (mimeExt == QLatin1String(
"bmp"))
1109 else if (mimeExt == QLatin1String(
"jpeg"))
1111 else if (mimeExt == QLatin1String(
"x-portable-pixmap"))
1113 else if (mimeExt == QLatin1String(
"tiff"))
1115 else if (mimeExt == QLatin1String(
"xpm"))
1121
1122
1123
1124
1125
1126
1127
1128
1131 QImage image = toImage();
1133 if (!image.isNull()) {
1135 QBuffer buffer(&ba);
1136 buffer.open(QIODevice::WriteOnly);
1137 const QString mime = mimeType.toLower();
1138 const char* type = mimeToType(mime);
1140 return QStringLiteral(
"data:,");
1142 image.save(&buffer, type);
1144 return QLatin1String(
"data:") + mime + QLatin1String(
";base64,") + QLatin1String(ba.toBase64().constData());
1146 return QStringLiteral(
"data:,");
1151 Q_D(QQuickCanvasItem);
1153 if (!d->context && !d->contextType.isNull())
1154 createContext(d->contextType);
1161 Q_D(QQuickCanvasItem);
1166 if (contextType == QLatin1String(
"2d")) {
1167 if (d->contextType.compare(QLatin1String(
"2d"), Qt::CaseInsensitive) != 0) {
1168 d->contextType = QLatin1String(
"2d");
1169 emit contextTypeChanged();
1171 initializeContext(
new QQuickContext2D(
this));
1180 Q_D(QQuickCanvasItem);
1182 d->context = context;
1183 d->context->init(
this, args);
1184 d->context->setV4Engine(qmlEngine(
this)->handle());
1185 connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
1186 connect(d->context, SIGNAL(textureChanged()), SIGNAL(
painted()));
1187 emit contextChanged();
1190QRect
QQuickCanvasItem::tiledRect(
const QRectF &window,
const QSize &tileSize)
1192 if (window.isEmpty())
1195 const int tw = tileSize.width();
1196 const int th = tileSize.height();
1197 const int h1 = window.left() / tw;
1198 const int v1 = window.top() / th;
1200 const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1201 const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1203 return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1207
1208
1209
1210
1211
1212
1213
1214
1217
1218
1219
1220
1221
1225#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)