Refactor context2d thread logic
[profile/ivi/qtdeclarative.git] / src / quick / items / context2d / qquickcanvasitem.cpp
index 2286be2..16f08d3 100644 (file)
@@ -3,7 +3,7 @@
 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 ** Contact: http://www.qt-project.org/
 **
-** This file is part of the QtDeclarative module of the Qt Toolkit.
+** This file is part of the QtQml module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** GNU Lesser General Public License Usage
 #include <private/qsgadaptationlayer_p.h>
 #include "qquickcanvasitem_p.h"
 #include <private/qquickitem_p.h>
-#include "qquickcontext2d_p.h"
-#include "qquickcontext2dnode_p.h"
-#include "qquickcontext2dtexture_p.h"
-#include <QtQuick/private/qdeclarativepixmapcache_p.h>
+#include <private/qquickcanvascontext_p.h>
+#include <private/qquickcontext2d_p.h>
+#include <qsgsimpletexturenode.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
 
-#include <qdeclarativeinfo.h>
-#include <private/qdeclarativeengine_p.h>
+#include <qqmlinfo.h>
+#include <private/qqmlengine_p.h>
 #include <QtCore/QBuffer>
 
 QT_BEGIN_NAMESPACE
 
+QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image, QQuickWindow *window)
+    : m_pixmap(0)
+    , m_image(image)
+    , m_texture(0)
+    , m_window(window)
+{
+
+}
+
+QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *window)
+    : m_pixmap(pixmap)
+    , m_texture(0)
+    , m_window(window)
+{
+
+}
+
+QQuickCanvasPixmap::~QQuickCanvasPixmap()
+{
+    delete m_pixmap;
+    if (m_texture)
+        m_texture->deleteLater();
+}
+
+qreal QQuickCanvasPixmap::width() const
+{
+    if (m_pixmap)
+        return m_pixmap->width();
+
+    return m_image.width();
+}
+
+qreal QQuickCanvasPixmap::height() const
+{
+    if (m_pixmap)
+        return m_pixmap->height();
+
+    return m_image.height();
+}
+
+bool QQuickCanvasPixmap::isValid() const
+{
+    if (m_pixmap)
+        return m_pixmap->isReady();
+    return !m_image.isNull();
+}
+
+QSGTexture *QQuickCanvasPixmap::texture()
+{
+    if (!m_texture) {
+        if (m_pixmap) {
+            Q_ASSERT(m_pixmap->textureFactory());
+            m_texture = m_pixmap->textureFactory()->createTexture(m_window);
+        } else {
+            m_texture = QQuickWindowPrivate::get(m_window)->context->createTexture(m_image);
+        }
+    }
+    return m_texture;
+}
+QImage QQuickCanvasPixmap::image()
+{
+    if (m_image.isNull() && m_pixmap)
+        m_image = m_pixmap->image();
+
+    return m_image;
+}
+
+QHash<QQmlEngine *,QQuickContext2DRenderThread*> QQuickContext2DRenderThread::renderThreads;
+QMutex QQuickContext2DRenderThread::renderThreadsMutex;
+
+QQuickContext2DRenderThread::QQuickContext2DRenderThread(QQmlEngine *eng)
+    : QThread(eng), m_engine(eng), m_eventLoopQuitHack(0)
+{
+    Q_ASSERT(eng);
+    m_eventLoopQuitHack = new QObject;
+    m_eventLoopQuitHack->moveToThread(this);
+    connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
+    start(QThread::IdlePriority);
+}
+
+QQuickContext2DRenderThread::~QQuickContext2DRenderThread()
+{
+    renderThreadsMutex.lock();
+    renderThreads.remove(m_engine);
+    renderThreadsMutex.unlock();
+
+    m_eventLoopQuitHack->deleteLater();
+    wait();
+}
+
+QQuickContext2DRenderThread *QQuickContext2DRenderThread::instance(QQmlEngine *engine)
+{
+    QQuickContext2DRenderThread *thread = 0;
+    renderThreadsMutex.lock();
+    if (renderThreads.contains(engine))
+        thread = renderThreads.value(engine);
+    else {
+        thread = new QQuickContext2DRenderThread(engine);
+        renderThreads.insert(engine, thread);
+    }
+    renderThreadsMutex.unlock();
+    return thread;
+}
+
 class QQuickCanvasItemPrivate : public QQuickItemPrivate
 {
 public:
     QQuickCanvasItemPrivate();
     ~QQuickCanvasItemPrivate();
-    QQuickContext2D* context;
-    QQuickContext2DTexture* texture;
+    QQuickCanvasContext *context;
     QSizeF canvasSize;
     QSize tileSize;
     QRectF canvasWindow;
     QRectF dirtyRect;
-    uint renderInThread : 1;
     uint hasCanvasSize :1;
     uint hasTileSize :1;
     uint hasCanvasWindow :1;
-    uint componentCompleted :1;
+    uint available :1;
+    uint contextInitialized :1;
     QQuickCanvasItem::RenderTarget renderTarget;
-    QHash<QUrl, QDeclarativePixmap*> images;
+    QQuickCanvasItem::RenderStrategy renderStrategy;
+    QString contextType;
+    QHash<QUrl, QQmlRefPointer<QQuickCanvasPixmap> > pixmaps;
     QUrl baseUrl;
+    QMap<int, v8::Persistent<v8::Function> > animationCallbacks;
 };
 
 QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
     : QQuickItemPrivate()
     , context(0)
-    , texture(0)
     , canvasSize(1, 1)
     , tileSize(1, 1)
-    , renderInThread(false)
     , hasCanvasSize(false)
     , hasTileSize(false)
     , hasCanvasWindow(false)
