From 46a0bd1ba016c49969b039a7e89e62bf1c68cc3b Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Mon, 16 May 2011 10:15:24 +1000 Subject: [PATCH] canvasitem implementation based on painteditem --- src/declarative/items/items.pri | 5 + src/declarative/items/qsgcanvasitem.cpp | 441 +++++ src/declarative/items/qsgcanvasitem_p.h | 89 + src/declarative/items/qsgcontext2d.cpp | 2716 ++++++++++++++++++++++++++++++ src/declarative/items/qsgcontext2d_p.h | 409 +++++ src/declarative/items/qsgcontext2d_p_p.h | 227 +++ 6 files changed, 3887 insertions(+) create mode 100644 src/declarative/items/qsgcanvasitem.cpp create mode 100644 src/declarative/items/qsgcanvasitem_p.h create mode 100644 src/declarative/items/qsgcontext2d.cpp create mode 100644 src/declarative/items/qsgcontext2d_p.h create mode 100644 src/declarative/items/qsgcontext2d_p_p.h diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index 3dbb4fa..d694297 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -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 index 0000000..f2eaf0d --- /dev/null +++ b/src/declarative/items/qsgcanvasitem.cpp @@ -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 + +#include "private/qsgadaptationlayer_p.h" +#include "qsgcanvasitem_p.h" +#include "qsgpainteditem_p.h" +#include "qsgcontext2d_p.h" +#include "private/qsgpainternode_p.h" +#include +#include "qdeclarativeengine_p.h" +#include + +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(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(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 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(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(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 index 0000000..a358c35 --- /dev/null +++ b/src/declarative/items/qsgcanvasitem_p.h @@ -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 +#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 index 0000000..6f7121a --- /dev/null +++ b/src/declarative/items/qsgcontext2d.cpp @@ -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 +#include +#include "private/qsgcontext_p.h" + +#include +#include +#include +#include +#include +#include "qdeclarativepixmapcache_p.h" +#include + +QT_BEGIN_NAMESPACE + +static const double Q_PI = 3.14159265358979323846; // pi +template +void memcpy_vector(QVector* dst, const QVector& src) +{ + int pos = dst->size(); + dst->resize(pos + src.size()); + memmove(dst->data() + pos, src.constData(), sizeof(T) * src.size()); +} + +template +void copy_vector(QVector* dst, const QVector& 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(style.value()); + 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(style.value()); + 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 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 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(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(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= 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(e); + + QSGContext2DPrivate* origin_d = static_cast(s->data->orig->d_func()); + + //quick copy + memcpy_vector(&origin_d->commands, d->commands); + memcpy_vector(&origin_d->ints, d->ints); + memcpy_vector(&origin_d->reals, d->reals); + memcpy_vector(&origin_d->colors, d->colors); + memcpy_vector(&origin_d->matrixes, d->matrixes); + memcpy_vector(&origin_d->sizes, d->sizes); + + //slow copy + copy_vector(&origin_d->strings, d->strings); + copy_vector(&origin_d->variants, d->variants); + copy_vector(&origin_d->pens, d->pens); + copy_vector(&origin_d->brushes, d->brushes); + copy_vector(&origin_d->pathes, d->pathes); + copy_vector(&origin_d->fonts, d->fonts); + copy_vector(&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()); + break; + case QSGContext2D::StrokeColor: + setStrokeColor(cmd.property(1).toVariant().value()); + 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(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(baseline), metrics); + int xoffset = d->textAlignOffset(static_cast(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(baseline), p->fontMetrics()); + int xoffset = d->textAlignOffset(static_cast(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 index 0000000..1a90010 --- /dev/null +++ b/src/declarative/items/qsgcontext2d_p.h @@ -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 +#include + +#include "qsgtexturematerial.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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 index 0000000..36f26c9 --- /dev/null +++ b/src/declarative/items/qsgcontext2d_p_p.h @@ -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 + +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 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 stateStack; + + //variables for actual painting + QVector commands; + QVector variants; + QVector ints; + QVector reals; + QVector strings; + QVector colors; + QVector matrixes; + QVector pens; + QVector brushes; + QVector pathes; + QVector fonts; + QVector images; + QVector sizes; + QList 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 -- 2.7.4