canvasitem implementation based on painteditem
authorCharles Yin <charles.yin@nokia.com>
Mon, 16 May 2011 00:15:24 +0000 (10:15 +1000)
committerCharles Yin <charles.yin@nokia.com>
Mon, 16 May 2011 00:15:24 +0000 (10:15 +1000)
src/declarative/items/items.pri
src/declarative/items/qsgcanvasitem.cpp [new file with mode: 0644]
src/declarative/items/qsgcanvasitem_p.h [new file with mode: 0644]
src/declarative/items/qsgcontext2d.cpp [new file with mode: 0644]
src/declarative/items/qsgcontext2d_p.h [new file with mode: 0644]
src/declarative/items/qsgcontext2d_p_p.h [new file with mode: 0644]

index 3dbb4fa..d694297 100644 (file)
@@ -58,6 +58,9 @@ HEADERS += \
     $$PWD/qsgstateoperations_p.h \
     $$PWD/qsgimplicitsizeitem_p.h \
     $$PWD/qsgimplicitsizeitem_p_p.h \
+    $$PWD/qsgcanvasitem_p.h \
+    $$PWD/qsgcontext2d_p.h \
+    $$PWD/qsgcontext2d_p_p.h \
 
 SOURCES += \
     $$PWD/qsgevents.cpp \
@@ -95,6 +98,8 @@ SOURCES += \
     $$PWD/qsganimation.cpp \
     $$PWD/qsgstateoperations.cpp \
     $$PWD/qsgimplicitsizeitem.cpp \
+    $$PWD/qsgcanvasitem.cpp \
+    $$PWD/qsgcontext2d.cpp \
 
 SOURCES += \
     $$PWD/qsgshadereffectitem.cpp \
