Refactor context2d thread logic
[profile/ivi/qtdeclarative.git] / src / quick / items / context2d / qquickcontext2d.cpp
index 6fa3644..40a9b8e 100644 (file)
@@ -61,6 +61,8 @@
 #include <qqmlengine.h>
 #include <private/qv8domerrors_p.h>
 #include <QtCore/qnumeric.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickwindowmanager_p.h>
 
 #ifdef Q_OS_QNX
 #include <ctype.h>
 
 QT_BEGIN_NAMESPACE
 /*!
-    \qmlclass Context2D QQuickContext2D
+    \qmltype Context2D
+    \instantiates QQuickContext2D
     \inqmlmodule QtQuick 2
+    \ingroup qtquick-canvas
     \since QtQuick 2.0
-    \brief The Context2D API allows you to draw 2d graphic shapes on the \c
-    Canvas item.
+    \brief Provides 2D context for shapes on a Canvas item
 
     The Context2D object can be created by \c Canvas item's \c getContext()
     method:
@@ -194,7 +197,7 @@ QFont qt_font_from_string(const QString& fontString) {
     QFont font;
      // ### this is simplified and incomplete
     // ### TODO:get code from Qt webkit
-     QStringList tokens = fontString.split(QLatin1String(" "));
+     const QStringList tokens = fontString.split(QLatin1Char(' '));
      foreach (const QString &token, tokens) {
          if (token == QLatin1String("italic"))
              font.setItalic(true);
@@ -233,7 +236,7 @@ class QV8Context2DResource : public QV8ObjectResource
 {
     V8_RESOURCE_TYPE(Context2DType)
 public:
-    QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e) {}
+    QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e), context(0) {}
     QQuickContext2D* context;
 };
 
@@ -1015,8 +1018,10 @@ static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &arg
         if (!qIsFinite(x0)
          || !qIsFinite(y0)
          || !qIsFinite(x1)
-         || !qIsFinite(y1))
+         || !qIsFinite(y1)) {
+            delete r;
             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
+        }
 
         r->brush = QLinearGradient(x0, y0, x1, y1);
         gradient->SetExternalResource(r);
@@ -1064,8 +1069,10 @@ static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &arg
          || !qIsFinite(x1)
          || !qIsFinite(r0)
          || !qIsFinite(r1)
-         || !qIsFinite(y1))
+         || !qIsFinite(y1)) {
+            delete r;
             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
+        }
 
         if (r0 < 0 || r1 < 0)
             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
@@ -1108,11 +1115,15 @@ static v8::Handle<v8::Value> ctx2d_createConicalGradient(const v8::Arguments &ar
         qreal x = args[0]->NumberValue();
         qreal y = args[1]->NumberValue();
         qreal angle = DEGREES(args[2]->NumberValue());
-        if (!qIsFinite(x) || !qIsFinite(y))
+        if (!qIsFinite(x) || !qIsFinite(y)) {
+            delete r;
             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
+        }
 
-        if (!qIsFinite(angle))
+        if (!qIsFinite(angle)) {
+            delete r;
             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
+        }
 
         r->brush = QConicalGradient(x, y, angle);
         gradient->SetExternalResource(r);
@@ -1193,7 +1204,7 @@ static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
                     patternTexture = pixelData->image;
                 }
             } else {
-                patternTexture = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
+                patternTexture = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
             }
 
             if (!patternTexture.isNull()) {
@@ -1586,8 +1597,8 @@ static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
   \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
   Adds an arc to the current subpath that lies on the circumference of the circle whose center is at the point (\c x,\cy) and whose radius is \c radius.
   \image qml-item-canvas-arcTo2.png
-  \sa  QtQuick2::Context2D::arcTo
-  See {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc}
+  \sa QtQuick2::Context2D::arcTo,
+      {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc}
   */
 static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
 {
@@ -1633,8 +1644,8 @@ static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
 
     \image qml-item-canvas-startAngle.png
     The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction.
-  \sa  QtQuick2::Context2D::arc
-  \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d context standard for arcTo}
+  \sa QtQuick2::Context2D::arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d
+      context standard for arcTo}
   */
 static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
 {
@@ -1972,20 +1983,23 @@ static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
 
 static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
 {
+    Q_UNUSED(args);
+
     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
-    return args.This();
 }
 
 static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
 {
+    Q_UNUSED(args);
+
     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
-    return args.This();
 }
 
 static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
 {
+    Q_UNUSED(args);
+
     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
-    return args.This();
 }
 // text
 /*!
@@ -2188,10 +2202,12 @@ static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
     return args.This();
 }
 /*!
-  \qmlclass QtQuick2::TextMetrics
+  \qmltype TextMetrics
     \inqmlmodule QtQuick 2
     \since QtQuick 2.0
-    \brief The Context2D TextMetrics interface.
+    \ingroup qtquick-canvas
+    \brief Provides a Context2D TextMetrics interface
+
     The TextMetrics object can be created by QtQuick2::Context2D::measureText method.
     See {http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details.
 
@@ -2267,7 +2283,7 @@ static v8::Handle<v8::Value> ctx2d_measureText(const v8::Arguments &args)
 /*!
   \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh)
   This is an overloaded function.
-  Draws the given item as \a image from source point (\a sx, \a sy) and source width \sw, source height \sh
+  Draws the given item as \a image from source point (\a sx, \a sy) and source width \a sw, source height \a sh
   onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
 
 
@@ -2300,44 +2316,51 @@ static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
     if (!r->context->state.invertibleCTM)
         return args.This();
 
-    QImage image;
+    QQmlRefPointer<QQuickCanvasPixmap> pixmap;
+
     if (args[0]->IsString()) {
         QUrl url(engine->toString(args[0]->ToString()));
         if (!url.isValid())
             V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
 
-        image = r->context->createImage(url);
+        pixmap = r->context->createPixmap(url);
     } else if (args[0]->IsObject()) {
         QQuickImage *imageItem = qobject_cast<QQuickImage*>(engine->toQObject(args[0]->ToObject()));
         QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(engine->toQObject(args[0]->ToObject()));
 
         QV8Context2DPixelArrayResource *pix = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->GetInternalField(0)->ToObject());
-        if (pix) {
-            image = pix->image;
+        if (pix && !pix->image.isNull()) {
+            pixmap.take(new QQuickCanvasPixmap(pix->image, r->context->canvas()->window()));
         } else if (imageItem) {
-            image = imageItem->image();
+            pixmap.take(r->context->createPixmap(imageItem->source()));
         } else if (canvas) {
-            image = canvas->toImage();
+            QImage img = canvas->toImage();
+            if (!img.isNull())
+                pixmap.take(new QQuickCanvasPixmap(img, canvas->window()));
         } else {
             V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
         }
     } else {
         V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
     }
+
+    if (pixmap.isNull() || !pixmap->isValid())
+        return args.This();
+
     if (args.Length() == 3) {
         dx = args[1]->NumberValue();
         dy = args[2]->NumberValue();
         sx = 0;
         sy = 0;
-        sw = image.width();
-        sh = image.height();
+        sw = pixmap->width();
+        sh = pixmap->height();
         dw = sw;
         dh = sh;
     } else if (args.Length() == 5) {
         sx = 0;
         sy = 0;
-        sw = image.width();
-        sh = image.height();
+        sw = pixmap->width();
+        sh = pixmap->height();
         dx = args[1]->NumberValue();
         dy = args[2]->NumberValue();
         dw = args[3]->NumberValue();
@@ -2365,22 +2388,28 @@ static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
      || !qIsFinite(dh))
         return args.This();
 
-    if (!image.isNull()) {
-        if (sx < 0 || sy < 0 || sw == 0 || sh == 0
-         || sx + sw > image.width() || sy + sh > image.height()
-         || sx + sw < 0 || sy + sh < 0) {
+    if (sx < 0
+    || sy < 0
+    || sw == 0
+    || sh == 0
+    || sx + sw > pixmap->width()
+    || sy + sh > pixmap->height()
+    || sx + sw < 0 || sy + sh < 0) {
             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error");
-        }
-
-        r->context->buffer()->drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
     }
 
+    r->context->buffer()->drawPixmap(pixmap, QRectF(sx, sy, sw, sh), QRectF(dx, dy, dw, dh));
+
     return args.This();
 }
 
 // pixel manipulation
 /*!
-  \qmlclass QtQuick2::CanvasImageData
+    \qmltype CanvasImageData
+    \inqmlmodule QtQuick 2
+    \ingroup qtquick-canvas
+    \brief Contains image pixel data in RGBA order
+
      The \a QtQuick2::CanvasImageData object holds the image pixel data.
 
      The \a QtQuick2::CanvasImageData object has the actual dimensions of the data stored in
@@ -2429,7 +2458,11 @@ v8::Handle<v8::Value> ctx2d_imageData_data(v8::Local<v8::String>, const v8::Acce
 }
 
 /*!
-  \qmlclass QtQuick2::CanvasPixelArray
+    \qmltype CanvasPixelArray
+    \inqmlmodule QtQuick 2
+    \ingroup qtquick-canvas
+    \brief Provides ordered and indexed access to the components of each pixel in image data
+
   The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
   The CanvasPixelArray can be accessed as normal Javascript array.
     \sa QtQuick2::CanvasImageData
@@ -2536,7 +2569,7 @@ static v8::Handle<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
                 return qt_create_image_data(w, h, engine, QImage());
             }
         } else if (args[0]->IsString()) {
-            QImage image = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
+            QImage image = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
             return qt_create_image_data(image.width(), image.height(), engine, image);
         }
     } else if (args.Length() == 2) {
@@ -2658,16 +2691,17 @@ static v8::Handle<v8::Value> ctx2d_putImageData(const v8::Arguments &args)
         }
 
         QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
-        r->context->buffer()->drawImage(image, dirtyX, dirtyY, dirtyWidth, dirtyHeight, dx, dy, dirtyWidth, dirtyHeight);
+        r->context->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
     }
     return args.This();
 }
 
 /*!
-  \qmlclass QtQuick2::CanvasGradient
+    \qmltype CanvasGradient
     \inqmlmodule QtQuick 2
     \since QtQuick 2.0
-    \brief The Context2D opaque CanvasGradient interface.
+    \ingroup qtquick-canvas
+    \brief Provides an opaque CanvasGradient interface
   */
 
 /*!
@@ -2882,7 +2916,7 @@ void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
         return;
 
-    buffer()->fillRect(x, y, w, h);
+    buffer()->fillRect(QRectF(x, y, w, h));
 }
 
 void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
@@ -2893,7 +2927,7 @@ void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
         return;
 
-    buffer()->strokeRect(x, y, w, h);
+    buffer()->strokeRect(QRectF(x, y, w, h));
 }
 
 void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
@@ -2904,7 +2938,7 @@ void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
         return;
 
-    buffer()->clearRect(x, y, w, h);
+    buffer()->clearRect(QRectF(x, y, w, h));
 }
 
 void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill)
@@ -3229,10 +3263,15 @@ static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetr
     return offset;
 }
 
+void QQuickContext2D::setGrabbedImage(const QImage& grab)
+{
+    m_grabbedImage = grab;
+    m_grabbed = true;
+}
 
-QImage QQuickContext2D::createImage(const QUrl& url)
+QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
 {
-    return m_canvas->loadedImage(url);
+    return m_canvas->loadedPixmap(url);
 }
 
 QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
@@ -3308,12 +3347,18 @@ QQuickContext2D::QQuickContext2D(QObject *parent)
     : QQuickCanvasContext(parent)
     , m_buffer(new QQuickContext2DCommandBuffer)
     , m_v8engine(0)
+    , m_windowManager(0)
+    , m_surface(0)
+    , m_glContext(0)
+    , m_thread(0)
+    , m_grabbed(false)
 {
 }
 
 QQuickContext2D::~QQuickContext2D()
 {
     delete m_buffer;
+    m_texture->deleteLater();
 }
 
 v8::Handle<v8::Object> QQuickContext2D::v8value() const
@@ -3333,16 +3378,13 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
     m_canvas = canvasItem;
     m_renderTarget = canvasItem->renderTarget();
 
-    // For the FBO target we only (currently) support Cooperative
-    if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
-        canvasItem->setRenderStrategy(QQuickCanvasItem::Cooperative);
-    }
-
+    QQuickWindow *window = canvasItem->window();
+    m_windowManager =  QQuickWindowPrivate::get(window)->windowManager;
     m_renderStrategy = canvasItem->renderStrategy();
 
     switch (m_renderTarget) {
     case QQuickCanvasItem::Image:
-        m_texture = new QQuickContext2DImageTexture(m_renderStrategy == QQuickCanvasItem::Threaded);
+        m_texture = new QQuickContext2DImageTexture;
         break;
     case QQuickCanvasItem::FramebufferObject:
         m_texture = new QQuickContext2DFBOTexture;
@@ -3355,6 +3397,29 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
     m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
     m_texture->setSmooth(canvasItem->smooth());
 
+    m_thread = QThread::currentThread();
+
+    QThread *renderThread = m_thread;
+    QThread *sceneGraphThread = window->openglContext() ? window->openglContext()->thread() : 0;
+
+    if (m_renderStrategy == QQuickCanvasItem::Threaded)
+        renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
+    else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
+        renderThread = sceneGraphThread;
+
+    if (renderThread && renderThread != QThread::currentThread())
+        m_texture->moveToThread(renderThread);
+
+    if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
+         QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->glContext();
+         m_surface = window;
+         m_glContext = new QOpenGLContext;
+         m_glContext->setFormat(cc->format());
+         m_glContext->setShareContext(cc);
+         if (renderThread != QThread::currentThread())
+             m_glContext->moveToThread(renderThread);
+    }
+
     connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
 
     reset();
@@ -3362,32 +3427,24 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
 
 void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth)
 {
-    m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth);
+    QMetaObject::invokeMethod(m_texture,
+                                                               "canvasChanged",
+                                                               Qt::AutoConnection,
+                                                               Q_ARG(QSize, canvasSize),
+                                                               Q_ARG(QSize, tileSize),
+                                                               Q_ARG(QRect, canvasWindow),
+                                                               Q_ARG(QRect, dirtyRect),
+                                                               Q_ARG(bool, smooth));
 }
 
 void QQuickContext2D::flush()
 {
-    if (!m_buffer->isEmpty()) {
-        QMutexLocker lock(&m_bufferMutex);
-        m_bufferQueue.enqueue(m_buffer);
-        m_buffer = new QQuickContext2DCommandBuffer;
-    } else
-        return;
-
-    switch (m_renderStrategy) {
-    case QQuickCanvasItem::Immediate:
-        // Cause the texture to consume paint commands immediately
-        m_texture->paint();
-        break;
-    case QQuickCanvasItem::Threaded:
-        // wake up thread to consume paint commands
-        m_texture->paint();
-        break;
-    case QQuickCanvasItem::Cooperative:
-        // NOTE: On SG Thread
-        m_texture->paint();
-        break;
-    }
+    if (m_buffer)
+        QMetaObject::invokeMethod(m_texture,
+                                                                   "paint",
+                                                                   Qt::AutoConnection,
+                                                                   Q_ARG(QQuickContext2DCommandBuffer*, m_buffer));
+    m_buffer = new QQuickContext2DCommandBuffer();
 }
 
 QSGDynamicTexture *QQuickContext2D::texture() const
@@ -3397,16 +3454,22 @@ QSGDynamicTexture *QQuickContext2D::texture() const
 
 QImage QQuickContext2D::toImage(const QRectF& bounds)
 {
-    switch (m_renderStrategy) {
-    case QQuickCanvasItem::Immediate:
-    case QQuickCanvasItem::Threaded:
-        flush();
-        break;
-    case QQuickCanvasItem::Cooperative:
-        break;
+    flush();
+    if (m_texture->thread() == QThread::currentThread())
+        m_texture->grabImage(bounds);
+    else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
+        qWarning() << "Pixel read back is not support in Cooperative mode, please try Theaded or Immediate mode";
+        return QImage();
+    } else {
+        QMetaObject::invokeMethod(m_texture,
+                                  "grabImage",
+                                  Qt::BlockingQueuedConnection,
+                                  Q_ARG(QRectF, bounds));
     }
-
-    return m_texture->toImage(bounds);
+    QImage img = m_grabbedImage;
+    m_grabbedImage = QImage();
+    m_grabbed = false;
+    return img;
 }
 
 
@@ -3611,7 +3674,7 @@ void QQuickContext2D::reset()
     m_stateStack.clear();
     m_stateStack.push(newState);
     popState();
-    m_buffer->clearRect(0, 0, m_canvas->width(), m_canvas->height());
+    m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
 }
 
 void QQuickContext2D::setV8Engine(QV8Engine *engine)
@@ -3637,7 +3700,7 @@ void QQuickContext2D::setV8Engine(QV8Engine *engine)
 
 QQuickContext2DCommandBuffer* QQuickContext2D::nextBuffer()
 {
-    QMutexLocker lock(&m_bufferMutex);
+    QMutexLocker lock(&m_mutex);
     return m_bufferQueue.isEmpty() ? 0 : m_bufferQueue.dequeue();
 }