-    , componentCompleted(false)
+    , available(false)
+    , contextInitialized(false)
     , renderTarget(QQuickCanvasItem::FramebufferObject)
+    , renderStrategy(QQuickCanvasItem::Cooperative)
 {
 }
 
 QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
 {
-    qDeleteAll(images);
+    pixmaps.clear();
 }
 
+
 /*!
-    \qmlclass Canvas QQuickCanvasItem
+    \qmltype Canvas
+    \instantiates QQuickCanvasItem
     \inqmlmodule QtQuick 2
     \since QtQuick 2.0
-    \brief The Canvas item provides a 2D canvas element which enables drawing via Javascript.
     \inherits Item
-    \ingroup qml-basic-visual-elements
+    \ingroup qtquick-canvas
+    \ingroup qtquick-visual
+    \brief Provides a 2D canvas item enabling drawing via JavaScript
 
     The Canvas item allows drawing of straight and curved lines, simple and
-    complex shapes, graphs, and referenced graphic images.  It can also add text, colors,
-    shadows, gradients, and patterns, and do low level pixel operations. The Canvas
-    output may be saved as an image file or serialized to a url.
-
-    To define a drawing area in the Canvas item set the \c width and \c height properties.
-    For example, the following code creates a Canvas item which has a drawing area with a height of 100
-    pixels and width of 200 pixels:
+    complex shapes, graphs, and referenced graphic images.  It can also add
+    text, colors, shadows, gradients, and patterns, and do low level pixel
+    operations. The Canvas output may be saved as an image file or serialized
+    to a URL.
+
+    To define a drawing area in the Canvas item set the \c width and \c height
+    properties.  For example, the following code creates a Canvas item which
+    has a drawing area with a height of 100 pixels and width of 200 pixels:
     \qml
     import QtQuick 2.0
     Canvas {
-      id:mycanvas
-      width:100
-      height:200
+        id: mycanvas
+        width: 100
+        height: 200
     }
     \endqml
 
@@ -123,47 +233,55 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
 
     \section1 Threaded Rendering and Render Target
 
-    The Canvas item supports two render targets: \c Canvas.Image and \c Canvas.FramebufferObject.
+    The Canvas item supports two render targets: \c Canvas.Image and
+    \c Canvas.FramebufferObject.
 
-    The \c Canvas.Image render target is a \a QImage object.  This render target supports background
-    thread rendering, allowing complex or long running painting to be executed without blocking the UI.
+    The \c Canvas.Image render target is a \a QImage object.  This render
+    target supports background thread rendering, allowing complex or long
+    running painting to be executed without blocking the UI.
 
-    The Canvas.FramebufferObject render target utilizes OpenGL hardware accelaration rather than rendering into
-    system memory, which in many cases results in faster rendering.
+    The Canvas.FramebufferObject render target utilizes OpenGL hardware
+    acceleration rather than rendering into system memory, which in many cases
+    results in faster rendering.
 
-    The default render target is Canvas.Image and the default renderInThread property is
-    false.
+    The default render target is Canvas.Image and the default renderStrategy is
+    Canvas.Threaded.
 
     \section1 Tiled Canvas
     The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize
     and \l canvasWindow properties.
 
-    Tiling allows efficient display of a very large virtual via a smaller canvas
-    window. The actual memory consumption is in relatation to the canvas window size.  The painting
-    code can draw within the virtual canvas without handling coordinate system transformations.
+    Tiling allows efficient display of a very large virtual canvas via a smaller
+    canvas window. The actual memory consumption is in relation to the canvas
+    window size.  The painting code can draw within the virtual canvas without
+    handling coordinate system transformations.
 
-    The tiles overlapping with the canvas window may be cached eliminating the need to redraw,
-    which can lead to significantly improved performance in some situations.
+    The tiles overlapping with the canvas window may be cached eliminating the
+    need to redraw, which can lead to significantly improved performance in
+    some situations.
 
     \section1 Pixel Operations
-    All HTML5 2D context pixel operations are supported. In order to ensure improved
-    pixel reading/writing performance the \a Canvas.Image render target should be chosen. The
-    \a Canvas.FramebufferObject render target requires the pixel data to be exchanged between
-    the system memory and the graphic card, which is significantly more expensive.  Rendering
-    may also be synchronized with the V-sync signal (to avoid {en.wikipedia.org/wiki/Screen_tearing}{screen tearing})
-    which will futher impact pixel operations with \c Canvas.FrambufferObject render target.
+    All HTML5 2D context pixel operations are supported. In order to ensure
+    improved pixel reading/writing performance the \a Canvas.Image render
+    target should be chosen. The \a Canvas.FramebufferObject render target
+    requires the pixel data to be exchanged between the system memory and the
+    graphic card, which is significantly more expensive.  Rendering may also be
+    synchronized with the V-sync signal (to avoid
+    {en.wikipedia.org/wiki/Screen_tearing}{screen tearing}) which will further
+    impact pixel operations with \c Canvas.FrambufferObject render target.
 
     \section1 Tips for Porting Existing HTML5 Canvas applications
 
-    Although the Canvas item is provides a HTML5 like API, HTML5 canvas applications
-    need to be modified to run in the Canvas item:
+    Although the Canvas item is provides a HTML5 like API, HTML5 canvas
+    applications need to be modified to run in the Canvas item:
     \list
-    \o Replace all DOM API calls with QML property bindings or Canvas item methods.
-    \o Replace all HTML event handlers with the \a MouseArea item.
-    \o Change setInterval/setTimeout function calls with the \a Timer item.
-    \o Place painting code into the \a QtQuick2::Canvas::onPaint handler and trigger
+    \li Replace all DOM API calls with QML property bindings or Canvas item methods.
+    \li Replace all HTML event handlers with the \a MouseArea item.
+    \li Change setInterval/setTimeout function calls with the \a Timer item or
+       the use of requestAnimationFrame.
+    \li Place painting code into the \a QtQuick2::Canvas::onPaint handler and trigger
        painting by calling the \c markDirty or \c requestPaint methods.
-    \o To draw images, load them by calling the Canvas's loadImage method and then request to paint
+    \li To draw images, load them by calling the Canvas's loadImage method and then request to paint
        them in the onImageLoaded handler.
     \endlist
 
@@ -183,16 +301,87 @@ QQuickCanvasItem::~QQuickCanvasItem()
 }
 
 /*!
+    \qmlproperty size QtQuick2::Canvas::available
+
+    Indicates when Canvas is able to provide a drawing context to operate on.
+*/
+
+bool QQuickCanvasItem::isAvailable() const
+{
+    return d_func()->available;
+}
+
+/*!
+    \qmlproperty string QtQuick2::Canvas::contextType
+    The type of drawing context to use.
+
+    This property is set to the name of the active context type.
+
+    If set explicitly the canvas will attempt to create a context of the
+    named type after becoming available.
+
+    The type name is the same as used in the getContext() call, for the 2d
+    canvas the value will be "2d".
+
+    \sa QtQuick2::Canvas::getContext, QtQuick2::Canvas::available
+*/
+
+QString QQuickCanvasItem::contextType() const
+{
+    return d_func()->contextType;
+}
+
+void QQuickCanvasItem::setContextType(const QString &contextType)
+{
+    Q_D(QQuickCanvasItem);
+
+    if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
+        return;
+
+    if (d->contextInitialized) {
+        qmlInfo(this) << "Canvas already initialized with a different context type";
+        return;
+    }
+
+    d->contextType = contextType;
+
+    if (d->available)
+        createContext(contextType);
+
+    emit contextTypeChanged();
+}
+
+/*!
+    \qmlproperty object QtQuick2::Canvas::context
+    Holds the active drawing context.
+
+    If the canvas is ready and there has been a successful call to getContext()
+    or the contextType property has been set with a supported context type,
+    this property will contain the current drawing context, otherwise null.
+*/
+
+QQmlV8Handle QQuickCanvasItem::context() const
+{
+    Q_D(const QQuickCanvasItem);
+    if (d->contextInitialized)
+        return QQmlV8Handle::fromHandle(d->context->v8value());
+
+    return QQmlV8Handle::fromHandle(v8::Null());
+}
+
+/*!
     \qmlproperty size QtQuick2::Canvas::canvasSize
-     Holds the logical canvas size that the context paints on.
+    Holds the logical canvas size that the context paints on.
+
+    By default, the canvas size is the same size as the current canvas item
+    size.
 
-     By default, the canvas size is the same size as the current canvas item size.
-     By setting the canvasSize, tileSize and canvasWindow, the Canvas
-     item can act as a large virtual canvas with many seperately rendered tile rectangles
-     Only those tiles within the current canvas window are painted by
-     the Canvas render engine.
+    By setting the canvasSize, tileSize and canvasWindow, the Canvas item can
+    act as a large virtual canvas with many separately rendered tile rectangles
+    Only those tiles within the current canvas window are painted by the Canvas
+    render engine.
 
-    \sa QtQuick2::Canvas::tileSize QtQuick2::Canvas::canvasWindow
+    \sa QtQuick2::Canvas::tileSize, QtQuick2::Canvas::canvasWindow
 */
 QSizeF QQuickCanvasItem::canvasSize() const
 {
@@ -207,24 +396,26 @@ void QQuickCanvasItem::setCanvasSize(const QSizeF & size)
         d->hasCanvasSize = true;
         d->canvasSize = size;
         emit canvasSizeChanged();
-        polish();
-        update();
+
+        if (d->contextInitialized)
+            polish();
     }
 }
 
 /*!
     \qmlproperty size QtQuick2::Canvas::tileSize
-     Holds the canvas rendering tile size.
+    Holds the canvas rendering tile size.
 
-     The Canvas item enters tiled mode by setting canvasSize, tileSize and
-     the canvasWindow. This can improve rendering performance
-     by rendering and caching tiles instead of rendering the whole canvas every time.
+    The Canvas item enters tiled mode by setting canvasSize, tileSize and the
+    canvasWindow. This can improve rendering performance by rendering and
+    caching tiles instead of rendering the whole canvas every time.
 
-     Memory will be consumed only by those tiles within the current visible region.
+    Memory will be consumed only by those tiles within the current visible
+    region.
 
-     By default the tileSize is the same as the canvasSize.
+    By default the tileSize is the same as the canvasSize.
 
-     \sa QtQuick2::Canvas::canvaasSize QtQuick2::Canvas::canvasWindow
+    \sa QtQuick2::Canvas::canvaasSize, QtQuick2::Canvas::canvasWindow
 */
 QSize QQuickCanvasItem::tileSize() const
 {
@@ -240,8 +431,9 @@ void QQuickCanvasItem::setTileSize(const QSize & size)
         d->tileSize = size;
 
         emit tileSizeChanged();
-        polish();
-        update();
+
+        if (d->contextInitialized)
+            polish();
     }
 }
 