diff --git a/src/declarative/items/qsgcanvasitem.cpp b/src/declarative/items/qsgcanvasitem.cpp
new file mode 100644 (file)
index 0000000..f2eaf0d
--- /dev/null
@@ -0,0 +1,441 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qpainter.h>
+
+#include "private/qsgadaptationlayer_p.h"
+#include "qsgcanvasitem_p.h"
+#include "qsgpainteditem_p.h"
+#include "qsgcontext2d_p.h"
+#include "private/qsgpainternode_p.h"
+#include <qdeclarativeinfo.h>
+#include "qdeclarativeengine_p.h"
+#include <QtCore/QBuffer>
+
+QT_BEGIN_NAMESPACE
+
+class QSGCanvasItemPrivate : public QSGPaintedItemPrivate
+{
+public:
+    QSGCanvasItemPrivate();
+    QSGContext2D* context;
+};
+
+
+/*!
+    \internal
+*/
+QSGCanvasItemPrivate::QSGCanvasItemPrivate()
+    : QSGPaintedItemPrivate()
+    , context(0)
+{
+}
+
+/*!
+    Constructs a QSGCanvasItem with the given \a parent item.
+ */
+QSGCanvasItem::QSGCanvasItem(QSGItem *parent)
+    : QSGPaintedItem(*(new QSGCanvasItemPrivate), parent)
+{
+}
+
+/*!
+    Destroys the QSGCanvasItem.
+*/
+QSGCanvasItem::~QSGCanvasItem()
+{
+}
+
+void QSGCanvasItem::paint(QPainter *painter)
+{
+    Q_D(QSGCanvasItem);
+
+    if (d->context) {
+        d->context->paint(painter);
+        emit canvasUpdated();
+    }
+}
+
+
+QSGContext2D* QSGCanvasItem::getContext(const QString &contextId)
+{
+    Q_D(QSGCanvasItem);
+    if (contextId == QLatin1String("2d")) {
+        if (!d->context) {
+            d->context = new QSGContext2D(this);
+            connect(d->context, SIGNAL(changed()), this, SLOT(requestPaint()));
+        }
+        return d->context;
+    }
+    qDebug("Canvas:requesting unsupported context");
+    return 0;
+}
+
+void QSGCanvasItem::requestPaint()
+{
+    Q_D(QSGCanvasItem);
+    //TODO:update(d->context->dirtyRect());
+    update();
+}
+
+bool QSGCanvasItem::save(const QString &filename) const
+{
+    Q_D(const QSGCanvasItem);
+    QSGPainterNode* node = static_cast<QSGPainterNode*>(d->paintNode);
+    if (node) {
+        QImage image = node->toImage();
+        image.save(filename);
+    }
+    return false;
+}
+
+QString QSGCanvasItem::toDataURL(const QString& mimeType) const
+{
+    Q_D(const QSGCanvasItem);
+
+    QSGPainterNode* node = static_cast<QSGPainterNode*>(d->paintNode);
+    if (node) {
+        QImage image = node->toImage();
+        QByteArray ba;
+        QBuffer buffer(&ba);
+        buffer.open(QIODevice::WriteOnly);
+        QString mime = mimeType;
+        QString type;
+        if (mimeType == QLatin1String("image/bmp"))
+            type = "BMP";
+        else if (mimeType == QLatin1String("image/jpeg"))
+            type = "JPEG";
+        else if (mimeType == QLatin1String("image/x-portable-pixmap"))
+            type = "PPM";
+        else if (mimeType == QLatin1String("image/tiff"))
+            type = "TIFF";
+        else if (mimeType == QLatin1String("image/xbm"))
+            type = "XBM";
+        else if (mimeType == QLatin1String("image/xpm"))
+            type = "XPM";
+        else {
+            type = "PNG";
+            mime = QLatin1String("image/png");
+        }
+        image.save(&buffer, type.ascii());
+        buffer.close();
+        QString dataUrl = QLatin1String("data:%1;base64,%2");
+        return dataUrl.arg(mime).arg(ba.toBase64().constData());
+    }
+    return QLatin1String("data:,");
+}
+//CanvasItemTextureProvider::CanvasItemTextureProvider(QObject *parent)
+//    : QSGTextureProvider(parent)
+//    , m_ctx2d(0)
+//    , m_fbo(0)
+//    , m_multisampledFbo(0)
+//    , m_dirtyTexture(true)
+//    , m_multisamplingSupportChecked(false)
+//    , m_multisampling(false)
+//{
+//}
+
+//CanvasItemTextureProvider::~CanvasItemTextureProvider()
+//{
+//    delete m_fbo;
+//    delete m_multisampledFbo;
+//}
+
+//void CanvasItemTextureProvider::updateTexture()
+//{
+//    if (m_dirtyTexture) {
+//        if (!m_ctx2d->isDirty())
+//            return;
+//        if (m_size.isEmpty()) {
+//            m_texture = QSGTextureRef();
+//            delete m_fbo;
+//            delete m_multisampledFbo;
+//            m_multisampledFbo = m_fbo = 0;
+//            return;
+//        }
+
+//#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
+//        //create texture
+//        if (!m_fbo || m_fbo->size() != m_size )
+//        {
+//            const QGLContext *ctx = QSGContext::current->glContext();
+//            if (!m_multisamplingSupportChecked) {
+//                QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
+//                m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
+//                                && extensions.contains("GL_EXT_framebuffer_blit");
+//                m_multisamplingSupportChecked = true;
+//            }
+
+//            if (ctx->format().sampleBuffers() && m_multisampling) {
+//                delete m_fbo;
+//                delete m_multisampledFbo;
+//                QGLFramebufferObjectFormat format;
+
+//                format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+//                format.setSamples(ctx->format().samples());
+//                m_multisampledFbo = new QGLFramebufferObject(m_size, format);
+//                {
+//                    QGLFramebufferObjectFormat format;
+//                    format.setAttachment(QGLFramebufferObject::NoAttachment);
+//                    m_fbo = new QGLFramebufferObject(m_size, format);
+//                }
+
+//                QSGPlainTexture *tex = new QSGPlainTexture;
+//                tex->setTextureId(m_fbo->texture());
+//                tex->setOwnsTexture(false);
+//                tex->setHasAlphaChannel(true);
+//                setOpaque(!tex->hasAlphaChannel());
+//                m_texture = QSGTextureRef(tex);
+//            } else {
+//                delete m_fbo;
+//                QGLFramebufferObjectFormat format;
+//                format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+//                m_fbo = new QGLFramebufferObject(m_size, format);
+//                QSGPlainTexture *tex = new QSGPlainTexture;
+//                tex->setTextureId(m_fbo->texture());
+//                tex->setOwnsTexture(false);
+//                tex->setHasAlphaChannel(true);
+//                setOpaque(!tex->hasAlphaChannel());
+//                m_texture = QSGTextureRef(tex);
+//            }
+//        }
+//#endif
+
+//#ifdef QSGCANVASITEM_DEBUG
+//        qDebug() << "painting interval:" << m_elapsedTimer.nsecsElapsed();
+//        m_elapsedTimer.restart();
+//#endif
+//        //paint 2d
+//        if (m_ctx2d) {
+//            QPainter p;
+//#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
+//            if (m_multisampledFbo)
+//                p.begin(m_multisampledFbo);
+//            else if (m_fbo)
+//                p.begin(m_fbo);
+//            else
+//                return;
+//            // move the origin of coordinates to the down left corner and
+//            // scale coordinates and turn y-axis up
+//            QSize size = m_ctx2d->size();
+//            p.translate( 0, size.height());
+//            p.scale(1, -1);
+
+//            m_ctx2d->paint(&p);
+
+//            p.end();
+
+//            if (m_multisampledFbo) {
+//                QRect r(0, 0, m_fbo->width(), m_fbo->height());
+//                QGLFramebufferObject::blitFramebuffer(m_fbo, r, m_multisampledFbo, r);
+//            }
+
+//            if (m_ctx2d->requireCachedImage())
+//                m_ctx2d->setCachedImage(m_fbo->toImage());
+
+//#else
+//            m_painter.begin(m_ctx2d->paintDevice());
+//            m_ctx2d->paint(&m_painter);
+//            m_painter.end();
+
+//            if (m_texture.isNull()) {
+//                m_texture = QSGContext::current->createTexture(m_ctx2d->toImage());
+//            } else {
+//                QSGPlainTexture* t =static_cast<QSGPlainTexture*>(m_texture.texture());
+//                t->setImage(m_ctx2d->toImage());
+//            }
+//            m_ctx2d->setCachedImage(m_ctx2d->toImage());
+
+//#endif
+
+//#ifdef QSGCANVASITEM_DEBUG
+//            qDebug() << "painting time:" << m_elapsedTimer.nsecsElapsed();
+//            m_elapsedTimer.restart();
+//#endif
+//            emit painted();
+//        }
+//    }
+//}
+
+//QSGTextureRef CanvasItemTextureProvider::texture()
+//{
+//    return m_texture;
+//}
+//void CanvasItemTextureProvider::setContext2D(QSGContext2D *ctx2d)
+//{
+//    if (ctx2d && m_ctx2d != ctx2d) {
+//        m_ctx2d = ctx2d;
+//        connect(this, SIGNAL(painted()), m_ctx2d, SIGNAL(painted()));
+//    }
+//}
+//void CanvasItemTextureProvider::setRect(const QRectF &rect)
+//{
+//    if (rect == m_rect)
+//        return;
+//    m_rect = rect;
+//    markDirtyTexture();
+//}
+
+//void CanvasItemTextureProvider::setSize(const QSize &size)
+//{
+//    if (size == m_size)
+//        return;
+//    m_size = size;
+//    markDirtyTexture();
+//}
+
+//void CanvasItemTextureProvider::markDirtyTexture()
+//{
+//    m_dirtyTexture = true;
+//    emit textureChanged();
+//}
+//QSGCanvasItem::QSGCanvasItem(QSGItem *parent)
+//    : TextureItem(parent)
+//    , m_textureProvider(0)
+//    , m_context2dChanged(false)
+//    , m_context2d( new QSGContext2D(this))
+//    , m_fillMode(QSGCanvasItem::Stretch)
+//    , m_color(Qt::white)
+//{
+//    m_textureProvider = new CanvasItemTextureProvider(this);
+//    m_textureProvider->setContext2D(m_context2d);
+//    setTextureProvider(m_textureProvider, true);
+//    setFlag(QSGItem::ItemHasContents, true);
+//}
+
+//QSGCanvasItem::~QSGCanvasItem()
+//{
+//}
+
+//void QSGCanvasItem::componentComplete()
+//{
+//    m_context2d->setSize(width(), height());
+//    qDebug() << "m_context2d.size:" << m_context2d->size();
+//    connect(m_context2d, SIGNAL(changed()), this, SLOT(requestPaint()));
+//    QScriptEngine* scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this));
+//    if (scriptEngine != m_context2d->scriptEngine())
+//        m_context2d->setScriptEngine(scriptEngine);
+//    QSGItem::componentComplete();
+//}
+
+
+//void QSGCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+//{
+//    if (width() == 0 && height()
+//        && newGeometry.width() > 0 && newGeometry.height() > 0) {
+//        m_context2d->setSize(width(), height());
+//    }
+//    TextureItem::geometryChanged(newGeometry, oldGeometry);
+//}
+
+//void QSGCanvasItem::setFillMode(FillMode mode)
+//{
+//    if (m_fillMode == mode)
+//        return;
+
+//    m_fillMode = mode;
+//    update();
+//    emit fillModeChanged();
+//}
+
+//QColor QSGCanvasItem::color()
+//{
+//    return m_color;
+//}
+
+//void QSGCanvasItem::setColor(const QColor &color)
+//{
+//    if (m_color !=color) {
+//        m_color = color;
+//        colorChanged();
+//    }
+//}
+
+//QSGCanvasItem::FillMode QSGCanvasItem::fillMode() const
+//{
+//    return m_fillMode;
+//}
+
+
+
+//Node *QSGCanvasItem::updatePaintNode(Node *oldNode, UpdatePaintNodeData *data)
+//{
+//    if (width() <= 0 || height() <= 0) {
+//        delete oldNode;
+//        return 0;
+//    }
+
+//    TextureNodeInterface *node = static_cast<TextureNodeInterface *>(oldNode);
+
+//    if (node && m_context2d->isDirty()) {
+//        QRectF bounds = boundingRect();
+
+//        if (m_textureProvider) {
+//            m_textureProvider->setRect(QRectF(bounds.x(), bounds.y(), width(), height()));
+
+//            m_textureProvider->setSize(QSize(width(), height()));
+//            //m_textureProvider->setOpaque(true);
+//            m_textureProvider->setHorizontalWrapMode(QSGTextureProvider::ClampToEdge);
+//            m_textureProvider->setVerticalWrapMode(QSGTextureProvider::ClampToEdge);
+//            node->setTargetRect(bounds);
+//            node->setSourceRect(QRectF(0, 0, 1, 1));
+//            //            node->setTargetRect(image.rect());
+////            node->setSourceRect(QRectF(0, 0, 1, 1));
+////            d->textureProvider->setHorizontalWrapMode(QSGTextureProvider::ClampToEdge);
+////            d->textureProvider->setVerticalWrapMode(QSGTextureProvider::ClampToEdge);
+////            d->textureProvider->setFiltering(d->smooth ? QSGTextureProvider::Linear : QSGTextureProvider::Nearest);
+//        }
+
+//        if (m_context2dChanged) {
+//            //force textnode update the content
+//            node->setTexture(0);
+//            node->setTexture(m_textureProvider);
+//            m_context2dChanged = false;
+//        }
+//    } else {
+//        if (m_context2d->requireCachedImage())
+//            m_context2d->setCachedImage(QImage(width(), height(), QImage::Format_ARGB32_Premultiplied));
+//    }
+
+//    return TextureItem::updatePaintNode(oldNode, data);
+//}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgcanvasitem_p.h b/src/declarative/items/qsgcanvasitem_p.h
new file mode 100644 (file)
index 0000000..a358c35
--- /dev/null
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCANVASITEM_P_H
+#define QSGCANVASITEM_P_H
+
+#include "qsgpainteditem.h"
+
+#define QSGCANVASITEM_DEBUG //enable this for just DEBUG purpose!
+
+#ifdef QSGCANVASITEM_DEBUG
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+class QSGContext2D;
+class QSGCanvasItemPrivate;
+class QSGCanvasItem : public QSGPaintedItem
+{
+    Q_OBJECT
+public:
+    QSGCanvasItem(QSGItem *parent = 0);
+    ~QSGCanvasItem();
+
+signals:
+    void canvasUpdated();
+public Q_SLOTS:
+    QString toDataURL(const QString& type = QLatin1String("image/png")) const;
+    QSGContext2D* getContext(const QString & = QLatin1String("2d"));
+    void requestPaint();
+
+    // Save current canvas to disk
+    bool save(const QString& filename) const;
+
+protected:
+    void paint(QPainter *painter);
+private:
+    Q_DECLARE_PRIVATE(QSGCanvasItem)
+    friend class QSGContext2D;
+};
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGCanvasItem)
+
+QT_END_HEADER
+
+#endif //QSGCANVASITEM_P_H
diff --git a/src/declarative/items/qsgcontext2d.cpp b/src/declarative/items/qsgcontext2d.cpp
new file mode 100644 (file)
index 0000000..6f7121a
--- /dev/null
@@ -0,0 +1,2716 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgcontext2d_p.h"
+#include "qsgcontext2d_p_p.h"
+#include "private/qsgadaptationlayer_p.h"
+#include "qsgcanvasitem_p.h"
+#include <QtOpenGL/qglframebufferobject.h>
+#include <QtCore/qdebug.h>
+#include "private/qsgcontext_p.h"
+
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicseffect.h>
+#include <qdeclarativeinfo.h>
+#include <QtCore/qmath.h>
+#include "qdeclarativepixmapcache_p.h"
+#include <QtScript/QScriptEngine>
+
+QT_BEGIN_NAMESPACE
+
+static const double Q_PI   = 3.14159265358979323846;   // pi
+template <class T>
+void memcpy_vector(QVector<T>* dst, const QVector<T>& src)
+{
+    int pos = dst->size();
+    dst->resize(pos + src.size());
+    memmove(dst->data() + pos, src.constData(), sizeof(T) * src.size());
+}
+
+template <class T>
+void copy_vector(QVector<T>* dst, const QVector<T>& src)
+{
+    int pos = dst->size();
+    dst->resize(pos + src.size());
+    for (int i = 0; i < src.size(); i++) {
+        (*dst)[pos + i] = src[i];
+    }
+}
+
+// Note, this is exported but in a private header as qtopengl depends on it.
+// But it really should be considered private API
+void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
+void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
+
+#define DEGREES(t) ((t) * 180.0 / Q_PI)
+#define qClamp(val, min, max) qMin(qMax(val, min), max)
+
+static inline int extractInt(const char **name)
+{
+    int result = 0;
+    bool negative = false;
+
+    //eat leading whitespace
+    while (isspace(*name[0]))
+        ++*name;
+
+    if (*name[0] == '-') {
+        ++*name;
+        negative = true;
+    } /*else if (name[0] == '+')
+        ++name;     //ignore*/
+
+    //construct number
+    while (isdigit(*name[0])) {
+        result = result * 10 + (*name[0] - '0');
+        ++*name;
+    }
+    if (negative)
+        result = -result;
+
+    //handle optional percentage
+    if (*name[0] == '%')
+        result *= qreal(255)/100;  //### floor or round?
+
+    //eat trailing whitespace
+    while (isspace(*name[0]))
+        ++*name;
+
+    return result;
+}
+
+static bool qt_get_rgb(const QString &string, QRgb *rgb)
+{
+    const char *name = string.toLatin1().constData();
+    int len = qstrlen(name);
+
+    if (len < 5)
+        return false;
+
+    bool handleAlpha = false;
+
+    if (name[0] != 'r')
+        return false;
+    if (name[1] != 'g')
+        return false;
+    if (name[2] != 'b')
+        return false;
+    if (name[3] == 'a') {
+        handleAlpha = true;
+        if(name[3] != '(')
+            return false;
+    } else if (name[3] != '(')
+        return false;
+
+    name += 4;
+
+    int r, g, b, a = 1;
+    int result;
+
+    //red
+    result = extractInt(&name);
+    if (name[0] == ',') {
+        r = result;
+        ++name;
+    } else
+        return false;
+
+    //green
+    result = extractInt(&name);
+    if (name[0] == ',') {
+        g = result;
+        ++name;
+    } else
+        return false;
+
+    char nextChar = handleAlpha ? ',' : ')';
+
+    //blue
+    result = extractInt(&name);
+    if (name[0] == nextChar) {
+        b = result;
+        ++name;
+    } else
+        return false;
+
+    //alpha
+    if (handleAlpha) {
+        result = extractInt(&name);
+        if (name[0] == ')') {
+            a = result * 255;   //map 0-1 to 0-255
+            ++name;
+        } else
+            return false;
+    }
+
+    if (name[0] != '\0')
+        return false;
+
+    *rgb = qRgba(qClamp(r,0,255), qClamp(g,0,255), qClamp(b,0,255), qClamp(a,0,255));
+    return true;
+}
+
+//### unify with qt_get_rgb?
+static bool qt_get_hsl(const QString &string, QColor *color)
+{
+    const char *name = string.toLatin1().constData();
+    int len = qstrlen(name);
+
+    if (len < 5)
+        return false;
+
+    bool handleAlpha = false;
+
+    if (name[0] != 'h')
+        return false;
+    if (name[1] != 's')
+        return false;
+    if (name[2] != 'l')
+        return false;
+    if (name[3] == 'a') {
+        handleAlpha = true;
+        if(name[3] != '(')
+            return false;
+    } else if (name[3] != '(')
+        return false;
+
+    name += 4;
+
+    int h, s, l, a = 1;
+    int result;
+
+    //hue
+    result = extractInt(&name);
+    if (name[0] == ',') {
+        h = result;
+        ++name;
+    } else
+        return false;
+
+    //saturation
+    result = extractInt(&name);
+    if (name[0] == ',') {
+        s = result;
+        ++name;
+    } else
+        return false;
+
+    char nextChar = handleAlpha ? ',' : ')';
+
+    //lightness
+    result = extractInt(&name);
+    if (name[0] == nextChar) {
+        l = result;
+        ++name;
+    } else
+        return false;
+
+    //alpha
+    if (handleAlpha) {
+        result = extractInt(&name);
+        if (name[0] == ')') {
+            a = result * 255;   //map 0-1 to 0-255
+            ++name;
+        } else
+            return false;
+    }
+
+    if (name[0] != '\0')
+        return false;
+
+    *color = QColor::fromHsl(qClamp(h,0,255), qClamp(s,0,255), qClamp(l,0,255), qClamp(a,0,255));
+    return true;
+}
+
+//### optimize further
+QColor colorFromString(const QString &name)
+{
+    if (name.startsWith(QLatin1String("rgb"))) {
+        QRgb rgb;
+        if (qt_get_rgb(name, &rgb))
+            return QColor(rgb);
+    } else if (name.startsWith(QLatin1String("hsl"))) {
+        QColor color;
+        if (qt_get_hsl(name, &color))
+            return color;
+    }
+
+    return QColor(name);
+}
+
+
+static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
+{
+    if (compositeOperator == QLatin1String("source-over")) {
+        return QPainter::CompositionMode_SourceOver;
+    } else if (compositeOperator == QLatin1String("source-out")) {
+        return QPainter::CompositionMode_SourceOut;
+    } else if (compositeOperator == QLatin1String("source-in")) {
+        return QPainter::CompositionMode_SourceIn;
+    } else if (compositeOperator == QLatin1String("source-atop")) {
+        return QPainter::CompositionMode_SourceAtop;
+    } else if (compositeOperator == QLatin1String("destination-atop")) {
+        return QPainter::CompositionMode_DestinationAtop;
+    } else if (compositeOperator == QLatin1String("destination-in")) {
+        return QPainter::CompositionMode_DestinationIn;
+    } else if (compositeOperator == QLatin1String("destination-out")) {
+        return QPainter::CompositionMode_DestinationOut;
+    } else if (compositeOperator == QLatin1String("destination-over")) {
+        return QPainter::CompositionMode_DestinationOver;
+    } else if (compositeOperator == QLatin1String("darker")) {
+        return QPainter::CompositionMode_SourceOver;
+    } else if (compositeOperator == QLatin1String("lighter")) {
+        return QPainter::CompositionMode_SourceOver;
+    } else if (compositeOperator == QLatin1String("copy")) {
+        return QPainter::CompositionMode_Source;
+    } else if (compositeOperator == QLatin1String("xor")) {
+        return QPainter::CompositionMode_Xor;
+    }
+
+    return QPainter::CompositionMode_SourceOver;
+}
+
+static QString compositeOperatorToString(QPainter::CompositionMode op)
+{
+    switch (op) {
+    case QPainter::CompositionMode_SourceOver:
+        return QLatin1String("source-over");
+    case QPainter::CompositionMode_DestinationOver:
+        return QLatin1String("destination-over");
+    case QPainter::CompositionMode_Clear:
+        return QLatin1String("clear");
+    case QPainter::CompositionMode_Source:
+        return QLatin1String("source");
+    case QPainter::CompositionMode_Destination:
+        return QLatin1String("destination");
+    case QPainter::CompositionMode_SourceIn:
+        return QLatin1String("source-in");
+    case QPainter::CompositionMode_DestinationIn:
+        return QLatin1String("destination-in");
+    case QPainter::CompositionMode_SourceOut:
+        return QLatin1String("source-out");
+    case QPainter::CompositionMode_DestinationOut:
+        return QLatin1String("destination-out");
+    case QPainter::CompositionMode_SourceAtop:
+        return QLatin1String("source-atop");
+    case QPainter::CompositionMode_DestinationAtop:
+        return QLatin1String("destination-atop");
+    case QPainter::CompositionMode_Xor:
+        return QLatin1String("xor");
+    case QPainter::CompositionMode_Plus:
+        return QLatin1String("plus");
+    case QPainter::CompositionMode_Multiply:
+        return QLatin1String("multiply");
+    case QPainter::CompositionMode_Screen:
+        return QLatin1String("screen");
+    case QPainter::CompositionMode_Overlay:
+        return QLatin1String("overlay");
+    case QPainter::CompositionMode_Darken:
+        return QLatin1String("darken");
+    case QPainter::CompositionMode_Lighten:
+        return QLatin1String("lighten");
+    case QPainter::CompositionMode_ColorDodge:
+        return QLatin1String("color-dodge");
+    case QPainter::CompositionMode_ColorBurn:
+        return QLatin1String("color-burn");
+    case QPainter::CompositionMode_HardLight:
+        return QLatin1String("hard-light");
+    case QPainter::CompositionMode_SoftLight:
+        return QLatin1String("soft-light");
+    case QPainter::CompositionMode_Difference:
+        return QLatin1String("difference");
+    case QPainter::CompositionMode_Exclusion:
+        return QLatin1String("exclusion");
+    default:
+        break;
+    }
+    return QString();
+}
+
+bool QSGContext2DPrivate::hasShadow() const
+{
+    return state.shadowColor.isValid()
+        && state.shadowColor.alpha()
+        && (state.shadowBlur || state.shadowOffsetX || state.shadowOffsetY);
+}
+
+void QSGContext2DPrivate::clearShadow()
+{
+    state.shadowOffsetX = 0;
+    state.shadowOffsetY = 0;
+    state.shadowBlur = 0;
+    state.shadowColor = QColor();
+}
+
+QImage QSGContext2DPrivate::makeShadowImage(const QPixmap& pix)
+{
+    QImage shadowImg(pix.width() + state.shadowBlur * 2 + qAbs(state.shadowOffsetX),
+                     pix.height() + state.shadowBlur *2 + qAbs(state.shadowOffsetY),
+                     QImage::Format_ARGB32);
+    shadowImg.fill(0);
+    QPainter tmpPainter(&shadowImg);
+    tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
+    qreal shadowX = state.shadowOffsetX > 0? state.shadowOffsetX : 0;
+    qreal shadowY = state.shadowOffsetY > 0? state.shadowOffsetY : 0;
+
+    tmpPainter.drawPixmap(shadowX, shadowY, pix);
+    tmpPainter.end();
+
+    // blur the alpha channel
+    if (state.shadowBlur > 0) {
+        QImage blurred(shadowImg.size(), QImage::Format_ARGB32);
+        blurred.fill(0);
+        QPainter blurPainter(&blurred);
+        qt_blurImage(&blurPainter, shadowImg, state.shadowBlur, false, true);
+        blurPainter.end();
+        shadowImg = blurred;
+    }
+
+    // blacken the image with shadow color...
+    tmpPainter.begin(&shadowImg);
+    tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+    tmpPainter.fillRect(shadowImg.rect(), state.shadowColor);
+    tmpPainter.end();
+    return shadowImg;
+}
+
+void QSGContext2DPrivate::fillRectShadow(QPainter* p, QRectF shadowRect)
+{
+    QRectF r = shadowRect;
+    r.moveTo(0, 0);
+
+    QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32);
+    QPainter tp;
+    tp.begin(&shadowImage);
+    tp.fillRect(r, p->brush());
+    tp.end();
+    shadowImage = makeShadowImage(QPixmap::fromImage(shadowImage));
+
+    qreal dx = shadowRect.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+    qreal dy = shadowRect.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+
+    p->drawImage(dx, dy, shadowImage);
+    p->fillRect(shadowRect, p->brush());
+}
+
+void QSGContext2DPrivate::fillShadowPath(QPainter* p, const QPainterPath& path)
+{
+    QRectF r = path.boundingRect();
+    QImage img(r.size().width() + r.left() + 1,
+               r.size().height() + r.top() + 1,
+               QImage::Format_ARGB32);
+    img.fill(0);
+    QPainter tp(&img);
+    tp.fillPath(path.translated(0, 0), p->brush());
+    tp.end();
+
+    QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
+    qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+    qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+
+    p->drawImage(dx, dy, shadowImage);
+    p->fillPath(path, p->brush());
+}
+
+void QSGContext2DPrivate::strokeShadowPath(QPainter* p, const QPainterPath& path)
+{
+    QRectF r = path.boundingRect();
+    QImage img(r.size().width() + r.left() + 1,
+               r.size().height() + r.top() + 1,
+               QImage::Format_ARGB32);
+    img.fill(0);
+    QPainter tp(&img);
+    tp.strokePath(path, p->pen());
+    tp.end();
+
+    QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
+    qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+    qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+    p->drawImage(dx, dy, shadowImage);
+    p->strokePath(path, p->pen());
+}
+
+void QSGContext2DPrivate::clear()
+{
+    clearRect(0, 0, size.width(), size.height());
+}
+
+void QSGContext2DPrivate::reset()
+{
+    stateStack.clear();
+    state.matrix = QMatrix();
+    state.clipPath = QPainterPath();
+    state.strokeStyle = Qt::black;
+    state.fillStyle = Qt::black;
+    state.globalAlpha = 1.0;
+    state.lineWidth = 1;
+    state.lineCap = Qt::FlatCap;
+    state.lineJoin = Qt::MiterJoin;
+    state.miterLimit = 10;
+    state.shadowOffsetX = 0;
+    state.shadowOffsetY = 0;
+    state.shadowBlur = 0;
+    state.shadowColor = qRgba(0, 0, 0, 0);
+    state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
+    state.font = QFont();
+    state.textAlign = QSGContext2D::Start;
+    state.textBaseline = QSGContext2D::Alphabetic;
+    clear();
+}
+
+
+void QSGContext2DPrivate::updateMatrix(const QMatrix& m)
+{
+    commands.push_back(QSGContext2D::UpdateMatrix);
+    matrixes.push_back(m);
+}
+
+
+
+
+void QSGContext2DPrivate::save()
+{
+    stateStack.push(state);
+}
+
+void QSGContext2DPrivate::restore()
+{
+    if (!stateStack.isEmpty()) {
+        bool update = false;
+        QSGContext2D::State s = stateStack.pop();
+        if (state.matrix != s.matrix) {
+            updateMatrix(s.matrix);
+            update = true;
+        }
+
+        if (s.pen != state.pen) {
+            commands.push_back(QSGContext2D::UpdatePen);
+            pens.push_back(s.pen);
+            update = true;
+        }
+
+        if (s.globalAlpha != state.globalAlpha) {
+            commands.push_back(QSGContext2D::GlobalAlpha);
+            reals.push_back(s.globalAlpha);
+            update = true;
+        }
+
+        if (s.globalCompositeOperation != state.globalCompositeOperation) {
+            commands.push_back(QSGContext2D::GlobalCompositeOperation);
+            ints.push_back(s.globalCompositeOperation);
+            update = true;
+        }
+
+        if (s.font != state.font) {
+            commands.push_back(QSGContext2D::Font);
+            fonts.push_back(s.font);
+            update = true;
+        }
+
+        if (s.fillStyle != state.fillStyle) {
+            commands.push_back(QSGContext2D::FillStyle);
+            brushes.push_back(s.fillStyle);
+            update = true;
+        }
+
+        if (s.clipPath != state.clipPath) {
+            //commands.push_back(QSGContext2D::ClipPath);
+            update = true;
+        }
+
+        if (s.textAlign != state.textAlign) {
+            commands.push_back(QSGContext2D::TextAlign);
+            update = true;
+        }
+
+        if (s.textBaseline != state.textBaseline) {
+            commands.push_back(QSGContext2D::TextBaseline);
+            update = true;
+        }
+
+        if (s.shadowBlur != state.shadowBlur
+         || s.shadowColor != state.shadowColor
+         || s.shadowOffsetX != state.shadowOffsetX
+         || s.shadowOffsetY != state.shadowOffsetY) {
+            update = true;
+        }
+
+        if (update)
+            state = s;
+    }
+}
+
+void QSGContext2DPrivate::scale(qreal x, qreal y)
+{
+    state.matrix.scale(x, y);
+    updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::rotate(qreal angle)
+{
+    state.matrix.rotate(DEGREES(angle));
+    updateMatrix(state.matrix);
+}
+
+
+void QSGContext2DPrivate::translate(qreal x, qreal y)
+{
+    state.matrix.translate(x, y);
+    updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::transform(
+                                    qreal m11, qreal m12,
+                                    qreal m21, qreal m22,
+                                    qreal dx, qreal dy)
+{
+    QMatrix matrix(m11, m12, m21, m22, dx, dy);
+    state.matrix *= matrix;
+    updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::setTransform(
+                                       qreal m11, qreal m12,
+                                       qreal m21, qreal m22,
+                                       qreal dx, qreal dy)
+{
+   QMatrix matrix(m11, m12, m21, m22, dx, dy);
+   state.matrix = matrix;
+   updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::clearRect(qreal x, qreal y,
+                                    qreal w, qreal h)
+{
+    commands.push_back(QSGContext2D::ClearRect);
+    reals.push_back(x);
+    reals.push_back(y);
+    reals.push_back(w);
+    reals.push_back(h);
+}
+
+void QSGContext2DPrivate::fillRect(qreal x, qreal y,
+                                   qreal w, qreal h)
+{
+    commands.push_back(QSGContext2D::FillRect);
+    reals.push_back(x);
+    reals.push_back(y);
+    reals.push_back(w);
+    reals.push_back(h);
+}
+
+void QSGContext2DPrivate::strokeRect(qreal x, qreal y,
+                                     qreal w, qreal h)
+{
+    QPainterPath path;
+    path.addRect(x, y, w, h);
+    commands.push_back(QSGContext2D::Stroke);
+    pathes.push_back(path);
+}
+
+void QSGContext2DPrivate::beginPath()
+{
+    path = QPainterPath();
+}
+
+void QSGContext2DPrivate::closePath()
+{
+    path.closeSubpath();
+}
+
+void QSGContext2DPrivate::moveTo( qreal x, qreal y)
+{
+    path.moveTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::lineTo( qreal x, qreal y)
+{
+    path.lineTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::quadraticCurveTo(qreal cpx, qreal cpy,
+                                           qreal x, qreal y)
+{
+    path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
+                      state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::bezierCurveTo(qreal cp1x, qreal cp1y,
+                                        qreal cp2x, qreal cp2y,
+                                        qreal x, qreal y)
+{
+    path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
+                       state.matrix.map(QPointF(cp2x, cp2y)),
+                       state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::arcTo(qreal x1, qreal y1,
+                                qreal x2, qreal y2,
+                                qreal radius)
+{
+    QPointF st  = state.matrix.map(QPoint(x1, y1));
+    QPointF end = state.matrix.map(QPoint(x2, y2));
+
+    path.arcTo(st.x(), st.y(),
+                     end.x()-st.x(), end.y()-st.y(),
+                     radius, 90);
+}
+
+void QSGContext2DPrivate::rect(qreal x, qreal y,
+                               qreal w, qreal h)
+{
+    QPainterPath path;
+    path.addRect(QRectF(x, y, w, h));
+    path.addPath(state.matrix.map(path));
+}
+
+void QSGContext2DPrivate::arc(qreal xc,
+                              qreal yc,
+                              qreal radius,
+                              qreal sar,
+                              qreal ear,
+                              bool antiClockWise)
+{
+    QPainterPath path;
+
+    //### HACK
+
+    // In Qt we don't switch the coordinate system for degrees
+    // and still use the 0,0 as bottom left for degrees so we need
+    // to switch
+    sar = -sar;
+    ear = -ear;
+    antiClockWise = !antiClockWise;
+    //end hack
+
+    float sa = DEGREES(sar);
+    float ea = DEGREES(ear);
+
+    double span = 0;
+
+    double xs     = xc - radius;
+    double ys     = yc - radius;
+    double width  = radius*2;
+    double height = radius*2;
+
+    if (!antiClockWise && (ea < sa)) {
+        span += 360;
+    } else if (antiClockWise && (sa < ea)) {
+        span -= 360;
+    }
+
+    //### this is also due to switched coordinate system
+    // we would end up with a 0 span instead of 360
+    if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
+          qFuzzyCompare(qAbs(span), 360))) {
+        span   += ea - sa;
+    }
+
+    path.moveTo(QPointF(xc + radius  * qCos(sar),
+                        yc - radius  * qSin(sar)));
+
+    path.arcTo(xs, ys, width, height, sa, span);
+
+    path.addPath(state.matrix.map(path));
+}
+
+void QSGContext2DPrivate::fill()
+{
+    commands.push_back(QSGContext2D::Fill);
+    pathes.push_back(path);
+}
+
+void QSGContext2DPrivate::stroke()
+{
+    commands.push_back(QSGContext2D::Stroke);
+    pathes.push_back(path);
+//        painter->setMatrix(state.matrix, false);
+//        QPainterPath tmp = state.matrix.inverted().map(path); //why?
+//        painter->strokePath(tmp, painter->pen());
+}
+
+void QSGContext2DPrivate::clip()
+{
+    state.clipPath = path;
+    pathes.push_back(state.clipPath);
+    commands.push_back(QSGContext2D::Clip);
+}
+
+void QSGContext2DPrivate::setGlobalAlpha( qreal alpha)
+{
+    state.globalAlpha = alpha;
+    commands.push_back(QSGContext2D::GlobalAlpha);
+    reals.push_back(state.globalAlpha);
+}
+
+void QSGContext2DPrivate::setGlobalCompositeOperation( const QString &op)
+{
+    state.globalCompositeOperation = compositeOperatorFromString(op);
+    commands.push_back(QSGContext2D::GlobalCompositeOperation);
+    ints.push_back(state.globalCompositeOperation);
+}
+
+void QSGContext2DPrivate::setStrokeStyle( const QVariant &style)
+{
+    QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
+    QBrush b;
+    if (gradient) {
+        b = gradient->value();
+    } else {
+        b =  colorFromString(style.toString());
+    }
+
+    if (state.strokeStyle != b) {
+        state.strokeStyle = b;
+        state.pen.setBrush(state.strokeStyle);
+        commands.push_back(QSGContext2D::UpdatePen);
+        pens.push_back(state.pen);
+    }
+}
+void QSGContext2DPrivate::setStrokeColor(const QColor& color)
+{
+    if (state.strokeStyle != color) {
+        state.strokeStyle = color;
+        commands.push_back(QSGContext2D::UpdatePen);
+        QPen pen;
+        pen.setBrush(state.strokeStyle);
+        pens.push_back(pen);
+    }
+}
+
+void QSGContext2DPrivate::setFillColor(const QColor& color)
+{
+    if (state.fillStyle != color) {
+        state.fillStyle = color;
+        commands.push_back(QSGContext2D::UpdateBrush);
+        brushes.push_back(state.fillStyle);
+    }
+}
+
+void QSGContext2DPrivate::setFillStyle( const QVariant &style)
+{
+    QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
+    QBrush b;
+    if (gradient) {
+        b = gradient->value();
+    } else {
+        b =  colorFromString(style.toString());
+    }
+
+    if (state.fillStyle != b) {
+        state.fillStyle = b;
+        commands.push_back(QSGContext2D::UpdateBrush);
+        brushes.push_back(b);
+    }
+}
+
+
+void QSGContext2DPrivate::setLineWidth( qreal w)
+{
+    if (state.lineWidth != w) {
+        state.pen.setWidthF(w);
+        state.lineWidth = w;
+        commands.push_back(QSGContext2D::UpdatePen);
+        pens.push_back(state.pen);
+    }
+}
+
+void QSGContext2DPrivate::setLineCap( const QString& cap)
+{
+    Qt::PenCapStyle style;
+    if (cap == QLatin1String("round"))
+        style = Qt::RoundCap;
+    else if (cap == QLatin1String("square"))
+        style = Qt::SquareCap;
+    else //if (capString == "butt")
+        style = Qt::FlatCap;
+
+
+    if (state.lineCap != style) {
+        state.pen.setCapStyle(style);
+        state.lineCap = style;
+        commands.push_back(QSGContext2D::UpdatePen);
+        pens.push_back(state.pen);
+    }
+}
+
+void QSGContext2DPrivate::setLineJoin( const QString& join)
+{
+    Qt::PenJoinStyle style;
+    if (join == QLatin1String("round"))
+        style = Qt::RoundJoin;
+    else if (join == QLatin1String("bevel"))
+        style = Qt::BevelJoin;
+    else //if (joinString == "miter")
+        style = Qt::MiterJoin;
+    if (state.lineJoin != style) {
+        state.lineJoin = style;
+        state.pen.setJoinStyle(style);
+        commands.push_back(QSGContext2D::UpdatePen);
+        pens.push_back(state.pen);
+    }
+}
+
+void QSGContext2DPrivate::setMiterLimit( qreal limit)
+{
+    if (state.miterLimit != limit) {
+        state.pen.setMiterLimit(limit);
+        state.miterLimit = limit;
+        commands.push_back(QSGContext2D::UpdatePen);
+        pens.push_back(state.pen);
+    }
+}
+
+void QSGContext2DPrivate::setShadowOffsetX( qreal x)
+{
+    if (state.shadowOffsetX != x) {
+        state.shadowOffsetX = x;
+        commands.push_back(QSGContext2D::ShadowOffsetX);
+        reals.push_back(x);
+    }
+}
+
+void QSGContext2DPrivate::setShadowOffsetY( qreal y)
+{
+    if (state.shadowOffsetY != y) {
+        state.shadowOffsetY = y;
+        commands.push_back(QSGContext2D::ShadowOffsetY);
+        reals.push_back(y);
+    }
+}
+
+void QSGContext2DPrivate::setShadowBlur( qreal b)
+{
+    if (state.shadowBlur != b) {
+        state.shadowBlur = b;
+        commands.push_back(QSGContext2D::ShadowBlur);
+        reals.push_back(b);
+    }
+}
+
+void QSGContext2DPrivate::setShadowColor( const QString&  color)
+{
+    QColor c = colorFromString(color);
+    if (state.shadowColor != c) {
+        state.shadowColor = c;
+        commands.push_back(QSGContext2D::ShadowColor);
+        colors.push_back(c);
+    }
+}
+
+void QSGContext2DPrivate::setFont( const QString&  fontString)
+{
+   QFont font;
+    // ### this is simplified and incomplete
+   // ### TODO:get code from Qt webkit
+    QStringList tokens = fontString.split(QLatin1String(" "));
+    foreach (const QString &token, tokens) {
+        if (token == QLatin1String("italic"))
+            font.setItalic(true);
+        else if (token == QLatin1String("bold"))
+            font.setBold(true);
+        else if (token.endsWith(QLatin1String("px"))) {
+            QString number = token;
+            number.remove(QLatin1String("px"));
+            font.setPointSizeF(number.trimmed().toFloat());
+        } else
+            font.setFamily(token);
+    }
+
+    if (state.font != font) {
+        state.font = font;
+        commands.push_back(QSGContext2D::Font);
+        fonts.push_back(font);
+    }
+}
+
+void QSGContext2DPrivate::setTextBaseline( const QString&  baseline)
+{
+    QSGContext2D::TextBaseLineType tbl;
+     if (baseline==QLatin1String("alphabetic"))
+        tbl = QSGContext2D::Alphabetic;
+    else if (baseline == QLatin1String("hanging"))
+        tbl = QSGContext2D::Hanging;
+    else if (baseline == QLatin1String("top"))
+        tbl = QSGContext2D::Top;
+    else if (baseline == QLatin1String("bottom"))
+        tbl = QSGContext2D::Bottom;
+    else if (baseline == QLatin1String("middle"))
+        tbl = QSGContext2D::Middle;
+    else {
+        tbl = QSGContext2D::Alphabetic;
+        Q_Q(QSGContext2D);
+        qmlInfo(q) << "QSGContext2D: invalid baseline:" << baseline;
+    }
+    if (state.textBaseline != tbl) {
+        state.textBaseline = tbl;
+        commands.push_back(QSGContext2D::TextBaseline);
+        ints.push_back(tbl);
+    }
+}
+
+void QSGContext2DPrivate::setTextAlign(const QString&  align)
+{
+    QSGContext2D::TextAlignType ta;
+     if (align==QLatin1String("start"))
+        ta = QSGContext2D::Start;
+    else if (align == QLatin1String("end"))
+        ta = QSGContext2D::End;
+    else if (align == QLatin1String("left"))
+        ta = QSGContext2D::Left;
+    else if (align == QLatin1String("right"))
+        ta = QSGContext2D::Right;
+    else if (align == QLatin1String("center"))
+        ta = QSGContext2D::Center;
+    else {
+        ta = QSGContext2D::Start;
+        Q_Q(QSGContext2D);
+        qmlInfo(q)  << "QSGContext2D: invalid text align:" << align;
+    }
+     if (state.textAlign != ta) {
+         state.textAlign = ta;
+         commands.push_back(QSGContext2D::TextAlign);
+         ints.push_back(ta);
+     }
+}
+
+void QSGContext2DPrivate::fillText(const QString&  text, qreal x, qreal y)
+{
+    commands.push_back(QSGContext2D::FillText);
+    strings.push_back(text);
+    reals.push_back(x);
+    reals.push_back(y);
+    ints.push_back(state.textAlign);
+    ints.push_back(state.textBaseline);
+}
+
+
+void QSGContext2DPrivate::strokeText( const QString&  text, qreal x, qreal y)
+{
+    commands.push_back(QSGContext2D::StrokeText);
+    strings.push_back(text);
+    reals.push_back(x);
+    reals.push_back(y);
+    ints.push_back(state.textAlign);
+    ints.push_back(state.textBaseline);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy)
+{
+    commands.push_back(QSGContext2D::DrawImage1);
+    strings.push_back(url);
+    reals.push_back(dx);
+    reals.push_back(dy);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+    commands.push_back(QSGContext2D::DrawImage2);
+    strings.push_back(url);
+    reals.push_back(dx);
+    reals.push_back(dy);
+    reals.push_back(dw);
+    reals.push_back(dh);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+    commands.push_back(QSGContext2D::DrawImage3);
+    strings.push_back(url);
+    reals.push_back(sx);
+    reals.push_back(sy);
+    reals.push_back(sw);
+    reals.push_back(sh);
+    reals.push_back(dx);
+    reals.push_back(dy);
+    reals.push_back(dw);
+    reals.push_back(dh);
+}
+
+QList<int> QSGContext2DPrivate::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+    Q_Q(QSGContext2D);
+    waitingForPainting = true;
+    commands.push_back(QSGContext2D::GetImageData);
+    reals.push_back(sx);
+    reals.push_back(sy);
+    reals.push_back(sw);
+    reals.push_back(sh);
+    q->sync();
+   return imageData;
+}
+
+void QSGContext2DPrivate::putImageData(const QVariantList& imageData, qreal dx, qreal dy, qreal w, qreal h)
+{
+    QImage image = cachedImage.copy(dx, dy, w, h);
+    uchar* data = image.bits();
+    int i = 0;
+    while(i< imageData.size() && i < image.byteCount()) {
+        //the stored order in QImage:BGRA
+        //the stored order in Canvas:RGBA
+        *(data+i)   = imageData[i+2].toInt();//B
+        *(data+i+1) = imageData[i+1].toInt();//G
+        *(data+i+2) = imageData[i].toInt();//R
+        *(data+i+3) = imageData[i+3].toInt();//A
+        i+=4;
+    }
+    commands.push_back(QSGContext2D::PutImageData);
+    images.push_back(image);
+    reals.push_back(dx);
+    reals.push_back(dy);
+}
+
+void QSGContext2D::save()
+{
+    Q_D(QSGContext2D);
+    d->save();
+}
+
+
+void QSGContext2D::restore()
+{
+    Q_D(QSGContext2D);
+    d->restore();
+}
+
+
+void QSGContext2D::scale(qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->scale(x, y);
+}
+
+
+void QSGContext2D::rotate(qreal angle)
+{
+    Q_D(QSGContext2D);
+    d->rotate(angle);
+}
+
+
+void QSGContext2D::translate(qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->translate(x, y);
+}
+
+void QSGContext2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
+                          qreal dx, qreal dy)
+{
+    Q_D(QSGContext2D);
+    d->transform(m11, m12, m21, m22, dx, dy);
+}
+
+
+void QSGContext2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+                             qreal dx, qreal dy)
+{
+    Q_D(QSGContext2D);
+    d->setTransform(m11, m12, m21, m22, dx, dy);
+}
+
+QString QSGContext2D::globalCompositeOperation() const
+{
+    Q_D(const QSGContext2D);
+    return compositeOperatorToString(d->state.globalCompositeOperation);
+}
+
+void QSGContext2D::setGlobalCompositeOperation(const QString &op)
+{
+    Q_D(QSGContext2D);
+    d->setGlobalCompositeOperation(op);
+}
+
+QVariant QSGContext2D::strokeStyle() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.strokeStyle;
+}
+
+void QSGContext2D::setStrokeStyle(const QVariant &style)
+{
+    Q_D(QSGContext2D);
+    d->setStrokeStyle(style);
+}
+
+QVariant QSGContext2D::fillStyle() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.fillStyle;
+}
+
+QColor QSGContext2D::strokeColor() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.strokeStyle.color();
+}
+
+QColor QSGContext2D::fillColor() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.fillStyle.color();
+}
+
+void QSGContext2D::setFillStyle(const QVariant &style)
+{
+    Q_D(QSGContext2D);
+    d->setFillStyle(style);
+}
+void QSGContext2D::setStrokeColor(const QColor& color)
+{
+    Q_D(QSGContext2D);
+    d->setStrokeColor(color);
+}
+
+void QSGContext2D::setFillColor(const QColor& color)
+{
+    Q_D(QSGContext2D);
+    d->setFillColor(color);
+}
+
+qreal QSGContext2D::globalAlpha() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.globalAlpha;
+}
+
+void QSGContext2D::setGlobalAlpha(qreal alpha)
+{
+    Q_D(QSGContext2D);
+    d->setGlobalAlpha(alpha);
+}
+
+QSGImage *QSGContext2D::createImage(const QString &url)
+{
+    Q_D(QSGContext2D);
+//### cache image
+    QSGImage* img = new QSGImage(d->canvas);
+    img->setSource(QUrl(url));
+    return img;
+}
+
+QSGCanvasGradient *QSGContext2D::createLinearGradient(qreal x0, qreal y0,
+                                                qreal x1, qreal y1)
+{
+    QLinearGradient g(x0, y0, x1, y1);
+    return new QSGCanvasGradient(g);
+}
+
+
+QSGCanvasGradient *QSGContext2D::createRadialGradient(qreal x0, qreal y0,
+                                                qreal r0, qreal x1,
+                                                qreal y1, qreal r1)
+{
+    QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+    return new QSGCanvasGradient(g);
+}
+
+qreal QSGContext2D::lineWidth() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.lineWidth;
+}
+
+void QSGContext2D::setLineWidth(qreal w)
+{
+    Q_D(QSGContext2D);
+    d->setLineWidth(w);
+}
+
+QString QSGContext2D::lineCap() const
+{
+    Q_D(const QSGContext2D);
+    switch(d->state.lineCap) {
+    case Qt::RoundCap:
+        return QLatin1String("round");
+    case Qt::FlatCap:
+        return QLatin1String("butt");
+    case Qt::SquareCap:
+        return QLatin1String("square");
+    default:
+        break;
+    }
+    return QLatin1String("");
+}
+
+void QSGContext2D::setLineCap(const QString &capString)
+{
+    Q_D(QSGContext2D);
+    d->setLineCap(capString);
+}
+
+QString QSGContext2D::lineJoin() const
+{
+    Q_D(const QSGContext2D);
+    switch (d->state.lineJoin) {
+    case Qt::RoundJoin:
+        return QLatin1String("round");
+    case Qt::BevelJoin:
+        return QLatin1String("bevel");
+    case Qt::MiterJoin:
+        return QLatin1String("miter");
+    default:
+        break;
+    }
+    return QLatin1String("");
+}
+
+void QSGContext2D::setLineJoin(const QString &joinString)
+{
+    Q_D(QSGContext2D);
+    d->setLineJoin(joinString);
+}
+
+qreal QSGContext2D::miterLimit() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.miterLimit;
+}
+
+void QSGContext2D::setMiterLimit(qreal m)
+{
+    Q_D(QSGContext2D);
+    d->setMiterLimit(m);
+}
+
+void QSGContext2D::setShadowOffsetX(qreal x)
+{
+    Q_D(QSGContext2D);
+    d->setShadowOffsetX(x);
+}
+
+void QSGContext2D::setShadowOffsetY(qreal y)
+{
+    Q_D(QSGContext2D);
+    d->setShadowOffsetY(y);
+}
+
+void QSGContext2D::setShadowBlur(qreal b)
+{
+    Q_D(QSGContext2D);
+    d->setShadowBlur(b);
+}
+
+void QSGContext2D::setShadowColor(const QString &str)
+{
+    Q_D(QSGContext2D);
+    d->setShadowColor(str);
+}
+
+QString QSGContext2D::textBaseline() const
+{
+    Q_D(const QSGContext2D);
+    switch(d->state.textBaseline) {
+    case QSGContext2D::Alphabetic:
+        return QLatin1String("alphabetic");
+    case QSGContext2D::Hanging:
+        return QLatin1String("hanging");
+    case QSGContext2D::Top:
+        return QLatin1String("top");
+    case QSGContext2D::Bottom:
+        return QLatin1String("bottom");
+    case QSGContext2D::Middle:
+        return QLatin1String("middle");
+    default:
+        break;
+    }
+    return QLatin1String("alphabetic");
+}
+
+void QSGContext2D::setTextBaseline(const QString &baseline)
+{
+    Q_D(QSGContext2D);
+    d->setTextBaseline(baseline);
+}
+
+QString QSGContext2D::textAlign() const
+{
+    Q_D(const QSGContext2D);
+    switch(d->state.textAlign) {
+    case QSGContext2D::Start:
+        return QLatin1String("start");
+   case QSGContext2D::End:
+        return QLatin1String("end");
+   case QSGContext2D::Left:
+        return QLatin1String("left");
+   case QSGContext2D::Right:
+        return QLatin1String("right");
+   case QSGContext2D::Center:
+        return QLatin1String("center");
+    default:
+        break;
+    }
+    return QLatin1String("start");
+}
+
+void QSGContext2D::setTextAlign(const QString &align)
+{
+    Q_D(QSGContext2D);
+    d->setTextAlign(align);
+
+    d->commands.push_back(QSGContext2D::TextAlign);
+    d->ints.push_back(d->state.textAlign);
+}
+
+void QSGContext2D::setFont(const QString &fontString)
+{
+    Q_D(QSGContext2D);
+    d->setFont(fontString);
+}
+
+QString QSGContext2D::font() const
+{
+    //### TODO
+    Q_D(const QSGContext2D);
+    return d->state.font.toString();
+}
+
+qreal QSGContext2D::shadowOffsetX() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.shadowOffsetX;
+}
+
+qreal QSGContext2D::shadowOffsetY() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.shadowOffsetY;
+}
+
+
+qreal QSGContext2D::shadowBlur() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.shadowBlur;
+}
+
+
+QString QSGContext2D::shadowColor() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.shadowColor.name();
+}
+
+
+void QSGContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
+{
+    Q_D(QSGContext2D);
+    d->clearRect(x, y, w, h);
+}
+
+void QSGContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
+{
+    Q_D(QSGContext2D);
+    d->fillRect(x, y, w, h);
+}
+
+int QSGContext2DPrivate::baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics)
+{
+    int offset = 0;
+    switch (value) {
+    case QSGContext2D::Top:
+        break;
+    case QSGContext2D::Alphabetic:
+    case QSGContext2D::Middle:
+    case QSGContext2D::Hanging:
+        offset = metrics.ascent();
+        break;
+    case QSGContext2D::Bottom:
+        offset = metrics.height();
+       break;
+    }
+    return offset;
+}
+
+int QSGContext2DPrivate::textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
+{
+    int offset = 0;
+    if (value == QSGContext2D::Start)
+        value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right;
+    else if (value == QSGContext2D::End)
+        value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left;
+    switch (value) {
+    case QSGContext2D::Center:
+        offset = metrics.width(text)/2;
+        break;
+    case QSGContext2D::Right:
+        offset = metrics.width(text);
+    case QSGContext2D::Left:
+    default:
+        break;
+    }
+    return offset;
+}
+
+void QSGContext2D::fillText(const QString &text, qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->fillText(text, x, y);
+}
+
+void QSGContext2D::strokeText(const QString &text, qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->strokeText(text, x, y);
+}
+
+void QSGContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
+{
+    Q_D(QSGContext2D);
+    d->strokeRect(x, y, w, h);
+}
+
+void QSGContext2D::beginPath()
+{
+    Q_D(QSGContext2D);
+    d->beginPath();
+}
+
+
+void QSGContext2D::closePath()
+{
+    Q_D(QSGContext2D);
+    d->closePath();
+}
+
+
+void QSGContext2D::moveTo(qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->moveTo(x, y);
+}
+
+
+void QSGContext2D::lineTo(qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->lineTo(x, y);
+}
+
+
+void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->quadraticCurveTo(cpx, cpy, x, y);
+}
+
+
+void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
+                              qreal cp2x, qreal cp2y, qreal x, qreal y)
+{
+    Q_D(QSGContext2D);
+    d->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+}
+
+
+void QSGContext2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
+{
+    Q_D(QSGContext2D);
+    d->arcTo(x1, y1, x2, y2, radius);
+}
+
+
+void QSGContext2D::rect(qreal x, qreal y, qreal w, qreal h)
+{
+    Q_D(QSGContext2D);
+    d->rect(x, y, w, h);
+}
+
+void QSGContext2D::arc(qreal xc, qreal yc, qreal radius,
+                    qreal sar, qreal ear,
+                    bool anticlockwise)
+{
+    Q_D(QSGContext2D);
+    d->arc(xc, yc, radius, sar, ear, anticlockwise);
+}
+
+
+void QSGContext2D::fill()
+{
+    Q_D(QSGContext2D);
+    d->fill();
+}
+
+
+void QSGContext2D::stroke()
+{
+    Q_D(QSGContext2D);
+    d->stroke();
+}
+
+
+void QSGContext2D::clip()
+{
+    Q_D(QSGContext2D);
+    d->clip();
+}
+
+
+bool QSGContext2D::isPointInPath(qreal x, qreal y) const
+{
+    Q_D(const QSGContext2D);
+    return d->path.contains(QPointF(x, y));
+}
+
+
+QList<int> QSGContext2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+    Q_D(QSGContext2D);
+    return d->getImageData(sx, sy, sw, sh);
+}
+
+void QSGContext2D::putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h)
+{
+    Q_D(QSGContext2D);
+    return d->putImageData(imageData.toList(), x, y, w, h);
+}
+
+QSGContext2D::QSGContext2D(QObject *parent)
+    : QObject(*(new QSGContext2DPrivate()), parent)
+{
+    Q_D(QSGContext2D);
+    d->canvas = qobject_cast<QSGCanvasItem*>(parent);
+}
+
+QSGContext2D::QSGContext2D(QSGContext2D *orig, QSGContext2DWorkerAgent* agentData)
+    : QObject(*(new QSGContext2DPrivate()), 0)
+{
+    Q_D(QSGContext2D);
+    d->agent = 0;
+    d->agentData = agentData;
+    if (d->agentData) {
+        d->agentData->orig = orig;
+    }
+    d->canvas = qobject_cast<QSGCanvasItem*>(orig);
+}
+
+QSGContext2D::~QSGContext2D()
+{
+    Q_D(QSGContext2D);
+    if (d->agent) {
+        d->agentData->syncDone.wakeAll();
+        d->agent->release();
+    }
+}
+
+bool QSGContext2D::isDirty() const
+{
+    Q_D(const QSGContext2D);
+    return !d->commands.isEmpty();
+}
+
+QScriptValue QSGContext2D::scriptValue() const
+{
+    Q_D(const QSGContext2D);
+    return d->scriptValue;
+}
+
+void QSGContext2D::setScriptEngine(QScriptEngine *eng)
+{
+    Q_D(QSGContext2D);
+    if (d->scriptEngine != eng) {
+        d->scriptEngine = eng;
+//        QScriptValue agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent"));
+//        if (!agent.isValid()) {
+//            d->scriptEngine->evaluate(QLatin1String(
+//                "(function CanvasImageData(w, h, d) {"
+//                "     this.widht = w;"
+//                "     this.height = h;"
+//                "     this.data = d;"
+//                "  })"));
+//            d->scriptEngine->evaluate(agentScript());
+//            agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent"));
+//            if (!agent.isValid()) {
+//                qWarning() << "QSGContext2D:error when evaluating context2d script value!";
+//                d->scriptValue = QScriptValue();
+//                return;
+//            }
+//        }
+//        QScriptValue o = d->scriptEngine->newQObject(this);
+//        d->scriptValue = agent.construct(QScriptValueList() << o);
+    }
+}
+
+QScriptEngine *QSGContext2D::scriptEngine() const
+{
+    Q_D(const QSGContext2D);
+    return d->scriptEngine;
+}
+
+void QSGContext2D::addref()
+{
+    Q_D(QSGContext2D);
+    Q_ASSERT(d->agentData);
+    d->agentData->ref.ref();
+}
+
+void QSGContext2D::release()
+{
+    Q_D(QSGContext2D);
+    Q_ASSERT(d->agentData);
+    if (!d->agentData->ref.deref()) {
+        deleteLater();
+    }
+}
+
+
+bool QSGContext2D::inWorkerThread() const
+{
+    Q_D(const QSGContext2D);
+    return d->agentData != 0;
+}
+const QString& QSGContext2D::agentScript() const
+{
+    static QString script;
+    if (script.isEmpty()) {
+        script = QString::fromLatin1(
+        "function CanvasImageData(w, h, d) {"
+        "     this.width = w;"
+        "     this.height = h;"
+        "     this.data = d;"
+        "}"
+        "function Context2DAgent(_ctx2d) {"
+        "  this._ctx = _ctx2d;"
+        "  this._fillColor = '#000000';"
+        "  this._fillStyle = '#000000';"
+        "  this._strokeColor = '#000000';"
+        "  this._strokeStyle = '#000000';"
+        "  this._globalCompositeOperation = \"source-over\";"
+        "  this._commands = [];"
+        "  this.createImageData = function() {"
+        "    var d = null;"
+        "    if (arguments.length == 1 && arguments[0] instanceof CanvasImageData) {"
+        "      d = new CanvasImageData(arguments[0].width,"
+        "                              arguments[0].height,"
+        "                              new Array(arguments[0].width * arguments[0].height * 4));"
+        "    } else if (arguments.length == 2) {"
+        "      d = new CanvasImageData(arguments[0], arguments[1], new Array(arguments[0] * arguments[1] * 4));"
+        "    }"
+        "    if (d)"
+        "      for (var i=0; i<d.data.length; i++)"
+        "        d.data[i] = 255;"
+        "    return d;"
+        "  };"
+        "  this.getImageData = function(sx, sy, sw, sh) {"
+        "    var imageData = new CanvasImageData(sw, sh, this._ctx.getImageData(sx, sy, sw, sh));"
+        "    return imageData;"
+        "  };"
+        "  this.sync = function() {"
+        "    this._ctx.processCommands(this._commands);"
+        "    this._commands.length = 0;"
+        "  };");
+
+        script.append(QString::fromLatin1(
+                        "this.save = function() {"
+                        "  this._commands.push([%1]);"
+                        "};").arg(Save));
+
+        script.append(QString::fromLatin1(
+                        "this.restore = function() {"
+                        "  this._commands.push([%1]);"
+                        "};").arg(Restore));
+
+        script.append(QString::fromLatin1(
+                        "this.scale = function(x, y) {"
+                        "  this._commands.push([%1, x, y]);"
+                        "};").arg(Scale));
+
+        script.append(QString::fromLatin1(
+                        "this.createImage = function(url) {"
+                          "  return this._ctx.createImage(url);"
+                        "};"));
+
+        script.append(QString::fromLatin1(
+                        "this.rotate = function(x) {"
+                        "  this._commands.push([%1, x]);"
+                        "};").arg(Rotate));
+
+        script.append(QString::fromLatin1(
+                        "this.translate = function(x, y) {"
+                        "  this._commands.push([%1, x, y]);"
+                        "};").arg(Translate));
+
+        script.append(QString::fromLatin1(
+                        "this.transform = function(a1, a2, a3, a4, a5, a6) {"
+                        "  this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+                        "};").arg(Transform));
+
+        script.append(QString::fromLatin1(
+                        "this.setTransform = function(a1, a2, a3, a4, a5, a6) {"
+                        "  this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+                        "};").arg(SetTransform));
+
+        script.append(QString::fromLatin1(
+                        "this.clearRect = function(x, y, w, h) {"
+                        "  this._commands.push([%1, x, y, w, h]);"
+                        "};").arg(ClearRect));
+
+        script.append(QString::fromLatin1(
+                        "this.fillRect = function(x, y, w, h) {"
+                        "  this._commands.push([%1, x, y, w, h]);"
+                        "};").arg(FillRect));
+
+        script.append(QString::fromLatin1(
+                        "this.strokeRect = function(x, y, w, h) {"
+                        "  this._commands.push([%1, x, y, w, h]);"
+                        "};").arg(StrokeRect));
+
+        script.append(QString::fromLatin1(
+                        "this.beginPath = function() {"
+                        "  this._commands.push([%1]);"
+                        "};").arg(BeginPath));
+
+        script.append(QString::fromLatin1(
+                        "this.closePath = function() {"
+                        "  this._commands.push([%1]);"
+                        "};").arg(ClosePath));
+
+        script.append(QString::fromLatin1(
+                        "this.moveTo = function(x, y) {"
+                        "  this._commands.push([%1, x, y]);"
+                        "};").arg(MoveTo));
+
+        script.append(QString::fromLatin1(
+                        "this.lineTo = function(x, y) {"
+                        "  this._commands.push([%1, x, y]);"
+                        "};").arg(LineTo));
+
+        script.append(QString::fromLatin1(
+                        "this.quadraticCurveTo = function(a1, a2, a3, a4) {"
+                        "  this._commands.push([%1, a1, a2, a3, a4]);"
+                        "};").arg(QuadraticCurveTo));
+
+        script.append(QString::fromLatin1(
+                        "this.bezierCurveTo = function(a1, a2, a3, a4, a5, a6) {"
+                        "  this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+                        "};").arg(BezierCurveTo));
+
+        script.append(QString::fromLatin1(
+                        "this.arcTo = function(x1, y1, x2, y2, radius) {"
+                        "  this._commands.push([%1, x1, y1, x2, y2, radius]);"
+                        "};").arg(ArcTo));
+
+        script.append(QString::fromLatin1(
+                        "this.rect = function(x, y, w, h) {"
+                        "  this._commands.push([%1, x, y, w, h]);"
+                        "};").arg(Rect));
+
+        script.append(QString::fromLatin1(
+                        "this.rect = function(x, y, radius, startAngle, endAngle, anticlockwise) {"
+                        "  this._commands.push([%1, x, y, radius, startAngle, endAngle, anticlockwise]);"
+                        "};").arg(Arc));
+
+        script.append(QString::fromLatin1(
+                        "this.fill = function() {"
+                        "  this._commands.push([%1]);"
+                        "};").arg(Fill));
+
+        script.append(QString::fromLatin1(
+                        "this.stroke = function() {"
+                        "  this._commands.push([%1]);"
+                        "};").arg(Stroke));
+
+        script.append(QString::fromLatin1(
+                        "this.clip = function() {"
+                        "  this._commands.push([%1]);"
+                        "};").arg(Clip));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"globalAlpha\", function() {"
+        "    return this._globalAlpha;"
+        "  });"
+        "  this.__defineSetter__(\"globalAlpha\", function(v) {"
+        "    this._globalAlpha = v;"
+        "    this._commands.push([%1, v]);"
+        "  });").arg(GlobalAlpha));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"globalCompositeOperation\", function() {"
+        "    return this._globalCompositeOperation;"
+        "  });"
+        "  this.__defineSetter__(\"globalCompositeOperation\", function(v) {"
+        "    this._globalCompositeOperation = v;"
+        "    this._commands.push([%1, v]);"
+        "  });").arg(GlobalCompositeOperation));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"strokeStyle\", function() {return this._strokeStyle; });"
+        "  this.__defineSetter__(\"strokeStyle\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._strokeStyle = v;"
+        "  });").arg(StrokeStyle));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"fillStyle\", function() {return this._fillStyle; });"
+        "  this.__defineSetter__(\"fillStyle\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._fillStyle = v;"
+        "  });").arg(FillStyle));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"strokeColor\", function() {return this._strokeColor; });"
+        "  this.__defineSetter__(\"strokeColor\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._strokeColor = v;"
+        "  });").arg(StrokeColor));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"fillColor\", function() {return this._fillColor; });"
+        "  this.__defineSetter__(\"fillColor\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._fillColor = v;"
+        "  });").arg(FillColor));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"lineWidth\", function() {return this._lineWidth; });"
+        "  this.__defineSetter__(\"lineWidth\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._lineWidth = v;"
+        "  });").arg(LineWidth));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"lineCap\", function() {return this._lineCap; });"
+        "  this.__defineSetter__(\"lineCap\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._lineCap = v;"
+        "  });").arg(LineCap));
+
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"lineJoin\", function() {return this._lineJoin; });"
+        "  this.__defineSetter__(\"lineJoin\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._lineJoin = v;"
+        "  });").arg(LineJoin));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"miterLimit\", function() {return this._miterLimit; });"
+        "  this.__defineSetter__(\"miterLimit\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._miterLimit = v;"
+        "  });").arg(MiterLimit));
+
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"shadowOffsetX\", function() {return this._shadowOffsetX; });"
+        "  this.__defineSetter__(\"shadowOffsetX\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._shadowOffsetX = v;"
+        "  });").arg(ShadowOffsetX));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"shadowOffsetY\", function() {return this._shadowOffsetY; });"
+        "  this.__defineSetter__(\"shadowOffsetY\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._shadowOffsetY = v;"
+        "  });").arg(ShadowOffsetY));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"shadowBlur\", function() {return this._shadowBlur; });"
+        "  this.__defineSetter__(\"shadowBlur\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._shadowBlur = v;"
+        "  });").arg(ShadowBlur));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"shadowColor\", function() {return this._shadowColor; });"
+        "  this.__defineSetter__(\"shadowColor\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._shadowColor = v;"
+        "  });").arg(ShadowColor));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"font\", function() {return this._font; });"
+        "  this.__defineSetter__(\"font\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._font = v;"
+        "  });").arg(Font));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"textBaseline\", function() {return this._textBaseline; });"
+        "  this.__defineSetter__(\"textBaseline\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._textBaseline = v;"
+        "  });").arg(TextBaseline));
+
+        script.append(QString::fromLatin1(
+        "  this.__defineGetter__(\"textAlign\", function() {return this._textAlign; });"
+        "  this.__defineSetter__(\"textAlign\", function(v) {"
+        "    this._commands.push([%1, v]);"
+        "    this._textAlign = v;"
+        "  });").arg(TextAlign));
+
+        script.append(QString::fromLatin1(
+                        "this.fillText = function(text, x, y) {"
+                        "  this._commands.push([%1, text, x, y]);"
+                        "};").arg(FillText));
+
+        script.append(QString::fromLatin1(
+                        "this.strokeText = function(text, x, y) {"
+                        "  this._commands.push([%1, text, x, y]);"
+                        "};").arg(StrokeText));
+
+        script.append(QString::fromLatin1(
+                        "this.drawImage = function() {"
+                          "  if (arguments.length == 3) {"
+                          "     this._commands.push([%1, arguments[0], arguments[1], arguments[2]]);"
+                          "  } else if (arguments.length == 5) {"
+                          "     this._commands.push([%2, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]]);"
+                          "  } else if (arguments.length == 9) {"
+                          "     this._commands.push([%3, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]]);}"
+                        "};").arg(DrawImage1).arg(DrawImage2).arg(DrawImage3));
+
+        script.append(QString::fromLatin1(
+                        "this.putImageData = function() {"
+                          "  var dx = arguments[1];"
+                          "  var dy = arguments[2];"
+                          "  if (arguments.length == 3) {"
+                          "     this._commands.push([%1, arguments[0].data, dx, dy, arguments[0].width, arguments[0].height]);"
+                          "  } else if (arguments.length == 7) {"
+                          "     var dirtyX = arguments[3];"
+                          "     var dirtyY = arguments[4];"
+                          "     var dirtyWidth = arguments[5];"
+                          "     var dirtyHeight = arguments[6];"
+                          "     var width = arguments[0].width;"
+                          "     var height = arguments[0].heigh;"
+                          "     var filteredData = arguments[0].data.filter(function(element, index, array){"
+                          "                                            var x=index/width;"
+                          "                                            var y=index%width-1;"
+                          "                                            return x >= dirtyX && x < dirtyX+dirtyWidth"
+                          "                                                && y >= dirtyY && y < dirtyY+dirtyHeight;"
+                          "                                           });"
+                          "     this._commands.push([%2, filteredData, dx, dy, dirtyWidth, dirtyHeight]);"
+                          "  }"
+                        "};").arg(PutImageData).arg(PutImageData));
+        script.append(QString::fromLatin1("}"));
+    }
+    return script;
+}
+
+QSGContext2D *QSGContext2D::agent()
+{
+    Q_D(QSGContext2D);
+
+    if (d->agent)
+        return d->agent;
+
+    d->agent = new QSGContext2D(this, new QSGContext2DWorkerAgent);
+    connect(this, SIGNAL(painted()), d->agent, SIGNAL(painted()));
+    d->agent->setSize(size());
+    return d->agent;
+
+}
+void QSGContext2D::processCommands(const QScriptValue& commands)
+{
+#ifdef QSGCANVASITEM_DEBUG
+    QElapsedTimer t;
+    t.start();
+#endif
+    int ii = 0;
+    if (commands.isArray()) {
+        QScriptValue cmd = commands.property(ii);
+        while(cmd.isValid()) {
+            processCommand(cmd);
+            ii++;
+            cmd = commands.property(ii);
+        }
+    }
+
+#ifdef QSGCANVASITEM_DEBUG
+    qDebug() << "processed" << ii << "commands in " << t.nsecsElapsed() << "nsecs";
+#endif
+    sync();
+}
+
+void QSGContext2D::sync()
+{
+    Q_D(QSGContext2D);
+
+#ifdef QSGCANVASITEM_DEBUG
+    QElapsedTimer t;
+    t.start();
+#endif
+    if (d->agentData) {
+        if (d->agentData->ref == 1) return;
+
+        Sync *s = new Sync;
+        s->data = d->agentData;
+
+        d->agentData->mutex.lock();
+        QCoreApplication::postEvent(this, s);
+        d->agentData->syncDone.wait(&d->agentData->mutex);
+        d->agentData->mutex.unlock();
+    } else {
+        //qmlInfo(this) << "Context2D sync() can only be called from a WorkerScript;";
+        emit changed();
+    }
+
+#ifdef QSGCANVASITEM_DEBUG
+    qDebug() << "syncing time:" << t.nsecsElapsed();
+#endif
+}
+
+
+bool QSGContext2D::event(QEvent *e)
+{
+    Q_D(QSGContext2D);
+    if (e->type() == QEvent::User && d->agentData) {
+        QMutexLocker locker(&d->agentData->mutex);
+        Sync *s = static_cast<Sync *>(e);
+
+        QSGContext2DPrivate* origin_d = static_cast<QSGContext2DPrivate*>(s->data->orig->d_func());
+
+        //quick copy
+        memcpy_vector<PaintCommand>(&origin_d->commands, d->commands);
+        memcpy_vector<int>(&origin_d->ints, d->ints);
+        memcpy_vector<qreal>(&origin_d->reals, d->reals);
+        memcpy_vector<QColor>(&origin_d->colors, d->colors);
+        memcpy_vector<QMatrix>(&origin_d->matrixes, d->matrixes);
+        memcpy_vector<QSize>(&origin_d->sizes, d->sizes);
+
+        //slow copy
+        copy_vector<QString>(&origin_d->strings, d->strings);
+        copy_vector<QVariant>(&origin_d->variants, d->variants);
+        copy_vector<QPen>(&origin_d->pens, d->pens);
+        copy_vector<QBrush>(&origin_d->brushes, d->brushes);
+        copy_vector<QPainterPath>(&origin_d->pathes, d->pathes);
+        copy_vector<QFont>(&origin_d->fonts, d->fonts);
+        copy_vector<QImage>(&origin_d->images, d->images);
+        origin_d->state = d->state;
+        d->clearCommands();
+
+        if (d->waitingForPainting) {
+            d->imageData.clear();
+            origin_d->imageData.clear();
+            emit s->data->orig->changed();
+            while(origin_d->imageData.isEmpty()) {
+                QCoreApplication::processEvents();
+            }
+            d->imageData = origin_d->imageData;
+            d->waitingForPainting = false;
+            qDebug() << "imageData size:" << d->imageData.size();
+        } else {
+            emit s->data->orig->changed();
+        }
+
+        d->agentData->syncDone.wakeAll();
+        return true;
+    }
+    return QObject::event(e);
+}
+
+void QSGContext2D::processCommand(const QScriptValue& cmd)
+{
+    int action = cmd.property(0).toInt32();
+    switch (action) {
+    case QSGContext2D::Save:
+        save();
+        break;
+    case QSGContext2D::Restore:
+        restore();
+        break;
+    case QSGContext2D::Scale:
+        scale(cmd.property(1).toNumber(), cmd.property(2).toNumber());
+        break;
+    case QSGContext2D::Rotate:
+        rotate(cmd.property(1).toNumber());
+        break;
+    case QSGContext2D::Translate:
+        translate(cmd.property(1).toNumber(), cmd.property(2).toNumber());
+        break;
+    case QSGContext2D::Transform:
+        transform(cmd.property(1).toNumber(),
+                  cmd.property(2).toNumber(),
+                  cmd.property(3).toNumber(),
+                  cmd.property(4).toNumber(),
+                  cmd.property(5).toNumber(),
+                  cmd.property(6).toNumber());
+        break;
+    case QSGContext2D::SetTransform:
+        setTransform(cmd.property(1).toNumber(),
+                  cmd.property(2).toNumber(),
+                  cmd.property(3).toNumber(),
+                  cmd.property(4).toNumber(),
+                  cmd.property(5).toNumber(),
+                  cmd.property(6).toNumber());
+        break;
+    case QSGContext2D::ClearRect:
+        clearRect(cmd.property(1).toNumber(),
+                  cmd.property(2).toNumber(),
+                  cmd.property(3).toNumber(),
+                  cmd.property(4).toNumber());
+        break;
+    case QSGContext2D::FillRect:
+        fillRect(cmd.property(1).toNumber(),
+                 cmd.property(2).toNumber(),
+                 cmd.property(3).toNumber(),
+                 cmd.property(4).toNumber());
+        break;
+    case QSGContext2D::StrokeRect:
+        strokeRect(cmd.property(1).toNumber(),
+                   cmd.property(2).toNumber(),
+                   cmd.property(3).toNumber(),
+                   cmd.property(4).toNumber());
+        break;
+    case QSGContext2D::BeginPath:
+        beginPath();
+        break;
+    case QSGContext2D::ClosePath:
+        closePath();
+        break;
+    case QSGContext2D::MoveTo:
+        moveTo(cmd.property(1).toNumber(),
+               cmd.property(2).toNumber());
+        break;
+    case QSGContext2D::LineTo:
+        lineTo(cmd.property(1).toNumber(),
+               cmd.property(2).toNumber());
+        break;
+    case QSGContext2D::QuadraticCurveTo:
+        quadraticCurveTo(cmd.property(1).toNumber(),
+                         cmd.property(2).toNumber(),
+                         cmd.property(3).toNumber(),
+                         cmd.property(4).toNumber());
+        break;
+    case QSGContext2D::BezierCurveTo:
+        bezierCurveTo(cmd.property(1).toNumber(),
+                      cmd.property(2).toNumber(),
+                      cmd.property(3).toNumber(),
+                      cmd.property(4).toNumber(),
+                      cmd.property(5).toNumber(),
+                      cmd.property(6).toNumber());
+        break;
+    case QSGContext2D::ArcTo:
+        arcTo(cmd.property(1).toNumber(),
+              cmd.property(2).toNumber(),
+              cmd.property(3).toNumber(),
+              cmd.property(4).toNumber(),
+              cmd.property(5).toNumber());
+        break;
+    case QSGContext2D::Rect:
+        rect(cmd.property(1).toNumber(),
+             cmd.property(2).toNumber(),
+             cmd.property(3).toNumber(),
+             cmd.property(4).toNumber());
+        break;
+    case QSGContext2D::Arc:
+        arc(cmd.property(1).toNumber(),
+            cmd.property(2).toNumber(),
+            cmd.property(3).toNumber(),
+            cmd.property(4).toNumber(),
+            cmd.property(5).toNumber(),
+            cmd.property(6).toBool());
+        break;
+    case QSGContext2D::Fill:
+        fill();
+        break;
+    case QSGContext2D::Stroke:
+        stroke();
+        break;
+    case QSGContext2D::Clip:
+        clip();
+        break;
+    case QSGContext2D::GlobalAlpha:
+        setGlobalAlpha(cmd.property(1).toNumber());
+        break;
+    case QSGContext2D::GlobalCompositeOperation:
+        setGlobalCompositeOperation(cmd.property(1).toString());
+        break;
+    case QSGContext2D::StrokeStyle:
+        setStrokeStyle(cmd.property(1).toVariant());
+        break;
+    case QSGContext2D::FillStyle:
+        setFillStyle(cmd.property(1).toVariant());
+        break;
+    case QSGContext2D::FillColor:
+        setFillColor(cmd.property(1).toVariant().value<QColor>());
+        break;
+    case QSGContext2D::StrokeColor:
+        setStrokeColor(cmd.property(1).toVariant().value<QColor>());
+        break;
+    case QSGContext2D::LineWidth:
+        setLineWidth(cmd.property(1).toNumber());
+        break;
+    case QSGContext2D::LineCap:
+        setLineCap(cmd.property(1).toString());
+        break;
+    case QSGContext2D::LineJoin:
+        setLineJoin(cmd.property(1).toString());
+        break;
+    case QSGContext2D::MiterLimit:
+        setMiterLimit(cmd.property(1).toNumber());
+        break;
+    case QSGContext2D::ShadowOffsetX:
+        setShadowOffsetX(cmd.property(1).toNumber());
+        break;
+    case QSGContext2D::ShadowOffsetY:
+        setShadowOffsetY(cmd.property(1).toNumber());
+        break;
+    case QSGContext2D::ShadowBlur:
+        setShadowBlur(cmd.property(1).toNumber());
+        break;
+    case QSGContext2D::ShadowColor:
+        setShadowColor(cmd.property(1).toString());
+        break;
+    case QSGContext2D::Font:
+        setFont(cmd.property(1).toString());
+        break;
+    case QSGContext2D::TextBaseline:
+        setTextBaseline(cmd.property(1).toString());
+        break;
+    case QSGContext2D::TextAlign:
+        setTextAlign(cmd.property(1).toString());
+        break;
+    case QSGContext2D::FillText:
+        fillText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
+        break;
+    case QSGContext2D::StrokeText:
+        strokeText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
+        break;
+    case QSGContext2D::DrawImage1:
+    {
+        drawImage(cmd.property(1).toString(),
+                  cmd.property(2).toNumber(),
+                  cmd.property(3).toNumber());
+        break;
+    }
+    case QSGContext2D::DrawImage2:
+        drawImage(cmd.property(1).toString(),
+                  cmd.property(2).toNumber(),
+                  cmd.property(3).toNumber(),
+                  cmd.property(4).toNumber(),
+                  cmd.property(5).toNumber());
+        break;
+    case QSGContext2D::DrawImage3:
+            drawImage(cmd.property(1).toString(),
+                      cmd.property(2).toNumber(),
+                      cmd.property(3).toNumber(),
+                      cmd.property(4).toNumber(),
+                      cmd.property(5).toNumber(),
+                      cmd.property(6).toNumber(),
+                      cmd.property(7).toNumber(),
+                      cmd.property(8).toNumber(),
+                      cmd.property(9).toNumber());
+            break;
+    case QSGContext2D::PutImageData:
+            putImageData(cmd.property(1).toVariant(),
+                         cmd.property(2).toNumber(),
+                         cmd.property(3).toNumber(),
+                         cmd.property(4).toNumber(),
+                         cmd.property(5).toNumber());
+            break;
+    default:
+        break;
+    }
+}
+
+void QSGContext2D::paint(QPainter* p)
+{
+    Q_D(QSGContext2D);
+
+    QTransform transform = p->worldTransform();
+    if (!d->commands.isEmpty()) {
+        int matrix_idx, real_idx, int_idx, variant_idx, string_idx,color_idx,cmd_idx,
+            pen_idx, brush_idx, font_idx, path_idx, image_idx, size_idx;
+
+        matrix_idx = real_idx = int_idx = variant_idx = string_idx =color_idx = cmd_idx
+         = pen_idx = brush_idx = font_idx = path_idx = image_idx = size_idx = 0;
+
+        foreach(PaintCommand cmd, d->commands) {
+            switch (cmd) {
+            case UpdateMatrix:
+            {
+//                qDebug() << "update matrix from " << d->state.matrix << " to " << d->matrixes[matrix_idx];
+                //p->setWorldTransform(transform * QTransform(d->matrixes[matrix_idx++]), false);
+                //p->setMatrix(d->matrixes[matrix_idx++]);
+                d->state.matrix = d->matrixes[matrix_idx++];
+                break;
+            }
+            case ClearRect:
+            {
+                qreal x = d->reals[real_idx++];
+                qreal y = d->reals[real_idx++];
+                qreal w = d->reals[real_idx++];
+                qreal h = d->reals[real_idx++];
+                p->eraseRect(QRectF(x, y, w, h));
+                break;
+            }
+            case FillRect:
+            {
+                qreal x = d->reals[real_idx++];
+                qreal y = d->reals[real_idx++];
+                qreal w = d->reals[real_idx++];
+                qreal h = d->reals[real_idx++];
+                if (d->hasShadow())
+                    d->fillRectShadow(p, QRectF(x, y, w, h));
+                else
+                    p->fillRect(QRectF(x, y, w, h), p->brush());
+                break;
+            }
+            case ShadowColor:
+            {
+                QColor c = d->colors[color_idx++];
+                d->state.shadowColor = c;
+                break;
+            }
+            case ShadowBlur:
+            {
+                qreal blur = d->reals[real_idx++];
+                d->state.shadowBlur = blur;
+                break;
+            }
+            case ShadowOffsetX:
+            {
+                qreal x = d->reals[real_idx++];
+                d->state.shadowOffsetX = x;
+                break;
+            }
+            case ShadowOffsetY:
+            {
+                qreal y = d->reals[real_idx++];
+                d->state.shadowOffsetY = y;
+                break;
+            }
+            case Fill:
+            {
+                QPainterPath path = d->pathes[path_idx++];
+                if (d->hasShadow())
+                    d->fillShadowPath(p,path);
+                else
+                    p->fillPath(path, p->brush());
+                break;
+            }
+            case Stroke:
+            {
+                p->setMatrix(d->state.matrix);
+                QPainterPath path = d->state.matrix.inverted().map(d->pathes[path_idx++]);
+                if (d->hasShadow())
+                    d->strokeShadowPath(p,path);
+                else
+                    p->strokePath(path, p->pen());
+                break;
+            }
+            case Clip:
+            {
+                QPainterPath clipPath = d->pathes[path_idx++];
+                p->setClipPath(clipPath);
+                p->setClipping(true);
+                break;
+            }
+            case UpdateBrush:
+            {
+                p->setBrush(d->brushes[brush_idx++]);
+                break;
+            }
+            case UpdatePen:
+            {
+                p->setPen(d->pens[pen_idx++]);
+                break;
+            }
+            case GlobalAlpha:
+            {
+                p->setOpacity(d->reals[real_idx++]);
+                break;
+            }
+            case GlobalCompositeOperation:
+            {
+                p->setCompositionMode(static_cast<QPainter::CompositionMode>(d->ints[int_idx++]));
+                break;
+            }
+            case Font:
+            {
+                p->setFont(d->fonts[font_idx++]);
+                break;
+            }
+            case StrokeText:
+            {
+                QString text = d->strings[string_idx++];
+                qreal x = d->reals[real_idx++];
+                qreal y = d->reals[real_idx++];
+                int align = d->ints[int_idx++];
+                int baseline = d->ints[int_idx++];
+
+                QPen oldPen = p->pen();
+                p->setPen(QPen(p->brush(),0));
+                //p->setMatrix(state.matrix, false); // always set?
+
+                QPainterPath textPath;
+                QFont oldFont = p->font();
+                QFont font = p->font();
+                font.setStyleStrategy(QFont::ForceOutline);
+                p->setFont(font);
+                const QFontMetrics &metrics = p->fontMetrics();
+                int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), metrics);
+                int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), metrics, text);
+                textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
+                if (d->hasShadow())
+                    d->strokeShadowPath(p,textPath);
+
+                p->strokePath(textPath, QPen(p->brush(), p->pen().widthF()));
+
+                //reset old font
+                p->setFont(oldFont);
+                p->setPen(oldPen);
+                break;
+            }
+            case FillText:
+            {
+                QString text = d->strings[string_idx++];
+                qreal x = d->reals[real_idx++];
+                qreal y = d->reals[real_idx++];
+                int align = d->ints[int_idx++];
+                int baseline = d->ints[int_idx++];
+
+                QFont oldFont = p->font();
+                QPen oldPen = p->pen();
+                p->setPen(QPen(p->brush(), p->pen().widthF()));
+                //p->setMatrix(state.matrix, false);
+                //QFont font = p->font();
+                QFont font = d->state.font;
+                font.setBold(true);
+
+                p->setFont(font);
+                int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), p->fontMetrics());
+                int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), p->fontMetrics(), text);
+                QTextOption opt; // Adjust baseLine etc
+                if (d->hasShadow()) {
+                    const QFontMetrics &metrics = p->fontMetrics();
+                    QPainterPath textPath;
+                    textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
+                    d->fillShadowPath(p,textPath);
+                }
+                //p->drawText(QRectF(x - xoffset, y - yoffset, QWIDGETSIZE_MAX, p->fontMetrics().height()), text, opt);
+                p->setFont(oldFont);
+                p->setPen(oldPen);
+                break;
+            }
+            case DrawImage1:
+            {
+                QUrl url(d->strings[string_idx++]);
+                qreal x = d->reals[real_idx++];
+                qreal y = d->reals[real_idx++];
+                QDeclarativePixmap px(qmlEngine(d->canvas), url);
+                qDebug() << "draw image:" << url << px.pixmap().size();
+                if (px.isReady()) {
+                    QPixmap pixmap = px.pixmap();
+                    if (d->hasShadow()) {
+                        QImage shadow = d->makeShadowImage(pixmap);
+                        qreal dx = x + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+                        qreal dy = y + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+                        p->drawImage(QPointF(dx, dy), shadow);
+                    }
+                    p->drawPixmap(QPointF(x, y), pixmap);
+                }
+                break;
+            }
+            case DrawImage2:
+            {
+                qreal dx = d->reals[real_idx++];
+                qreal dy = d->reals[real_idx++];
+                qreal dw = d->reals[real_idx++];
+                qreal dh = d->reals[real_idx++];
+                QUrl url(d->strings[string_idx++]);
+                QDeclarativePixmap px(qmlEngine(d->canvas), url);
+                if (px.isReady()) {
+                    QPixmap pixmap = px.pixmap().scaled(dw, dh);
+                    if (d->hasShadow()) {
+                        QImage shadow = d->makeShadowImage(pixmap);
+                        qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+                        qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+                        p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
+                    }
+                    p->drawPixmap(QPointF(dx, dy), pixmap);
+                }
+                break;
+            }
+            case DrawImage3:
+            {
+                qreal sx = d->reals[real_idx++];
+                qreal sy = d->reals[real_idx++];
+                qreal sw = d->reals[real_idx++];
+                qreal sh = d->reals[real_idx++];
+                qreal dx = d->reals[real_idx++];
+                qreal dy = d->reals[real_idx++];
+                qreal dw = d->reals[real_idx++];
+                qreal dh = d->reals[real_idx++];
+                QUrl url(d->strings[string_idx++]);
+                QDeclarativePixmap px(qmlEngine(d->canvas), url);
+                if (px.isReady()) {
+                    QPixmap pixmap = px.pixmap().copy(sx, sy, sw, sh).scaled(dw, dh);
+                    if (d->hasShadow()) {
+                        QImage shadow = d->makeShadowImage(pixmap);
+                        qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+                        qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+                        p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
+                    }
+                    p->drawPixmap(QPointF(dx, dy), pixmap);
+                }
+                break;
+            }
+            case GetImageData:
+            {
+                qreal sx = d->reals[real_idx++];
+                qreal sy = d->reals[real_idx++];
+                qreal sw = d->reals[real_idx++];
+                qreal sh = d->reals[real_idx++];
+                QImage img = toImage().copy(sx, sy, sw, sh);
+                const uchar* data = img.constBits();
+                int i = 0;
+
+                while(i< img.byteCount()) {
+                    //the stored order in QImage:BGRA
+                    d->imageData << *(data+i+2);//R
+                    d->imageData << *(data+i+1);//G
+                    d->imageData << *(data+i);//B
+                    d->imageData << *(data+i+3);//A
+                    i+=4;
+                }
+                break;
+            }
+            case PutImageData:
+            {
+                QImage image = d->images[image_idx++];
+                qreal x = d->reals[real_idx++];
+                qreal y = d->reals[real_idx++];
+                p->drawImage(QPointF(x, y), image);
+                break;
+            }
+            default:
+                break;
+            }
+        }
+        d->clearCommands();
+    }
+}
+
+QPaintDevice* QSGContext2D::paintDevice()
+{
+    Q_D(QSGContext2D);
+    return &d->cachedImage;
+}
+const QImage& QSGContext2D::toImage() const
+{
+    Q_D(const QSGContext2D);
+    return d->cachedImage;
+}
+bool QSGContext2D::requireCachedImage() const
+{
+    Q_D(const QSGContext2D);
+    return d->waitingForPainting;
+}
+void QSGContext2D::setCachedImage(const QImage& image)
+{
+    Q_D(QSGContext2D);
+#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
+    if (d->waitingForPainting) {
+        d->cachedImage = image;
+        d->waitingForPainting = false;
+    }
+#endif
+    if (inWorkerThread()) {
+        d->agent->setCachedImage(image);
+    }
+}
+
+void QSGContext2D::clear()
+{
+    Q_D(QSGContext2D);
+    d->clear();
+}
+
+void QSGContext2D::reset()
+{
+    Q_D(QSGContext2D);
+    d->reset();
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy)
+{
+    Q_D(QSGContext2D);
+    if (!imgUrl.isEmpty())
+        d->drawImage(imgUrl, dx, dy);
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+    Q_D(QSGContext2D);
+    if (!imgUrl.isEmpty())
+        d->drawImage(imgUrl, sx, sy, sw, sh, dx, dy, dw, dh);
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy,
+                             qreal dw, qreal dh)
+{
+    Q_D(QSGContext2D);
+    if (!imgUrl.isEmpty())
+        d->drawImage(imgUrl, dx, dy, dw, dh);
+}
+
+void QSGContext2D::setSize(int width, int height)
+{
+    QSize size(width, height);
+    setSize(size);
+}
+
+void QSGContext2D::setSize(const QSize &size)
+{
+    Q_D(QSGContext2D);
+
+    if (d->size == size)
+        return;
+    d->setSize(size);
+    emit changed();
+}
+
+QSize QSGContext2D::size() const
+{
+    Q_D(const QSGContext2D);
+    return d->size;
+}
+
+QMatrix QSGContext2D::worldMatrix() const
+{
+    Q_D(const QSGContext2D);
+    return d->state.matrix;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgcontext2d_p.h b/src/declarative/items/qsgcontext2d_p.h
new file mode 100644 (file)
index 0000000..1a90010
--- /dev/null
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCONTEXT2D_P_H
+#define QSGCONTEXT2D_P_H
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+
+#include "qsgtexturematerial.h"
+
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpixmap.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qvariant.h>
+#include <QtScript/qscriptvalue.h>
+#include <QMutex>
+#include <QWaitCondition>
+#include "qsgimage_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+QColor colorFromString(const QString &name);
+
+class QSGCanvasGradient : public QObject
+{
+    Q_OBJECT
+public:
+    QSGCanvasGradient(const QGradient &gradient) : m_gradient(gradient) {}
+
+public slots:
+    QGradient value() { return m_gradient; }
+    void addColorStop(float pos, const QString &color) { m_gradient.setColorAt(pos, colorFromString(color));}
+
+public:
+    QGradient m_gradient;
+};
+
+Q_DECLARE_METATYPE(QSGCanvasGradient*)
+
+
+//class QSGCanvasImage: public QObject
+//{
+//    Q_OBJECT
+//    Q_PROPERTY(QString src READ src WRITE setSrc NOTIFY sourceChanged)
+//public:
+//    QSGCanvasImage() {}
+//    QSGCanvasImage(const QString &url) : m_image(url), m_src(url) {
+//    }
+//    QSGCanvasImage(const QImage &img) {m_image = img;}
+
+//public slots:
+//    QImage &value() { return m_image; }
+//    const QImage &value() const{ return m_image; }
+//    QString src() { return m_src; }
+//    void setSrc(const QString &src) { m_src = src; m_image.load(src); emit sourceChanged();}
+//signals:
+//    void sourceChanged();
+
+//private:
+//    QSGImage* img;
+//    QString   src;
+//};
+
+//Q_DECLARE_METATYPE(QSGCanvasImage*)
+
+
+/*
+
+  */
+
+class QSGContext2DWorkerAgent;
+class QSGContext2DPrivate;
+class QSGContext2D : public QObject
+{
+    Q_OBJECT
+    // compositing
+    Q_PROPERTY(qreal globalAlpha READ globalAlpha WRITE setGlobalAlpha)
+    Q_PROPERTY(QString globalCompositeOperation READ globalCompositeOperation WRITE setGlobalCompositeOperation)
+    Q_PROPERTY(QVariant strokeStyle READ strokeStyle WRITE setStrokeStyle)
+    Q_PROPERTY(QVariant fillStyle READ fillStyle WRITE setFillStyle)
+    Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor)
+    Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor)
+    // line caps/joins
+    Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth)
+    Q_PROPERTY(QString lineCap READ lineCap WRITE setLineCap)
+    Q_PROPERTY(QString lineJoin READ lineJoin WRITE setLineJoin)
+    Q_PROPERTY(qreal miterLimit READ miterLimit WRITE setMiterLimit)
+    // shadows
+    Q_PROPERTY(qreal shadowOffsetX READ shadowOffsetX WRITE setShadowOffsetX)
+    Q_PROPERTY(qreal shadowOffsetY READ shadowOffsetY WRITE setShadowOffsetY)
+    Q_PROPERTY(qreal shadowBlur READ shadowBlur WRITE setShadowBlur)
+    Q_PROPERTY(QString shadowColor READ shadowColor WRITE setShadowColor)
+    // fonts
+    Q_PROPERTY(QString font READ font WRITE setFont)
+    Q_PROPERTY(QString textBaseline READ textBaseline WRITE setTextBaseline)
+    Q_PROPERTY(QString textAlign READ textAlign WRITE setTextAlign)
+    Q_ENUMS(PaintCommand)
+public:
+    enum TextBaseLineType { Alphabetic=0, Top, Middle, Bottom, Hanging};
+    enum TextAlignType { Start=0, End, Left, Right, Center};
+    enum PaintCommand {
+        Invalid = 0,
+        Save,
+        Restore,
+        //matrix operations
+        UpdateMatrix,
+        Scale,
+        Rotate,
+        Translate,
+        Transform,
+        SetTransform,
+
+        ClearRect,
+        FillRect,
+
+        //path operations
+        UpdatePath,
+        BeginPath,
+        ClosePath,
+        MoveTo,
+        LineTo,
+        QuadraticCurveTo,
+        BezierCurveTo,
+        ArcTo,
+        Rect,
+        Arc,
+        Fill,
+        Stroke,
+        Clip,
+        StrokeRect,
+
+        //brushes and pens
+        UpdateBrush,
+        UpdatePen,
+        GlobalAlpha,
+        GlobalCompositeOperation,
+        StrokeStyle,
+        FillStyle,
+        StrokeColor,
+        FillColor,
+        LineWidth,
+        LineCap,
+        LineJoin,
+        MiterLimit,
+
+        //shadows
+        UpdateShadow,
+        ShadowOffsetX,
+        ShadowOffsetY,
+        ShadowBlur,
+        ShadowColor,
+
+        //font&text
+        Font,
+        TextBaseline,
+        TextAlign,
+        FillText,
+        StrokeText,
+
+        //image
+        DrawImage1,
+        DrawImage2,
+        DrawImage3,
+        GetImageData,
+        PutImageData
+    };
+
+    QSGContext2D(QObject *parent = 0);
+    QSGContext2D(QSGContext2D *ctx2d, QSGContext2DWorkerAgent* agentData);
+    ~QSGContext2D();
+    void setSize(int width, int height);
+    void setSize(const QSize &size);
+    QSize size() const;
+
+    void clear();
+    void reset();
+    QPaintDevice* paintDevice();
+    const QImage& toImage() const;
+    bool requireCachedImage() const;
+    void setCachedImage(const QImage& image);
+    // compositing
+    qreal globalAlpha() const; // (default 1.0)
+    QString globalCompositeOperation() const; // (default over)
+    QVariant strokeStyle() const; // (default black)
+    QVariant fillStyle() const; // (default black)
+    QColor strokeColor() const; // (default black)
+    QColor fillColor() const; // (default black)
+
+    void setGlobalAlpha(qreal alpha);
+    void setGlobalCompositeOperation(const QString &op);
+    void setStrokeStyle(const QVariant &style);
+    void setFillStyle(const QVariant &style);
+    void setStrokeColor(const QColor& color);
+    void setFillColor(const QColor& color);
+
+    // line caps/joins
+    qreal lineWidth() const; // (default 1)
+    QString lineCap() const; // "butt", "round", "square" (default "butt")
+    QString lineJoin() const; // "round", "bevel", "miter" (default "miter")
+    qreal miterLimit() const; // (default 10)
+
+    void setLineWidth(qreal w);
+    void setLineCap(const QString &s);
+    void setLineJoin(const QString &s);
+    void setMiterLimit(qreal m);
+
+    void setFont(const QString &font);
+    QString font() const;
+    void setTextBaseline(const QString &font);
+    QString textBaseline() const;
+    void setTextAlign(const QString &font);
+    QString textAlign() const;
+
+
+    // shadows
+    qreal shadowOffsetX() const; // (default 0)
+    qreal shadowOffsetY() const; // (default 0)
+    qreal shadowBlur() const; // (default 0)
+    QString shadowColor() const; // (default black)
+
+    void setShadowOffsetX(qreal x);
+    void setShadowOffsetY(qreal y);
+    void setShadowBlur(qreal b);
+    void setShadowColor(const QString &str);
+
+public slots:
+    void save(); // push state on state stack
+    void restore(); // pop state stack and restore state
+
+    //    QTextMetrics measureText(const QString& text);
+
+    void fillText(const QString &text, qreal x, qreal y);
+    void strokeText(const QString &text, qreal x, qreal y);
+
+    void scale(qreal x, qreal y);
+    void rotate(qreal angle);
+    void translate(qreal x, qreal y);
+    void transform(qreal m11, qreal m12, qreal m21, qreal m22,
+                   qreal dx, qreal dy);
+    void setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+                      qreal dx, qreal dy);
+
+    QSGCanvasGradient *createLinearGradient(qreal x0, qreal y0,
+                                         qreal x1, qreal y1);
+    QSGCanvasGradient *createRadialGradient(qreal x0, qreal y0,
+                                         qreal r0, qreal x1,
+                                         qreal y1, qreal r1);
+
+    // rects
+    void clearRect(qreal x, qreal y, qreal w, qreal h);
+    void fillRect(qreal x, qreal y, qreal w, qreal h);
+    void strokeRect(qreal x, qreal y, qreal w, qreal h);
+
+    // path API
+    void beginPath();
+    void closePath();
+    void moveTo(qreal x, qreal y);
+    void lineTo(qreal x, qreal y);
+    void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y);
+    void bezierCurveTo(qreal cp1x, qreal cp1y,
+                       qreal cp2x, qreal cp2y, qreal x, qreal y);
+    void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius);
+    void rect(qreal x, qreal y, qreal w, qreal h);
+    void arc(qreal x, qreal y, qreal radius,
+             qreal startAngle, qreal endAngle,
+             bool anticlockwise);
+    void fill();
+    void stroke();
+    void clip();
+    bool isPointInPath(qreal x, qreal y) const;
+
+
+    QSGImage *createImage(const QString &url);
+
+    void drawImage(const QString& imgUrl, qreal dx, qreal dy);
+    void drawImage(const QString& imgUrl, qreal dx, qreal dy, qreal dw, qreal dh);
+    void drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh);
+
+    // pixel manipulation
+    QList<int> getImageData(qreal sx, qreal sy, qreal sw, qreal sh);
+    void putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h);
+
+    void paint(QPainter* painter);
+    void sync();
+    void processCommands(const QScriptValue& commands);
+signals:
+    void changed();
+    void painted();
+public:
+    bool isDirty() const;
+    QScriptValue scriptValue() const;
+    void setScriptEngine(QScriptEngine *eng);
+    QScriptEngine *scriptEngine() const;
+
+    void addref();
+    void release();
+
+    struct VariantRef
+    {
+        VariantRef() : a(0) {}
+        VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); }
+        VariantRef(QSGContext2D *_a) : a(_a) { if (a) a->addref(); }
+        ~VariantRef() { if (a) a->release(); }
+
+        VariantRef &operator=(const VariantRef &o) {
+            if (o.a) o.a->addref();
+            if (a) a->release(); a = o.a;
+            return *this;
+        }
+
+        QSGContext2D *a;
+    };
+    struct Sync : public QEvent {
+        Sync() : QEvent(QEvent::User) {}
+        QSGContext2DWorkerAgent *data;
+    };
+    inline bool inWorkerThread() const;
+    QSGContext2D *agent();
+    const QString& agentScript() const;
+
+
+    struct State {
+        QMatrix matrix;
+        QPainterPath clipPath;
+        QBrush strokeStyle;
+        QBrush fillStyle;
+        qreal globalAlpha;
+        qreal lineWidth;
+        Qt::PenCapStyle lineCap;
+        Qt::PenJoinStyle lineJoin;
+        qreal miterLimit;
+        qreal shadowOffsetX;
+        qreal shadowOffsetY;
+        qreal shadowBlur;
+        QColor shadowColor;
+        QPainter::CompositionMode globalCompositeOperation;
+        QFont font;
+        QSGContext2D::TextAlignType textAlign;
+        QSGContext2D::TextBaseLineType textBaseline;
+        QPen pen;
+    };
+
+    QMatrix worldMatrix() const;
+
+protected:
+    virtual bool event(QEvent *);
+
+private:
+    void processCommand(const QScriptValue& command);
+
+    Q_DECLARE_PRIVATE(QSGContext2D)
+};
+
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSGContext2D::VariantRef)
+QML_DECLARE_TYPE(QSGContext2D)
+
+QT_END_HEADER
+
+#endif // QSGCONTEXT2D_P_H
diff --git a/src/declarative/items/qsgcontext2d_p_p.h b/src/declarative/items/qsgcontext2d_p_p.h
new file mode 100644 (file)
index 0000000..36f26c9
--- /dev/null
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCONTEXT2D_P_P_H
+#define QSGCONTEXT2D_P_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgcontext2d_p.h"
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+class QSGCanvasItem;
+struct QSGContext2DWorkerAgent {
+    QSGContext2DWorkerAgent()
+        :ref(1)
+        , orig(0)
+    {}
+
+    QAtomicInt ref;
+    QSGContext2D *orig;
+    QMutex mutex;
+    QWaitCondition syncDone;
+};
+
+class QSGContext2DPrivate : public QObjectPrivate
+{
+    Q_DECLARE_PUBLIC(QSGContext2D)
+
+public:
+    QSGContext2DPrivate()
+        : agent(0)
+        , agentData(0)
+        , scriptEngine(0)
+        , cachedImage(1,1, QImage::Format_ARGB32)
+        , canvas(0)
+        , waitingForPainting(false)
+    {
+    }
+    void updateMatrix(const QMatrix& m);
+
+    void setSize(const QSize &s)
+    {
+        size = s;
+        cachedImage = QImage(s, QImage::Format_ARGB32);
+    }
+    void clear();
+    void reset();
+
+    // compositing
+    void setGlobalAlpha(qreal alpha);
+    void setGlobalCompositeOperation(const QString &op);
+    void setStrokeStyle(const QVariant &style);
+    void setFillStyle(const QVariant &style);
+    void setStrokeColor(const QColor& color);
+    void setFillColor(const QColor& color);
+
+    // line caps/joins
+    void setLineWidth(qreal w);
+    void setLineCap(const QString &s);
+    void setLineJoin(const QString &s);
+    void setMiterLimit(qreal m);
+
+    void setFont(const QString &font);
+    void setTextBaseline(const QString &font);
+    void setTextAlign(const QString &font);
+
+
+    // shadows
+    void setShadowOffsetX(qreal x);
+    void setShadowOffsetY(qreal y);
+    void setShadowBlur(qreal b);
+    void setShadowColor(const QString &str);
+
+    bool hasShadow() const;
+    void clearShadow();
+    QImage makeShadowImage(const QPixmap& pix);
+    void fillRectShadow(QPainter* p, QRectF shadowRect);
+    void fillShadowPath(QPainter* p, const QPainterPath& path);
+    void strokeShadowPath(QPainter* p, const QPainterPath& path);
+    void save();
+    void restore();
+
+    //    QTextMetrics measureText(const QString& text);
+
+    void fillText(const QString &text, qreal x, qreal y);
+    void strokeText(const QString &text, qreal x, qreal y);
+
+    void scale(qreal x, qreal y);
+    void rotate(qreal angle);
+    void translate(qreal x, qreal y);
+    void transform(qreal m11, qreal m12, qreal m21, qreal m22,
+                   qreal dx, qreal dy);
+    void setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+                      qreal dx, qreal dy);
+
+    // rects
+    void clearRect(qreal x, qreal y, qreal w, qreal h);
+    void fillRect(qreal x, qreal y, qreal w, qreal h);
+    void strokeRect(qreal x, qreal y, qreal w, qreal h);
+
+    // path API
+    void beginPath();
+    void closePath();
+    void moveTo(qreal x, qreal y);
+    void lineTo(qreal x, qreal y);
+    void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y);
+    void bezierCurveTo(qreal cp1x, qreal cp1y,
+                       qreal cp2x, qreal cp2y, qreal x, qreal y);
+    void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius);
+    void rect(qreal x, qreal y, qreal w, qreal h);
+    void arc(qreal x, qreal y, qreal radius,
+             qreal startAngle, qreal endAngle,
+             bool anticlockwise);
+    void fill();
+    void stroke();
+    void clip();
+
+    void drawImage(const QString& url, qreal dx, qreal dy);
+    void drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh);
+    void drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh);
+
+    QList<int> getImageData(qreal sx, qreal sy, qreal sw, qreal sh);
+    void putImageData(const QVariantList& imageData, qreal x, qreal y, qreal w, qreal h);
+
+    int baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics);
+    int textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &string);
+
+    void clearCommands()
+    {
+        commands.remove(0, commands.size());
+        variants.remove(0, variants.size());
+        pens.remove(0, pens.size());
+        ints.remove(0, ints.size());
+        reals.remove(0, reals.size());
+        strings.remove(0, strings.size());
+        colors.remove(0, colors.size());
+        matrixes.remove(0, matrixes.size());
+        brushes.remove(0, brushes.size());
+        pathes.remove(0, pathes.size());
+        fonts.remove(0, fonts.size());
+        images.remove(0, images.size());
+        sizes.remove(0, sizes.size());
+    }
+
+    //current context2d variables
+    QPainterPath path;
+    QSize size;
+    QSGContext2D::State state;
+    QStack<QSGContext2D::State> stateStack;
+
+    //variables for actual painting
+    QVector<QSGContext2D::PaintCommand> commands;
+    QVector<QVariant> variants;
+    QVector<int> ints;
+    QVector<qreal> reals;
+    QVector<QString> strings;
+    QVector<QColor> colors;
+    QVector<QMatrix> matrixes;
+    QVector<QPen> pens;
+    QVector<QBrush> brushes;
+    QVector<QPainterPath> pathes;
+    QVector<QFont> fonts;
+    QVector<QImage> images;
+    QVector<QSize> sizes;
+    QList<int> imageData;
+
+    //workerscript agent
+    QSGContext2D* agent;
+    QSGContext2DWorkerAgent* agentData;
+    QScriptEngine* scriptEngine;
+    QScriptValue scriptValue;
+    QImage cachedImage;
+    QSGCanvasItem* canvas;
+    bool waitingForPainting;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCONTEXT2D_P_P_H