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>
21#include <QtCore/qdatetime.h>
23#include <private/qv4value_p.h>
24#include <private/qv4functionobject_p.h>
25#include <private/qv4scopedvalue_p.h>
26#include <private/qv4jscall_p.h>
27#include <private/qv4qobjectwrapper_p.h>
28#include <private/qjsvalue_p.h>
61 return m_pixmap->width();
63 return m_image.width();
69 return m_pixmap->height();
71 return m_image.height();
77 return m_pixmap->isReady();
78 return !m_image.isNull();
83 if (m_image.isNull() && m_pixmap)
84 m_image = m_pixmap->image();
93 : QThread(eng), m_engine(eng), m_eventLoopQuitHack(
nullptr)
96 m_eventLoopQuitHack =
new QObject;
97 m_eventLoopQuitHack->moveToThread(
this);
98 connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
99 start(QThread::IdlePriority);
104 renderThreadsMutex.lock();
105 renderThreads.remove(m_engine);
106 renderThreadsMutex.unlock();
108 m_eventLoopQuitHack->deleteLater();
115 renderThreadsMutex.lock();
116 if (renderThreads.contains(engine))
117 thread = renderThreads.value(engine);
119 thread =
new QQuickContext2DRenderThread(engine);
120 renderThreads.insert(engine, thread);
122 renderThreadsMutex.unlock();
166 implicitAntialiasing =
true;
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
258
261 : QQuickItem(*(
new QQuickCanvasItemPrivate), parent)
263 setFlag(ItemHasContents);
268 Q_D(QQuickCanvasItem);
270 if (d->textureProvider)
271 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
275
276
277
278
282 return d_func()->available;
286
287
288
289
290
291
292
293
294
295
296
297
298
302 return d_func()->contextType;
307 Q_D(QQuickCanvasItem);
309 if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
313 qmlWarning(
this) <<
"Canvas already initialized with a different context type";
317 d->contextType = contextType;
320 createContext(contextType);
322 emit contextTypeChanged();
326
327
328
329
330
331
332
336 Q_D(
const QQuickCanvasItem);
337 return d->context ? QJSValuePrivate::fromReturnedValue(d->context->v4value()) : QJSValue();
341
342
343
344
345
346
347
348
349
350
351
352
353
356 Q_D(
const QQuickCanvasItem);
357 return d->canvasSize;
362 Q_D(QQuickCanvasItem);
363 if (d->canvasSize != size) {
364 d->hasCanvasSize =
true;
365 d->canvasSize = size;
366 emit canvasSizeChanged();
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
392 Q_D(
const QQuickCanvasItem);
398 Q_D(QQuickCanvasItem);
399 if (d->tileSize != size) {
400 d->hasTileSize =
true;
403 emit tileSizeChanged();
411
412
413
414
415
416
417
418
419
420
421
422
423
424
427 Q_D(
const QQuickCanvasItem);
428 return d->canvasWindow;
433 Q_D(QQuickCanvasItem);
434 if (d->canvasWindow != rect) {
435 d->canvasWindow = rect;
437 d->hasCanvasWindow =
true;
438 emit canvasWindowChanged();
446
447
448
449
450
451
452
453
454
455
456
457
458
459
462 Q_D(
const QQuickCanvasItem);
463 return d->renderTarget;
468 Q_D(QQuickCanvasItem);
469 if (d->renderTarget != target) {
471 qmlWarning(
this) <<
"Canvas:renderTarget not changeble once context is active.";
475 d->renderTarget = target;
476 emit renderTargetChanged();
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
505 return d_func()->renderStrategy;
510 Q_D(QQuickCanvasItem);
511 if (d->renderStrategy != strategy) {
513 qmlWarning(
this) <<
"Canvas:renderStrategy not changeable once context is active.";
516 d->renderStrategy = strategy;
517 emit renderStrategyChanged();
523 return d_func()->context;
528 IS_SIGNAL_CONNECTED(
this, QQuickCanvasItem, paint, (
const QRect &));
533 Q_D(QQuickCanvasItem);
536 connect(
this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
537 QMetaObject::invokeMethod(
this,
"availableChanged", Qt::QueuedConnection);
539 if (!d->contextType.isNull())
540 QMetaObject::invokeMethod(
this,
"delayedCreate", Qt::QueuedConnection);
541 else if (isPaintConnected())
542 QMetaObject::invokeMethod(
this,
"requestPaint", Qt::QueuedConnection);
547 Q_D(QQuickCanvasItem);
549 QQuickItem::geometryChange(newGeometry, oldGeometry);
553 QSizeF newSize = QSizeF(width(), height());
554 if (!d->hasCanvasSize && d->canvasSize != newSize) {
555 d->canvasSize = newSize;
556 emit canvasSizeChanged();
559 if (!d->hasTileSize && d->tileSize != newSize) {
560 d->tileSize = newSize.toSize();
561 emit tileSizeChanged();
564 const QRectF rect = QRectF(QPointF(0, 0), newSize);
566 if (!d->hasCanvasWindow && d->canvasWindow != rect) {
567 d->canvasWindow = rect;
568 emit canvasWindowChanged();
571 if (d->available && newSize != oldGeometry.size()) {
572 if (isVisible() || (d->extra.isAllocated() && d->extra->effectRefCount > 0))
579 Q_D(QQuickCanvasItem);
583 d->context =
nullptr;
586 if (d->textureProvider) {
587 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
588 d->textureProvider =
nullptr;
590 if (d->nodeTexture) {
591 QQuickWindowQObjectCleanupJob::schedule(window(), d->nodeTexture);
592 d->nodeTexture =
nullptr;
598 switch (event->type()) {
599 case QEvent::PolishRequest:
603 return QQuickItem::event(event);
609 Q_D(QQuickCanvasItem);
611 d->context->deleteLater();
612 d->context =
nullptr;
614 delete d->textureProvider;
615 d->textureProvider =
nullptr;
616 delete d->nodeTexture;
617 d->nodeTexture =
nullptr;
626 auto polishRequestEvent =
new QEvent(QEvent::PolishRequest);
627 QCoreApplication::postEvent(
this, polishRequestEvent);
632 QQuickItem::componentComplete();
634 Q_D(QQuickCanvasItem);
635 d->baseUrl = qmlEngine(
this)->contextForObject(
this)->baseUrl();
640 QQuickItem::itemChange(change, value);
641 if (change != QQuickItem::ItemSceneChange)
644 Q_D(QQuickCanvasItem);
646 if (d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask)
651 if (value.window==
nullptr)
654 d->window = value.window;
655 QSGRenderContext *context = QQuickWindowPrivate::get(d->window)->context;
658 if (context !=
nullptr && (d->renderTarget != FramebufferObject || context->isValid())) {
662 QMetaObject::invokeMethod(
this,
"sceneGraphInitialized", Qt::QueuedConnection);
664 connect(d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
670 QQuickItem::updatePolish();
672 Q_D(QQuickCanvasItem);
673 if (d->context && d->renderStrategy != QQuickCanvasItem::Cooperative)
674 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth, antialiasing());
676 if (d->animationCallbacks.size() > 0 && isVisible()) {
677 QMap<
int, QV4::PersistentValue> animationCallbacks = d->animationCallbacks;
678 d->animationCallbacks.clear();
680 QV4::ExecutionEngine *v4 = qmlEngine(
this)->handle();
681 QV4::Scope scope(v4);
682 QV4::ScopedFunctionObject function(scope);
683 QV4::JSCallArguments jsCall(scope, 1);
684 *jsCall.thisObject = QV4::QObjectWrapper::wrap(v4,
this);
686 for (
auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
687 function = it.value().value();
688 jsCall.args[0] = QV4::Value::fromUInt32(QDateTime::currentMSecsSinceEpoch());
689 function->call(jsCall);
693 if (d->dirtyRect.isValid()) {
694 if (d->hasTileSize && d->hasCanvasWindow)
695 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
697 emit paint(d->dirtyRect.toRect());
698 d->dirtyRect = QRectF();
703 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
712 Q_D(QQuickCanvasItem);
714 if (!d->context || d->canvasWindow.size().isEmpty()) {
715 if (d->textureProvider) {
716 d->textureProvider->tex =
nullptr;
717 d->textureProvider->fireTextureChanged();
723 QSGInternalImageNode *node =
static_cast<QSGInternalImageNode *>(oldNode);
725 QSGRenderContext *rc = QQuickWindowPrivate::get(window())->context;
726 node = rc->sceneGraphContext()->createInternalImageNode(rc);
732 node->setFiltering(QSGTexture::Linear);
734 node->setFiltering(QSGTexture::Nearest);
736 if (d->renderStrategy == QQuickCanvasItem::Cooperative) {
737 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth, antialiasing());
741 QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(d->context);
742 QQuickContext2DTexture *factory = ctx->texture();
743 QSGTexture *texture = factory->textureForNextFrame(d->nodeTexture, window());
747 d->nodeTexture =
nullptr;
748 if (d->textureProvider) {
749 d->textureProvider->tex =
nullptr;
750 d->textureProvider->fireTextureChanged();
755 d->nodeTexture = texture;
756 node->setTexture(texture);
757 node->setTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
758 node->setInnerTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
761 if (d->textureProvider) {
762 d->textureProvider->tex = d->nodeTexture;
763 d->textureProvider->fireTextureChanged();
778 if (QQuickItem::isTextureProvider())
779 return QQuickItem::textureProvider();
781 Q_D(
const QQuickCanvasItem);
783 QQuickWindow *w = window();
784 if (!w || !w->isSceneGraphInitialized()
785 || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) {
786 qWarning(
"QQuickCanvasItem::textureProvider: can only be queried on the rendering thread of an exposed window");
790 if (!d->textureProvider)
791 d->textureProvider =
new QQuickCanvasTextureProvider;
792 d->textureProvider->tex = d->nodeTexture;
793 return d->textureProvider;
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
817 Q_D(QQuickCanvasItem);
819 QV4::Scope scope(args->v4engine());
820 QV4::ScopedString str(scope, (*args)[0]);
822 qmlWarning(
this) <<
"getContext should be called with a string naming the required context type";
823 args->setReturnValue(QV4::Encode::null());
828 qmlWarning(
this) <<
"Unable to use getContext() at this time, please wait for available: true";
829 args->setReturnValue(QV4::Encode::null());
833 QString contextId = str->toQString();
835 if (d->context !=
nullptr) {
836 if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
837 args->setReturnValue(d->context->v4value());
841 qmlWarning(
this) <<
"Canvas already initialized with a different context type";
842 args->setReturnValue(QV4::Encode::null());
846 if (createContext(contextId))
847 args->setReturnValue(d->context->v4value());
849 args->setReturnValue(QV4::Encode::null());
853
854
855
856
857
861 QV4::Scope scope(args->v4engine());
862 QV4::ScopedFunctionObject f(scope, (*args)[0]);
864 qmlWarning(
this) <<
"requestAnimationFrame should be called with an animation callback function";
865 args->setReturnValue(QV4::Encode::null());
869 Q_D(QQuickCanvasItem);
873 d->animationCallbacks.insert(++id, QV4::PersistentValue(scope.engine, f->asReturnedValue()));
879 args->setReturnValue(QV4::Encode(id));
883
884
885
886
890 QV4::Scope scope(args->v4engine());
891 QV4::ScopedValue v(scope, (*args)[0]);
892 if (!v->isInteger()) {
893 qmlWarning(
this) <<
"cancelRequestAnimationFrame should be called with an animation callback id";
894 args->setReturnValue(QV4::Encode::null());
898 d_func()->animationCallbacks.remove(v->integerValue());
903
904
905
906
907
908
912 markDirty(d_func()->canvasWindow);
916
917
918
919
920
921
922
926 Q_D(QQuickCanvasItem);
930 d->dirtyRect |= rect;
937 if (d_func()->animationCallbacks.size() > 0 && isVisible())
942
943
944
945
946
947
948
949
950
951
952
953
954
955
958 Q_D(
const QQuickCanvasItem);
959 QString localFile = filename;
960 if (QDir::isRelativePath(filename)) {
962 url.setPath(filename);
963 localFile = d->baseUrl.resolved(url).toLocalFile();
965 return toImage(QRectF(QPointF(0, 0), imageSize)).save(localFile);
970 Q_D(QQuickCanvasItem);
971 QUrl fullPathUrl = d->baseUrl.resolved(url);
972 if (!d->pixmaps.contains(fullPathUrl)) {
973 loadImage(url, sourceSize);
975 return d->pixmaps.value(fullPathUrl);
979
980
981
982
983
984
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1005 Q_D(QQuickCanvasItem);
1006 QUrl fullPathUrl = d->baseUrl.resolved(url);
1007 if (!d->pixmaps.contains(fullPathUrl)) {
1008 QQuickPixmap* pix =
new QQuickPixmap();
1009 QQmlRefPointer<QQuickCanvasPixmap> canvasPix;
1010 canvasPix.adopt(
new QQuickCanvasPixmap
(pix
));
1011 d->pixmaps.insert(fullPathUrl, canvasPix);
1013 pix->load(qmlEngine(
this)
1016 , sourceSize.toSize()
1017 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
1018 if (pix->isLoading())
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1035 Q_D(QQuickCanvasItem);
1036 d->pixmaps.remove(d->baseUrl.resolved(url));
1040
1041
1042
1043
1044
1045
1048 Q_D(
const QQuickCanvasItem);
1049 QUrl fullPathUrl = d->baseUrl.resolved(url);
1050 return d->pixmaps.contains(fullPathUrl)
1051 && d->pixmaps.value(fullPathUrl)->pixmap()->isError();
1055
1056
1057
1058
1059
1062 Q_D(
const QQuickCanvasItem);
1063 QUrl fullPathUrl = d->baseUrl.resolved(url);
1064 return d->pixmaps.contains(fullPathUrl)
1065 && d->pixmaps.value(fullPathUrl)->pixmap()->isLoading();
1068
1069
1070
1071
1072
1075 Q_D(
const QQuickCanvasItem);
1076 QUrl fullPathUrl = d->baseUrl.resolved(url);
1077 return d->pixmaps.contains(fullPathUrl)
1078 && d->pixmaps.value(fullPathUrl)->pixmap()->isReady();
1082
1083
1084
1085
1089 Q_D(
const QQuickCanvasItem);
1094 const QRectF &rectSource = rect.isEmpty() ? canvasWindow() : rect;
1095 const qreal dpr = window() && rect.isEmpty() ? window()->effectiveDevicePixelRatio() : qreal(1);
1096 const QRectF rectScaled(rectSource.topLeft() * dpr, rectSource.size() * dpr);
1098 QImage image = d->context->toImage(rectScaled);
1099 image.setDevicePixelRatio(dpr);
1105 const QLatin1String imagePrefix(
"image/");
1106 if (!mime.startsWith(imagePrefix))
1108 const QStringView mimeExt = QStringView{mime}.mid(imagePrefix.size());
1109 if (mimeExt == QLatin1String(
"png"))
1111 else if (mimeExt == QLatin1String(
"bmp"))
1113 else if (mimeExt == QLatin1String(
"jpeg"))
1115 else if (mimeExt == QLatin1String(
"x-portable-pixmap"))
1117 else if (mimeExt == QLatin1String(
"tiff"))
1119 else if (mimeExt == QLatin1String(
"xpm"))
1125
1126
1127
1128
1129
1130
1131
1132
1135 QImage image = toImage();
1137 if (!image.isNull()) {
1139 QBuffer buffer(&ba);
1140 buffer.open(QIODevice::WriteOnly);
1141 const QString mime = mimeType.toLower();
1142 const char* type = mimeToType(mime);
1144 return QStringLiteral(
"data:,");
1146 image.save(&buffer, type);
1148 return QLatin1String(
"data:") + mime + QLatin1String(
";base64,") + QLatin1String(ba.toBase64().constData());
1150 return QStringLiteral(
"data:,");
1155 Q_D(QQuickCanvasItem);
1157 if (!d->context && !d->contextType.isNull())
1158 createContext(d->contextType);
1165 Q_D(QQuickCanvasItem);
1170 if (contextType == QLatin1String(
"2d")) {
1171 if (d->contextType.compare(QLatin1String(
"2d"), Qt::CaseInsensitive) != 0) {
1172 d->contextType = QLatin1String(
"2d");
1173 emit contextTypeChanged();
1175 initializeContext(
new QQuickContext2D(
this));
1184 Q_D(QQuickCanvasItem);
1186 d->context = context;
1187 d->context->init(
this, args);
1188 d->context->setV4Engine(qmlEngine(
this)->handle());
1189 connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
1190 connect(d->context, SIGNAL(textureChanged()), SIGNAL(
painted()));
1191 emit contextChanged();
1194QRect
QQuickCanvasItem::tiledRect(
const QRectF &window,
const QSize &tileSize)
1196 if (window.isEmpty())
1199 const int tw = tileSize.width();
1200 const int th = tileSize.height();
1201 const int h1 = window.left() / tw;
1202 const int v1 = window.top() / th;
1204 const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1205 const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1207 return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1211
1212
1213
1214
1215
1216
1217
1218
1221
1222
1223
1224
1225
1229#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 bool QtQuick::Canvas::isImageLoaded(url image) Returns true if the image is successfully l...
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 void QtQuick::Canvas::unloadImage(url image)
bool isImageError(const QUrl &url) const
\qmlmethod bool 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 void QtQuick::Canvas::cancelRequestAnimationFrame(int handle)
Q_INVOKABLE void getContext(QQmlV4FunctionPtr args)
\qmlmethod Context2D QtQuick::Canvas::getContext(string contextId, ... args)
bool isImageLoading(const QUrl &url) const
\qmlmethod bool QtQuick::Canvas::isImageLoading(url image) Returns true if the image is currently loa...
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
Q_INVOKABLE void markDirty(const QRectF &dirtyRect=QRectF())
\qmlmethod void 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 void 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)