@@ -249,14 +441,14 @@ void QQuickCanvasItem::setTileSize(const QSize & size)
     \qmlproperty rect QtQuick2::Canvas::canvasWindow
      Holds the current canvas visible window.
 
-     By default the canvasWindow size is the same as the Canvas item
-     size with the topleft point as (0, 0).
+     By default the canvasWindow size is the same as the Canvas item size with
+     the top-left point as (0, 0).
 
-     If the canvasSize is different to the Canvas item size, the Canvas
-     item can display different visible areas by changing the canvas windowSize
+     If the canvasSize is different to the Canvas item size, the Canvas item
+     can display different visible areas by changing the canvas windowSize
      and/or position.
 
-    \sa QtQuick2::Canvas::canvasSize QtQuick2::Canvas::tileSize
+    \sa QtQuick2::Canvas::canvasSize, QtQuick2::Canvas::tileSize
 */
 QRectF QQuickCanvasItem::canvasWindow() const
 {
@@ -272,54 +464,28 @@ void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
 
         d->hasCanvasWindow = true;
         emit canvasWindowChanged();
-        polish();
-        update();
+
+        if (d->contextInitialized)
+            polish();
     }
 }
 
-
-QQuickContext2D* QQuickCanvasItem::context() const
-{
-    Q_D(const QQuickCanvasItem);
-    return d->context;
-}
 /*!
-    \qmlproperty bool QtQuick2::Canvas::renderInThread
-     Holds the current canvas rendering mode.
+    \qmlproperty enumeration QtQuick2::Canvas::renderTarget
+    Holds the current canvas render target.
 
-     Set renderInThread to true to render complex and long
-     running painting in a dedicated background
-     thread, avoiding blocking the main UI.
+    \list
+    \li Canvas.Image  - render to an in memory image buffer.
+    \li Canvas.FramebufferObject - render to an OpenGL frame buffer
+    \endlist
 
-     \note: Not all renderTargets support background rendering.  If background rendering
-     is not supported by the current renderTarget, the renderInThread
-     property is ignored.
+    This hint is supplied along with renderStrategy to the graphics context to
+    determine the method of rendering. A renderStrategy, renderTarget or a
+    combination may not be supported by a graphics context, in which case the
+    context will choose appropriate options and Canvas will signal the change
+    to the properties.
 
-     The default value is false.
-    \sa QtQuick2::Canvas::renderTarget
-*/
-bool QQuickCanvasItem::renderInThread() const
-{
-    Q_D(const QQuickCanvasItem);
-    return d->renderInThread;
-}
-/*!
-    \qmlproperty bool QtQuick2::Canvas::renderTarget
-     Holds the current canvas render target.
-
-     \list
-     \o Canvas.Image  - render to an in memory image buffer, the render
-                        target supports background rendering.
-     \o Canvas.FramebufferObject - render to an OpenGL frame buffer,
-                                   this render target will ignore the
-                                   renderInThread property. The actual
-                                   rendering happens in the main QML rendering
-                                   process, which may be in a seperate render thread
-                                   or in the main GUI thread depending upon the platform.
-     \endlist
-
-     The default render target is \c Canvas.Image.
-    \sa QtQuick2::Canvas::renderInThread
+    The default render target is \c Canvas.FramebufferObject.
 */
 QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const
 {
@@ -327,200 +493,348 @@ QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const
     return d->renderTarget;
 }
 
