Make Canvas a texture provider.
authorGunnar Sletta <gunnar@sletta.org>
Sat, 23 Aug 2014 12:13:53 +0000 (14:13 +0200)
committerGunnar Sletta <gunnar@sletta.org>
Tue, 26 Aug 2014 04:12:51 +0000 (06:12 +0200)
Change-Id: I8b5c9d8a5ed9ef9079eafb30ef0cb73786204ee6
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
src/quick/items/context2d/qquickcanvasitem.cpp
src/quick/items/context2d/qquickcanvasitem_p.h

index 8d317f7..878cc70 100644 (file)
 
 QT_BEGIN_NAMESPACE
 
+class QQuickCanvasNode : public QSGSimpleTextureNode
+{
+public:
+    QQuickCanvasNode() {
+        qsgnode_set_description(this, QStringLiteral("canvasnode"));
+        setOwnsTexture(true);
+    }
+};
+
+class QQuickCanvasTextureProvider : public QSGTextureProvider
+{
+public:
+    QQuickCanvasNode *node;
+    QSGTexture *texture() const Q_DECL_OVERRIDE { return node ? node->texture() : 0; }
+    void fireTextureChanged() { emit textureChanged(); }
+};
+
 QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image)
     : m_pixmap(0)
     , m_image(image)
@@ -158,6 +175,8 @@ public:
     QHash<QUrl, QQmlRefPointer<QQuickCanvasPixmap> > pixmaps;
     QUrl baseUrl;
     QMap<int, QV4::PersistentValue> animationCallbacks;
+    mutable QQuickCanvasTextureProvider *textureProvider;
+    QQuickCanvasNode *node;
 };
 
 QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
@@ -171,6 +190,8 @@ QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
     , available(false)
     , renderTarget(QQuickCanvasItem::Image)
     , renderStrategy(QQuickCanvasItem::Immediate)
+    , textureProvider(0)
+    , node(0)
 {
     implicitAntialiasing = true;
 }
@@ -269,6 +290,11 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
        them in the \c onImageLoaded handler.
     \endlist
 
+    Starting Qt 5.4, the Canvas is a
+    \l{QSGTextureProvider}{texture provider}
+    and can be used directly in \l {ShaderEffect}{ShaderEffects} and other
+    classes that consume texture providers.
+
     \sa Context2D
 */
 
@@ -283,6 +309,8 @@ QQuickCanvasItem::~QQuickCanvasItem()
 {
     Q_D(QQuickCanvasItem);
     delete d->context;
+    if (d->textureProvider)
+        QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
 }
 
 /*!
@@ -597,6 +625,11 @@ void QQuickCanvasItem::releaseResources()
         delete d->context;
         d->context = 0;
     }
+    d->node = 0; // managed by the scene graph, just reset the pointer
+    if (d->textureProvider) {
+        QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
+        d->textureProvider = 0;
+    }
 }
 
 void QQuickCanvasItem::invalidateSG()
@@ -604,6 +637,9 @@ void QQuickCanvasItem::invalidateSG()
     Q_D(QQuickCanvasItem);
     d->context->deleteLater();
     d->context = 0;
+    d->node = 0; // managed by the scene graph, just reset the pointer
+    delete d->textureProvider;
+    d->textureProvider = 0;
 }
 
 void QQuickCanvasItem::componentComplete()
@@ -678,27 +714,25 @@ void QQuickCanvasItem::updatePolish()
     }
 }
 
-class QQuickCanvasNode : public QSGSimpleTextureNode
-{
-public:
-    ~QQuickCanvasNode()
-    {
-        delete texture();
-    }
-};
-
 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
 {
     Q_D(QQuickCanvasItem);
 
     if (!d->context || d->canvasWindow.size().isEmpty()) {
+        if (d->textureProvider) {
+            d->textureProvider->node = 0;
+            d->textureProvider->fireTextureChanged();
+        }
         delete oldNode;
         return 0;
     }
 
     QQuickCanvasNode *node = static_cast<QQuickCanvasNode*>(oldNode);
-    if (!node)
+    if (!node) {
         node = new QQuickCanvasNode();
+        d->node = node;
+    }
+
 
     if (d->smooth)
         node->setFiltering(QSGTexture::Linear);
@@ -715,14 +749,43 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
     QSGTexture *texture = factory->textureForNextFrame(node->texture());
     if (!texture) {
         delete node;
+        d->node = 0;
+        if (d->textureProvider) {
+            d->textureProvider->node = 0;
+            d->textureProvider->fireTextureChanged();
+        }
         return 0;
     }
 
     node->setTexture(texture);
     node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
+
+    if (d->textureProvider) {
+        d->textureProvider->node = node;
+        d->textureProvider->fireTextureChanged();
+    }
     return node;
 }
 
+bool QQuickCanvasItem::isTextureProvider() const
+{
+    return true;
+}
+
+QSGTextureProvider *QQuickCanvasItem::textureProvider() const
+{
+    Q_D(const QQuickCanvasItem);
+    QQuickWindow *w = window();
+    if (!w || !w->openglContext() || QThread::currentThread() != w->openglContext()->thread()) {
+        qWarning("QQuickCanvasItem::textureProvider: can only be queried on the rendering thread of an exposed window");
+        return 0;
+    }
+    if (!d->textureProvider)
+        d->textureProvider = new QQuickCanvasTextureProvider;
+    d->textureProvider->node = d->node;
+    return d->textureProvider;
+}
+
 /*!
     \qmlmethod object QtQuick::Canvas::getContext(string contextId, any... args)
 
index 6297009..95bc6f3 100644 (file)
@@ -133,6 +133,9 @@ public:
     Q_INVOKABLE QString toDataURL(const QString& type = QLatin1String("image/png")) const;
     QQmlRefPointer<QQuickCanvasPixmap> loadedPixmap(const QUrl& url);
 
+    bool isTextureProvider() const Q_DECL_OVERRIDE;
+    QSGTextureProvider *textureProvider() const Q_DECL_OVERRIDE;
+
 Q_SIGNALS:
     void paint(const QRect &region);
     void painted();