-void QQuickCanvasItem::setRenderTarget(RenderTarget target)
+void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target)
 {
     Q_D(QQuickCanvasItem);
     if (d->renderTarget != target) {
-        d->renderTarget = target;
+        if (d->contextInitialized) {
+            qmlInfo(this) << "Canvas:renderTarget not changeble once context is active.";
+            return;
+        }
 
-        if (d->componentCompleted)
-            createTexture();
+        d->renderTarget = target;
         emit renderTargetChanged();
     }
 }
 
-void QQuickCanvasItem::_doPainting(const QRectF& region)
+/*!
+    \qmlproperty enumeration QtQuick2::Canvas::renderStrategy
+    Holds the current canvas rendering strategy.
+
+    \list
+    \li Canvas.Immediate - context will perform graphics commands immediately in the main UI thread.
+    \li Canvas.Threaded - context will defer graphics commands to a private rendering thread.
+    \li Canvas.Cooperative - context will defer graphics commands to the applications global render thread.
+    \endlist
+
+    This hint is supplied along with renderTarget to the graphics context to
+    determine the method of rendering. A renderStrategy, renderTarget or a
+    combination may not be supported by a graphics context, in which case the
+    context will choose appropriate options and Canvas will signal the change
+    to the properties.
+
+    Configuration or runtime tests may cause the QML Scene Graph to render in
+    the GUI thread.  Selecting \c Canvas.Cooperative, does not guarantee
+    rendering will occur on a thread separate from the GUI thread.
+
+    The default value is \c Canvas.Cooperative.
+
+    \sa QtQuick2::Canvas::renderTarget
+*/
+
+QQuickCanvasItem::RenderStrategy QQuickCanvasItem::renderStrategy() const
+{
+    return d_func()->renderStrategy;
+}
+
+void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strategy)
 {
     Q_D(QQuickCanvasItem);
-    emit paint(QDeclarativeV8Handle::fromHandle(d->context->v8value())
-             , QQuickContext2DTexture::tiledRect(region, d->tileSize));
-    if (d->texture)
-        d->texture->wake();
+    if (d->renderStrategy != strategy) {
+        if (d->contextInitialized) {
+            qmlInfo(this) << "Canvas:renderStrategy not changeable once context is active.";
+            return;
+        }
+        d->renderStrategy = strategy;
+        emit renderStrategyChanged();
+    }
+}
+
+QQuickCanvasContext* QQuickCanvasItem::rawContext() const
+{
+    return d_func()->context;
+}
+
+bool QQuickCanvasItem::isPaintConnected()
+{
+    IS_SIGNAL_CONNECTED(this, QQuickCanvasItem, paint, (const QRect &));
 }
 
-void QQuickCanvasItem::setRenderInThread(bool renderInThread)
+void QQuickCanvasItem::sceneGraphInitialized()
 {
     Q_D(QQuickCanvasItem);
-    if (d->renderInThread != renderInThread) {
-        d->renderInThread = renderInThread;
 
-        if (d->componentCompleted)
-            createTexture();
+    d->available = true;
+    connect(this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
+    QMetaObject::invokeMethod(this, "availableChanged", Qt::QueuedConnection);
 
-        if (d->renderInThread)
-            connect(this, SIGNAL(painted()), SLOT(update()));
-        else
-            disconnect(this, SIGNAL(painted()), this, SLOT(update()));
-        emit renderInThreadChanged();
-        polish();
-        update();
-    }
+    if (!d->contextType.isNull())
+        QMetaObject::invokeMethod(this, "delayedCreate", Qt::QueuedConnection);
+    else if (isPaintConnected())
+        QMetaObject::invokeMethod(this, "requestPaint", Qt::QueuedConnection);
 }
 
-void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry,
-                             const QRectF &oldGeometry)
+void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
 {
     Q_D(QQuickCanvasItem);
-    QQuickItem::geometryChanged(newGeometry, oldGeometry);
 
-    const qreal w = newGeometry.width();
-    const qreal h = newGeometry.height();
+    QQuickItem::geometryChanged(newGeometry, oldGeometry);
 
-    if (!d->hasCanvasSize) {
-        d->canvasSize = QSizeF(w, h);
+    QSizeF newSize = newGeometry.size();
+    if (!d->hasCanvasSize && d->canvasSize != newSize) {
+        d->canvasSize = newSize;
         emit canvasSizeChanged();
     }
 
-    if (!d->hasTileSize) {
-        d->tileSize = d->canvasSize.toSize();
+    if (!d->hasTileSize && d->tileSize != newSize) {
+        d->tileSize = newSize.toSize();
         emit tileSizeChanged();
     }
 
-    if (!d->hasCanvasWindow) {
-        d->canvasWindow = newGeometry;
+    const QRectF rect = QRectF(QPointF(0, 0), newSize);
+
+    if (!d->hasCanvasWindow && d->canvasWindow != rect) {
+        d->canvasWindow = rect;
         emit canvasWindowChanged();
     }
 
-    polish();
-    update();
+    if (d->available)
+        requestPaint();
 }
 
-void QQuickCanvasItem::componentComplete()
+void QQuickCanvasItem::releaseResources()
 {
     Q_D(QQuickCanvasItem);
-    QQuickItem::componentComplete();
 
-    if (!d->context)
-        createContext();
-    createTexture();
+    if (d->context) {
+        delete d->context;
+        d->context = 0;
+    }
+}
 
+void QQuickCanvasItem::componentComplete()
+{
+    QQuickItem::componentComplete();
+
+    Q_D(QQuickCanvasItem);
     d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
-    requestPaint();
-    updatePolish(); //force update the canvas sizes to texture for the first time
-    update();
-    d->componentCompleted = true;
 }
 
-void QQuickCanvasItem::updatePolish()
+void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
 {
+    QQuickItem::itemChange(change, value);
+    if (change != QQuickItem::ItemSceneChange)
+        return;
+
     Q_D(QQuickCanvasItem);
+    if (d->available)
+        return;
+
+    if (value.window== 0)
+        return;
+
+    d->window = value.window;
+    if (d->window->openglContext() != 0) // available context == initialized
+        sceneGraphInitialized();
+    else
+        connect(d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
+}
+
+void QQuickCanvasItem::updatePolish()
+{
     QQuickItem::updatePolish();
-    if (d->texture) {
-        if (!d->renderInThread && d->dirtyRect.isValid())
-            _doPainting(d->dirtyRect);
-
-        d->texture->canvasChanged(d->canvasSize.toSize()
-                                , d->tileSize
-                                , d->canvasWindow.toAlignedRect()
-                                , d->dirtyRect.toAlignedRect()
-                                , d->smooth);
-        d->dirtyRect = QRectF();
+
+    Q_D(QQuickCanvasItem);
+
+    if (d->contextInitialized)
+        d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth);
+
+    if (d->animationCallbacks.size() > 0 && isVisible()) {
+        QMap<int, v8::Persistent<v8::Function> > animationCallbacks = d->animationCallbacks;
+        d->animationCallbacks.clear();
+
+        foreach (int key, animationCallbacks.keys()) {
+            v8::HandleScope handle_scope;
+            v8::Handle<v8::Object> self = QQmlEnginePrivate::getV8Engine(qmlEngine(this))->newQObject(this).As<v8::Object>();
+            v8::Handle<v8::Value> args[] = { v8::Uint32::New(QDateTime::currentDateTimeUtc().toTime_t()) };
+            v8::Persistent<v8::Function> f = animationCallbacks.value(key);
+            f->Call(self, 1, args);
+            f.Dispose();
+        }
+    }
+    else {
+        if (d->dirtyRect.isValid()) {
+            if (d->hasTileSize && d->hasCanvasWindow)
+                emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
+            else
+                emit paint(d->dirtyRect.toRect());
+            d->dirtyRect = QRectF();
+        }
+    }
+
+    if (d->contextInitialized) {
+        if (d->renderStrategy == QQuickCanvasItem::Cooperative)
+            update();
+        else
+            d->context->flush();
     }
 }
 
 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
 {
     Q_D(QQuickCanvasItem);
-    QQuickContext2DNode *node = static_cast<QQuickContext2DNode *>(oldNode);
+
+    if (!d->contextInitialized)
+        return 0;
+
+    QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode*>(oldNode);
     if (!node)
-        node = new QQuickContext2DNode(this);
+        node = new QSGSimpleTextureNode;
 
-    node->setTexture(d->texture);
-    node->setSize(d->canvasWindow.size());
-    node->update();
+    if (d->renderStrategy == QQuickCanvasItem::Cooperative)
+        d->context->flush();
+
+    node->setTexture(d->context->texture());
+    node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
     return node;
 }
 
-void QQuickCanvasItem::createTexture()
+/*!
+    \qmlmethod object QtQuick2::Canvas::getContext(string contextId, any... args)
+
+    Returns a drawing context or null if no context available.
+
+    The \a contextId parameter names the required context. The Canvas item
+    will return a context that implements the required drawing mode. After the
+    first call to getContext any subsequent call to getContext with the same
+    contextId will return the same context object.
+
+    If the context type is not supported or the canvas has previously been
+    requested to provide a different and incompatible context type, null will
+    be returned.
+
+    Canvas only supports a 2d context.
+*/
+
+void QQuickCanvasItem::getContext(QQmlV8Function *args)
 {
     Q_D(QQuickCanvasItem);
 
-    if (!d->texture
-      || d->texture->threadRendering() != d->renderInThread
-      || d->texture->renderTarget() != d->renderTarget) {
-        if (d->texture) {
-            d->texture->deleteLater();
-            d->texture = 0;
-        }
+    if (args->Length() < 1 || !(*args)[0]->IsString()) {
+        qmlInfo(this) << "getContext should be called with a string naming the required context type";
+        args->returnValue(v8::Null());
+        return;
+    }
 
-        if (d->renderTarget == QQuickCanvasItem::Image) {
-            d->texture = new QQuickContext2DImageTexture(d->renderInThread);
-        } else if (d->renderTarget == QQuickCanvasItem::FramebufferObject) {
-            d->texture = new QQuickContext2DFBOTexture();
-        }
+    if (!d->available) {
+        qmlInfo(this) << "Unable to use getContext() at this time, please wait for available: true";
+        args->returnValue(v8::Null());
+        return;
+    }
 
-        if (d->renderInThread && !d->texture->supportThreadRendering()) {
-            qWarning("Canvas: render target does not support thread rendering, force to non-thread rendering mode.");
-            d->renderInThread = false;
-            emit renderInThreadChanged();
-        }
+    QString contextId = QString::fromUtf16(*v8::String::Value((*args)[0]));
 
-        if (d->renderInThread)
-            connect(d->texture, SIGNAL(textureChanged()), this, SLOT(update()));
+    if (d->context != 0) {
+        if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
+            args->returnValue(d->context->v8value());
+            return;
+        }
 
-        d->texture->setItem(this);
+        qmlInfo(this) << "Canvas already initialized with a different context type";
+        args->returnValue(v8::Null());
+        return;
     }
+
+    if (createContext(contextId))
+        args->returnValue(d->context->v8value());
+    else
+        args->returnValue(v8::Null());
 }
 
-void QQuickCanvasItem::createContext()
+/*!
+    \qmlmethod long QtQuick2::Canvas::requestAnimationFrame(callback)
+
+    This function schedules callback to be invoked before composing the QtQuick
+    scene.
+*/
+
+void QQuickCanvasItem::requestAnimationFrame(QQmlV8Function *args)
 {
+    if (args->Length() < 1 || !(*args)[0]->IsFunction()) {
+        qmlInfo(this) << "requestAnimationFrame should be called with an animation callback function";
+        args->returnValue(v8::Null());
+        return;
+    }
+
     Q_D(QQuickCanvasItem);
 
-    delete d->context;
+    static int id = 0;
 
-    d->context = new QQuickContext2D(this);
+    d->animationCallbacks.insert(++id, v8::Persistent<v8::Function>::New(((*args)[0]).As<v8::Function>()));
+
+    if (isVisible())
+        polish();
 
-    QV8Engine *e = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this));
-    d->context->setV8Engine(e);
+    args->returnValue(v8::Int32::New(id));
 }
 
 /*!
-  \qmlmethod object QtQuick2::Canvas::getContext(string contextId)
+    \qmlmethod void QtQuick2::Canvas::cancelRequestAnimationFrmae(long handle)
 
-  Currently, the canvas item only supports the 2D context. If the \a contextId
-  parameter isn't provided or is "2d", then the QtQuick2::Context2D object is
-  returned, otherwise returns an invalid value.
-  */
-QDeclarativeV8Handle QQuickCanvasItem::getContext(const QString &contextId)
+    This function will cancel the animation callback referenced by \a handle.
+*/
+
+void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV8Function *args)
 {
-    Q_D(QQuickCanvasItem);
+    if (args->Length() < 1 || !(*args)[0]->IsInt32()) {
+        qmlInfo(this) << "cancelRequestAnimationFrame should be called with an animation callback id";
+        args->returnValue(v8::Null());
+        return;
+    }
 
-    if (contextId.toLower() != QLatin1String("2d"))
-        return QDeclarativeV8Handle::fromHandle(v8::Undefined());
+    d_func()->animationCallbacks.remove((*args)[0]->Int32Value());
+}
+
+
+/*!
+    \qmlmethod void QtQuick2::Canvas::requestPaint()
 
-    if (!d->context)
-        createContext();
-    return QDeclarativeV8Handle::fromHandle(d->context->v8value());
+    Request the entire visible region be re-drawn.
+
+    \sa QtQuick::Canvas::markDirty
+*/
+
+void QQuickCanvasItem::requestPaint()
+{
+    markDirty(d_func()->canvasWindow);
 }
 
 /*!
-  \qmlmethod void QtQuick2::Canvas::markDirty(rect region)
+    \qmlmethod void QtQuick2::Canvas::markDirty(rect area)
 
-    Mark the given \a region as dirty, so that when this region is visible
-    the canvas renderer will redraw it. This will trigger the "onPaint" signal
+    Mark the given \a area as dirty, so that when this area is visible the
+    canvas renderer will redraw it. This will trigger the "onPaint" signal
     handler function.
 
-    \sa QtQuick2::Canvas::paint QtQuick2::Canvas::requestPaint
-  */
-void QQuickCanvasItem::markDirty(const QRectF& region)
+    \sa QtQuick2::Canvas::paint, QtQuick2::Canvas::requestPaint
+*/
+
+void QQuickCanvasItem::markDirty(const QRectF& rect)
 {
     Q_D(QQuickCanvasItem);
-    d->dirtyRect |= region;
-    if (d->componentCompleted)
-        polish();
-    update();
+    if (!d->available)
+        return;
+
+    d->dirtyRect |= rect;
+
+    polish();
 }
 
+void QQuickCanvasItem::checkAnimationCallbacks()
+{
+    if (d_func()->animationCallbacks.size() > 0 && isVisible())
+        polish();
+}
 
 /*!
   \qmlmethod bool QtQuick2::Canvas::save(string filename)
@@ -532,8 +846,8 @@ void QQuickCanvasItem::markDirty(const QRectF& region)
    Note: calling this method will force painting the whole canvas, not just the
    current canvas visible window.
 
-   \sa canvasWindow canvasSize toDataURL
-  */
+   \sa canvasWindow, canvasSize, toDataURL
+*/
 bool QQuickCanvasItem::save(const QString &filename) const
 {
     Q_D(const QQuickCanvasItem);
@@ -541,18 +855,14 @@ bool QQuickCanvasItem::save(const QString &filename) const
     return toImage().save(url.toLocalFile());
 }
 
-QImage QQuickCanvasItem::loadedImage(const QUrl& url)
+QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url)
 {
     Q_D(QQuickCanvasItem);
     QUrl fullPathUrl = d->baseUrl.resolved(url);
-    if (!d->images.contains(fullPathUrl)) {
+    if (!d->pixmaps.contains(fullPathUrl)) {
         loadImage(url);
     }
-    QDeclarativePixmap* pix = d->images.value(fullPathUrl);
-    if (pix->isLoading() || pix->isError()) {
-        return QImage();
-    }
-    return pix->image();
+    return d->pixmaps.value(fullPathUrl);
 }
 
 /*!
@@ -563,20 +873,22 @@ QImage QQuickCanvasItem::loadedImage(const QUrl& url)
     The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
 
     Note: Only loaded images can be painted on the Canvas item.
-  \sa QtQuick2::Canvas::unloadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
-  \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
-  */
+  \sa QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
+      QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
+*/
 void QQuickCanvasItem::loadImage(const QUrl& url)
 {
     Q_D(QQuickCanvasItem);
     QUrl fullPathUrl = d->baseUrl.resolved(url);
-    if (!d->images.contains(fullPathUrl)) {
-        QDeclarativePixmap* pix = new QDeclarativePixmap();
-        d->images.insert(fullPathUrl, pix);
+    if (!d->pixmaps.contains(fullPathUrl)) {
+        QQuickPixmap* pix = new QQuickPixmap();
+        QQmlRefPointer<QQuickCanvasPixmap> canvasPix;
+        canvasPix.take(new QQuickCanvasPixmap(pix, d->window));
+        d->pixmaps.insert(fullPathUrl, canvasPix);
 
         pix->load(qmlEngine(this)
                 , fullPathUrl
-                , QDeclarativePixmap::Cache | QDeclarativePixmap::Asynchronous);
+                , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
         if (pix->isLoading())
             pix->connectFinished(this, SIGNAL(imageLoaded()));
     }
@@ -588,17 +900,13 @@ void QQuickCanvasItem::loadImage(const QUrl& url)
   Once an image is unloaded it cannot be painted by the canvas context
   unless it is loaded again.
 
-  \sa QtQuick2::Canvas::loadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
-  \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
-  */
+  \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
+      QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
+*/
 void QQuickCanvasItem::unloadImage(const QUrl& url)
 {
     Q_D(QQuickCanvasItem);
-    QUrl removeThis = d->baseUrl.resolved(url);
-    if (d->images.contains(removeThis)) {
-        delete d->images.value(removeThis);
-        d->images.remove(removeThis);
-    }
+    d->pixmaps.remove(d->baseUrl.resolved(url));
 }
 
 /*!
@@ -606,13 +914,13 @@ void QQuickCanvasItem::unloadImage(const QUrl& url)
   Returns true if the \a image failed to load.
 
   \sa QtQuick2::Canvas::loadImage
-  */
+*/
 bool QQuickCanvasItem::isImageError(const QUrl& url) const
 {
     Q_D(const QQuickCanvasItem);
     QUrl fullPathUrl = d->baseUrl.resolved(url);
-    return d->images.contains(fullPathUrl)
-        && d->images.value(fullPathUrl)->isError();
+    return d->pixmaps.contains(fullPathUrl)
+        && d->pixmaps.value(fullPathUrl)->pixmap()->isError();
 }
 
 /*!
@@ -620,37 +928,38 @@ bool QQuickCanvasItem::isImageError(const QUrl& url) const
   Returns true if the \a image is currently loading.
 
   \sa QtQuick2::Canvas::loadImage
-  */
+*/
 bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
 {
     Q_D(const QQuickCanvasItem);
     QUrl fullPathUrl = d->baseUrl.resolved(url);
-    return d->images.contains(fullPathUrl)
-        && d->images.value(fullPathUrl)->isLoading();
+    return d->pixmaps.contains(fullPathUrl)
+        && d->pixmaps.value(fullPathUrl)->pixmap()->isLoading();
 }
 /*!
   \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
   Returns true if the \a image is sucessfully loaded and ready to use.
 
   \sa QtQuick2::Canvas::loadImage
-  */
+*/
 bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
 {
     Q_D(const QQuickCanvasItem);
     QUrl fullPathUrl = d->baseUrl.resolved(url);
-    return d->images.contains(fullPathUrl)
-        && d->images.value(fullPathUrl)->isReady();
+    return d->pixmaps.contains(fullPathUrl)
+        && d->pixmaps.value(fullPathUrl)->pixmap()->isReady();
 }
 
-QImage QQuickCanvasItem::toImage(const QRectF& region) const
+QImage QQuickCanvasItem::toImage(const QRectF& rect) const
 {
     Q_D(const QQuickCanvasItem);
-    if (d->texture) {
-        if (region.isEmpty())
-            return d->texture->toImage(canvasWindow());
+    if (d->contextInitialized) {
+        if (rect.isEmpty())
+            return d->context->toImage(canvasWindow());
         else
-            return d->texture->toImage(region);
+            return d->context->toImage(rect);
     }
+
     return QImage();
 }
 
@@ -662,7 +971,7 @@ QImage QQuickCanvasItem::toImage(const QRectF& region) const
    The default \a mimeType is "image/png".
 
    \sa QtQuick2::Canvas::save
-  */
+*/
 QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
 {
     QImage image = toImage();
@@ -673,36 +982,92 @@ QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
         buffer.open(QIODevice::WriteOnly);
         QString mime = mimeType.toLower();
         QString type;
-        if (mime == QLatin1Literal("image/png")) {
-            type = QLatin1Literal("PNG");
-        } else if (mime == QLatin1Literal("image/bmp"))
-            type = QLatin1Literal("BMP");
-        else if (mime == QLatin1Literal("image/jpeg"))
-            type = QLatin1Literal("JPEG");
-        else if (mime == QLatin1Literal("image/x-portable-pixmap"))
-            type = QLatin1Literal("PPM");
-        else if (mime == QLatin1Literal("image/tiff"))
-            type = QLatin1Literal("TIFF");
-        else if (mime == QLatin1Literal("image/xpm"))
-            type = QLatin1Literal("XPM");
+        if (mime == QLatin1String("image/png")) {
+            type = QStringLiteral("PNG");
+        } else if (mime == QLatin1String("image/bmp"))
+            type = QStringLiteral("BMP");
+        else if (mime == QLatin1String("image/jpeg"))
+            type = QStringLiteral("JPEG");
+        else if (mime == QLatin1String("image/x-portable-pixmap"))
+            type = QStringLiteral("PPM");
+        else if (mime == QLatin1String("image/tiff"))
+            type = QStringLiteral("TIFF");
+        else if (mime == QLatin1String("image/xpm"))
+            type = QStringLiteral("XPM");
         else
-            return QLatin1Literal("data:,");
+            return QStringLiteral("data:,");
 
-        image.save(&buffer, type.toAscii());
+        image.save(&buffer, type.toLatin1());
         buffer.close();
-        QString dataUrl = QLatin1Literal("data:%1;base64,%2");
+        QString dataUrl = QStringLiteral("data:%1;base64,%2");
         return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
     }
-    return QLatin1Literal("data:,");
+    return QStringLiteral("data:,");
+}
+
+void QQuickCanvasItem::delayedCreate()
+{
+    Q_D(QQuickCanvasItem);
+
+    if (!d->contextInitialized && !d->contextType.isNull())
+        createContext(d->contextType);
+
+    requestPaint();
+}
+
+bool QQuickCanvasItem::createContext(const QString &contextType)
+{
+    Q_D(QQuickCanvasItem);
+
+    if (contextType == QLatin1String("2d")) {
+        if (d->contextType.compare(QLatin1String("2d"), Qt::CaseInsensitive) != 0)  {
+            d->contextType = QLatin1String("2d");
+            emit contextTypeChanged(); // XXX: can't be in setContextType()
+        }
+        initializeContext(new QQuickContext2D(this));
+        return true;
+    }
+
+    return false;
+}
+
+void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args)
+{
+    Q_D(QQuickCanvasItem);
+
+    d->context = context;
+    d->context->init(this, args);
+    d->context->setV8Engine(QQmlEnginePrivate::getV8Engine(qmlEngine(this)));
+    d->contextInitialized = true;
+    connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
+    connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted()));
+    emit contextChanged();
+}
+
+QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
+{
+    if (window.isEmpty())
+        return QRect();
+
+    const int tw = tileSize.width();
+    const int th = tileSize.height();
+    const int h1 = window.left() / tw;
+    const int v1 = window.top() / th;
+
+    const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
+    const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
+
+    return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
 }
 
 /*!
-    \qmlsignal QtQuick2::Canvas::onPaint(QtQuick2::Context2D context, rect region)
+    \qmlsignal QtQuick2::Canvas::onPaint(rect region)
 
-    This handler is called to render the \a region.
+    This handler is called to render the \a region. If a context is active it
+    can be referenced from the context property.
 
-    This signal can be triggered by QtQuick2::Canvas::markdirty, QtQuick2::Canvas::requestPaint
-    or by changing the current canvas window.
+    This signal can be triggered by QtQuick2::Canvas::markdirty,
+    QtQuick2::Canvas::requestPaint or by changing the current canvas window.
 */
 
 /*!