aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/items/qsgcontext2d.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/items/qsgcontext2d.cpp')
-rw-r--r--src/declarative/items/qsgcontext2d.cpp2716
1 files changed, 2716 insertions, 0 deletions
diff --git a/src/declarative/items/qsgcontext2d.cpp b/src/declarative/items/qsgcontext2d.cpp
new file mode 100644
index 0000000000..6f7121ac30
--- /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 <QtOpenGL/qglframebufferobject.h>
+#include <QtCore/qdebug.h>
+#include "private/qsgcontext_p.h"
+
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicseffect.h>
+#include <qdeclarativeinfo.h>
+#include <QtCore/qmath.h>
+#include "qdeclarativepixmapcache_p.h"
+#include <QtScript/QScriptEngine>
+
+QT_BEGIN_NAMESPACE
+
+static const double Q_PI = 3.14159265358979323846; // pi
+template <class T>
+void memcpy_vector(QVector<T>* dst, const QVector<T>& src)
+{
+ int pos = dst->size();
+ dst->resize(pos + src.size());
+ memmove(dst->data() + pos, src.constData(), sizeof(T) * src.size());
+}
+
+template <class T>
+void copy_vector(QVector<T>* dst, const QVector<T>& src)
+{
+ int pos = dst->size();
+ dst->resize(pos + src.size());
+ for (int i = 0; i < src.size(); i++) {
+ (*dst)[pos + i] = src[i];
+ }
+}
+
+// Note, this is exported but in a private header as qtopengl depends on it.
+// But it really should be considered private API
+void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
+void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
+
+#define DEGREES(t) ((t) * 180.0 / Q_PI)
+#define qClamp(val, min, max) qMin(qMax(val, min), max)
+
+static inline int extractInt(const char **name)
+{
+ int result = 0;
+ bool negative = false;
+
+ //eat leading whitespace
+ while (isspace(*name[0]))
+ ++*name;
+
+ if (*name[0] == '-') {
+ ++*name;
+ negative = true;
+ } /*else if (name[0] == '+')
+ ++name; //ignore*/
+
+ //construct number
+ while (isdigit(*name[0])) {
+ result = result * 10 + (*name[0] - '0');
+ ++*name;
+ }
+ if (negative)
+ result = -result;
+
+ //handle optional percentage
+ if (*name[0] == '%')
+ result *= qreal(255)/100; //### floor or round?
+
+ //eat trailing whitespace
+ while (isspace(*name[0]))
+ ++*name;
+
+ return result;
+}
+
+static bool qt_get_rgb(const QString &string, QRgb *rgb)
+{
+ const char *name = string.toLatin1().constData();
+ int len = qstrlen(name);
+
+ if (len < 5)
+ return false;
+
+ bool handleAlpha = false;
+
+ if (name[0] != 'r')
+ return false;
+ if (name[1] != 'g')
+ return false;
+ if (name[2] != 'b')
+ return false;
+ if (name[3] == 'a') {
+ handleAlpha = true;
+ if(name[3] != '(')
+ return false;
+ } else if (name[3] != '(')
+ return false;
+
+ name += 4;
+
+ int r, g, b, a = 1;
+ int result;
+
+ //red
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ r = result;
+ ++name;
+ } else
+ return false;
+
+ //green
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ g = result;
+ ++name;
+ } else
+ return false;
+
+ char nextChar = handleAlpha ? ',' : ')';
+
+ //blue
+ result = extractInt(&name);
+ if (name[0] == nextChar) {
+ b = result;
+ ++name;
+ } else
+ return false;
+
+ //alpha
+ if (handleAlpha) {
+ result = extractInt(&name);
+ if (name[0] == ')') {
+ a = result * 255; //map 0-1 to 0-255
+ ++name;
+ } else
+ return false;
+ }
+
+ if (name[0] != '\0')
+ return false;
+
+ *rgb = qRgba(qClamp(r,0,255), qClamp(g,0,255), qClamp(b,0,255), qClamp(a,0,255));
+ return true;
+}
+
+//### unify with qt_get_rgb?
+static bool qt_get_hsl(const QString &string, QColor *color)
+{
+ const char *name = string.toLatin1().constData();
+ int len = qstrlen(name);
+
+ if (len < 5)
+ return false;
+
+ bool handleAlpha = false;
+
+ if (name[0] != 'h')
+ return false;
+ if (name[1] != 's')
+ return false;
+ if (name[2] != 'l')
+ return false;
+ if (name[3] == 'a') {
+ handleAlpha = true;
+ if(name[3] != '(')
+ return false;
+ } else if (name[3] != '(')
+ return false;
+
+ name += 4;
+
+ int h, s, l, a = 1;
+ int result;
+
+ //hue
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ h = result;
+ ++name;
+ } else
+ return false;
+
+ //saturation
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ s = result;
+ ++name;
+ } else
+ return false;
+
+ char nextChar = handleAlpha ? ',' : ')';
+
+ //lightness
+ result = extractInt(&name);
+ if (name[0] == nextChar) {
+ l = result;
+ ++name;
+ } else
+ return false;
+
+ //alpha
+ if (handleAlpha) {
+ result = extractInt(&name);
+ if (name[0] == ')') {
+ a = result * 255; //map 0-1 to 0-255
+ ++name;
+ } else
+ return false;
+ }
+
+ if (name[0] != '\0')
+ return false;
+
+ *color = QColor::fromHsl(qClamp(h,0,255), qClamp(s,0,255), qClamp(l,0,255), qClamp(a,0,255));
+ return true;
+}
+
+//### optimize further
+QColor colorFromString(const QString &name)
+{
+ if (name.startsWith(QLatin1String("rgb"))) {
+ QRgb rgb;
+ if (qt_get_rgb(name, &rgb))
+ return QColor(rgb);
+ } else if (name.startsWith(QLatin1String("hsl"))) {
+ QColor color;
+ if (qt_get_hsl(name, &color))
+ return color;
+ }
+
+ return QColor(name);
+}
+
+
+static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
+{
+ if (compositeOperator == QLatin1String("source-over")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("source-out")) {
+ return QPainter::CompositionMode_SourceOut;
+ } else if (compositeOperator == QLatin1String("source-in")) {
+ return QPainter::CompositionMode_SourceIn;
+ } else if (compositeOperator == QLatin1String("source-atop")) {
+ return QPainter::CompositionMode_SourceAtop;
+ } else if (compositeOperator == QLatin1String("destination-atop")) {
+ return QPainter::CompositionMode_DestinationAtop;
+ } else if (compositeOperator == QLatin1String("destination-in")) {
+ return QPainter::CompositionMode_DestinationIn;
+ } else if (compositeOperator == QLatin1String("destination-out")) {
+ return QPainter::CompositionMode_DestinationOut;
+ } else if (compositeOperator == QLatin1String("destination-over")) {
+ return QPainter::CompositionMode_DestinationOver;
+ } else if (compositeOperator == QLatin1String("darker")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("lighter")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("copy")) {
+ return QPainter::CompositionMode_Source;
+ } else if (compositeOperator == QLatin1String("xor")) {
+ return QPainter::CompositionMode_Xor;
+ }
+
+ return QPainter::CompositionMode_SourceOver;
+}
+
+static QString compositeOperatorToString(QPainter::CompositionMode op)
+{
+ switch (op) {
+ case QPainter::CompositionMode_SourceOver:
+ return QLatin1String("source-over");
+ case QPainter::CompositionMode_DestinationOver:
+ return QLatin1String("destination-over");
+ case QPainter::CompositionMode_Clear:
+ return QLatin1String("clear");
+ case QPainter::CompositionMode_Source:
+ return QLatin1String("source");
+ case QPainter::CompositionMode_Destination:
+ return QLatin1String("destination");
+ case QPainter::CompositionMode_SourceIn:
+ return QLatin1String("source-in");
+ case QPainter::CompositionMode_DestinationIn:
+ return QLatin1String("destination-in");
+ case QPainter::CompositionMode_SourceOut:
+ return QLatin1String("source-out");
+ case QPainter::CompositionMode_DestinationOut:
+ return QLatin1String("destination-out");
+ case QPainter::CompositionMode_SourceAtop:
+ return QLatin1String("source-atop");
+ case QPainter::CompositionMode_DestinationAtop:
+ return QLatin1String("destination-atop");
+ case QPainter::CompositionMode_Xor:
+ return QLatin1String("xor");
+ case QPainter::CompositionMode_Plus:
+ return QLatin1String("plus");
+ case QPainter::CompositionMode_Multiply:
+ return QLatin1String("multiply");
+ case QPainter::CompositionMode_Screen:
+ return QLatin1String("screen");
+ case QPainter::CompositionMode_Overlay:
+ return QLatin1String("overlay");
+ case QPainter::CompositionMode_Darken:
+ return QLatin1String("darken");
+ case QPainter::CompositionMode_Lighten:
+ return QLatin1String("lighten");
+ case QPainter::CompositionMode_ColorDodge:
+ return QLatin1String("color-dodge");
+ case QPainter::CompositionMode_ColorBurn:
+ return QLatin1String("color-burn");
+ case QPainter::CompositionMode_HardLight:
+ return QLatin1String("hard-light");
+ case QPainter::CompositionMode_SoftLight:
+ return QLatin1String("soft-light");
+ case QPainter::CompositionMode_Difference:
+ return QLatin1String("difference");
+ case QPainter::CompositionMode_Exclusion:
+ return QLatin1String("exclusion");
+ default:
+ break;
+ }
+ return QString();
+}
+
+bool QSGContext2DPrivate::hasShadow() const
+{
+ return state.shadowColor.isValid()
+ && state.shadowColor.alpha()
+ && (state.shadowBlur || state.shadowOffsetX || state.shadowOffsetY);
+}
+
+void QSGContext2DPrivate::clearShadow()
+{
+ state.shadowOffsetX = 0;
+ state.shadowOffsetY = 0;
+ state.shadowBlur = 0;
+ state.shadowColor = QColor();
+}
+
+QImage QSGContext2DPrivate::makeShadowImage(const QPixmap& pix)
+{
+ QImage shadowImg(pix.width() + state.shadowBlur * 2 + qAbs(state.shadowOffsetX),
+ pix.height() + state.shadowBlur *2 + qAbs(state.shadowOffsetY),
+ QImage::Format_ARGB32);
+ shadowImg.fill(0);
+ QPainter tmpPainter(&shadowImg);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
+ qreal shadowX = state.shadowOffsetX > 0? state.shadowOffsetX : 0;
+ qreal shadowY = state.shadowOffsetY > 0? state.shadowOffsetY : 0;
+
+ tmpPainter.drawPixmap(shadowX, shadowY, pix);
+ tmpPainter.end();
+
+ // blur the alpha channel
+ if (state.shadowBlur > 0) {
+ QImage blurred(shadowImg.size(), QImage::Format_ARGB32);
+ blurred.fill(0);
+ QPainter blurPainter(&blurred);
+ qt_blurImage(&blurPainter, shadowImg, state.shadowBlur, false, true);
+ blurPainter.end();
+ shadowImg = blurred;
+ }
+
+ // blacken the image with shadow color...
+ tmpPainter.begin(&shadowImg);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ tmpPainter.fillRect(shadowImg.rect(), state.shadowColor);
+ tmpPainter.end();
+ return shadowImg;
+}
+
+void QSGContext2DPrivate::fillRectShadow(QPainter* p, QRectF shadowRect)
+{
+ QRectF r = shadowRect;
+ r.moveTo(0, 0);
+
+ QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32);
+ QPainter tp;
+ tp.begin(&shadowImage);
+ tp.fillRect(r, p->brush());
+ tp.end();
+ shadowImage = makeShadowImage(QPixmap::fromImage(shadowImage));
+
+ qreal dx = shadowRect.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+ qreal dy = shadowRect.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+
+ p->drawImage(dx, dy, shadowImage);
+ p->fillRect(shadowRect, p->brush());
+}
+
+void QSGContext2DPrivate::fillShadowPath(QPainter* p, const QPainterPath& path)
+{
+ QRectF r = path.boundingRect();
+ QImage img(r.size().width() + r.left() + 1,
+ r.size().height() + r.top() + 1,
+ QImage::Format_ARGB32);
+ img.fill(0);
+ QPainter tp(&img);
+ tp.fillPath(path.translated(0, 0), p->brush());
+ tp.end();
+
+ QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
+ qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+ qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+
+ p->drawImage(dx, dy, shadowImage);
+ p->fillPath(path, p->brush());
+}
+
+void QSGContext2DPrivate::strokeShadowPath(QPainter* p, const QPainterPath& path)
+{
+ QRectF r = path.boundingRect();
+ QImage img(r.size().width() + r.left() + 1,
+ r.size().height() + r.top() + 1,
+ QImage::Format_ARGB32);
+ img.fill(0);
+ QPainter tp(&img);
+ tp.strokePath(path, p->pen());
+ tp.end();
+
+ QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
+ qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+ qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+ p->drawImage(dx, dy, shadowImage);
+ p->strokePath(path, p->pen());
+}
+
+void QSGContext2DPrivate::clear()
+{
+ clearRect(0, 0, size.width(), size.height());
+}
+
+void QSGContext2DPrivate::reset()
+{
+ stateStack.clear();
+ state.matrix = QMatrix();
+ state.clipPath = QPainterPath();
+ state.strokeStyle = Qt::black;
+ state.fillStyle = Qt::black;
+ state.globalAlpha = 1.0;
+ state.lineWidth = 1;
+ state.lineCap = Qt::FlatCap;
+ state.lineJoin = Qt::MiterJoin;
+ state.miterLimit = 10;
+ state.shadowOffsetX = 0;
+ state.shadowOffsetY = 0;
+ state.shadowBlur = 0;
+ state.shadowColor = qRgba(0, 0, 0, 0);
+ state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
+ state.font = QFont();
+ state.textAlign = QSGContext2D::Start;
+ state.textBaseline = QSGContext2D::Alphabetic;
+ clear();
+}
+
+
+void QSGContext2DPrivate::updateMatrix(const QMatrix& m)
+{
+ commands.push_back(QSGContext2D::UpdateMatrix);
+ matrixes.push_back(m);
+}
+
+
+
+
+void QSGContext2DPrivate::save()
+{
+ stateStack.push(state);
+}
+
+void QSGContext2DPrivate::restore()
+{
+ if (!stateStack.isEmpty()) {
+ bool update = false;
+ QSGContext2D::State s = stateStack.pop();
+ if (state.matrix != s.matrix) {
+ updateMatrix(s.matrix);
+ update = true;
+ }
+
+ if (s.pen != state.pen) {
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(s.pen);
+ update = true;
+ }
+
+ if (s.globalAlpha != state.globalAlpha) {
+ commands.push_back(QSGContext2D::GlobalAlpha);
+ reals.push_back(s.globalAlpha);
+ update = true;
+ }
+
+ if (s.globalCompositeOperation != state.globalCompositeOperation) {
+ commands.push_back(QSGContext2D::GlobalCompositeOperation);
+ ints.push_back(s.globalCompositeOperation);
+ update = true;
+ }
+
+ if (s.font != state.font) {
+ commands.push_back(QSGContext2D::Font);
+ fonts.push_back(s.font);
+ update = true;
+ }
+
+ if (s.fillStyle != state.fillStyle) {
+ commands.push_back(QSGContext2D::FillStyle);
+ brushes.push_back(s.fillStyle);
+ update = true;
+ }
+
+ if (s.clipPath != state.clipPath) {
+ //commands.push_back(QSGContext2D::ClipPath);
+ update = true;
+ }
+
+ if (s.textAlign != state.textAlign) {
+ commands.push_back(QSGContext2D::TextAlign);
+ update = true;
+ }
+
+ if (s.textBaseline != state.textBaseline) {
+ commands.push_back(QSGContext2D::TextBaseline);
+ update = true;
+ }
+
+ if (s.shadowBlur != state.shadowBlur
+ || s.shadowColor != state.shadowColor
+ || s.shadowOffsetX != state.shadowOffsetX
+ || s.shadowOffsetY != state.shadowOffsetY) {
+ update = true;
+ }
+
+ if (update)
+ state = s;
+ }
+}
+
+void QSGContext2DPrivate::scale(qreal x, qreal y)
+{
+ state.matrix.scale(x, y);
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::rotate(qreal angle)
+{
+ state.matrix.rotate(DEGREES(angle));
+ updateMatrix(state.matrix);
+}
+
+
+void QSGContext2DPrivate::translate(qreal x, qreal y)
+{
+ state.matrix.translate(x, y);
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::transform(
+ qreal m11, qreal m12,
+ qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix matrix(m11, m12, m21, m22, dx, dy);
+ state.matrix *= matrix;
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::setTransform(
+ qreal m11, qreal m12,
+ qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix matrix(m11, m12, m21, m22, dx, dy);
+ state.matrix = matrix;
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::clearRect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ commands.push_back(QSGContext2D::ClearRect);
+ reals.push_back(x);
+ reals.push_back(y);
+ reals.push_back(w);
+ reals.push_back(h);
+}
+
+void QSGContext2DPrivate::fillRect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ commands.push_back(QSGContext2D::FillRect);
+ reals.push_back(x);
+ reals.push_back(y);
+ reals.push_back(w);
+ reals.push_back(h);
+}
+
+void QSGContext2DPrivate::strokeRect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ QPainterPath path;
+ path.addRect(x, y, w, h);
+ commands.push_back(QSGContext2D::Stroke);
+ pathes.push_back(path);
+}
+
+void QSGContext2DPrivate::beginPath()
+{
+ path = QPainterPath();
+}
+
+void QSGContext2DPrivate::closePath()
+{
+ path.closeSubpath();
+}
+
+void QSGContext2DPrivate::moveTo( qreal x, qreal y)
+{
+ path.moveTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::lineTo( qreal x, qreal y)
+{
+ path.lineTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::quadraticCurveTo(qreal cpx, qreal cpy,
+ qreal x, qreal y)
+{
+ path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
+ state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y,
+ qreal x, qreal y)
+{
+ path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
+ state.matrix.map(QPointF(cp2x, cp2y)),
+ state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::arcTo(qreal x1, qreal y1,
+ qreal x2, qreal y2,
+ qreal radius)
+{
+ QPointF st = state.matrix.map(QPoint(x1, y1));
+ QPointF end = state.matrix.map(QPoint(x2, y2));
+
+ path.arcTo(st.x(), st.y(),
+ end.x()-st.x(), end.y()-st.y(),
+ radius, 90);
+}
+
+void QSGContext2DPrivate::rect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ QPainterPath path;
+ path.addRect(QRectF(x, y, w, h));
+ path.addPath(state.matrix.map(path));
+}
+
+void QSGContext2DPrivate::arc(qreal xc,
+ qreal yc,
+ qreal radius,
+ qreal sar,
+ qreal ear,
+ bool antiClockWise)
+{
+ QPainterPath path;
+
+ //### HACK
+
+ // In Qt we don't switch the coordinate system for degrees
+ // and still use the 0,0 as bottom left for degrees so we need
+ // to switch
+ sar = -sar;
+ ear = -ear;
+ antiClockWise = !antiClockWise;
+ //end hack
+
+ float sa = DEGREES(sar);
+ float ea = DEGREES(ear);
+
+ double span = 0;
+
+ double xs = xc - radius;
+ double ys = yc - radius;
+ double width = radius*2;
+ double height = radius*2;
+
+ if (!antiClockWise && (ea < sa)) {
+ span += 360;
+ } else if (antiClockWise && (sa < ea)) {
+ span -= 360;
+ }
+
+ //### this is also due to switched coordinate system
+ // we would end up with a 0 span instead of 360
+ if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
+ qFuzzyCompare(qAbs(span), 360))) {
+ span += ea - sa;
+ }
+
+ path.moveTo(QPointF(xc + radius * qCos(sar),
+ yc - radius * qSin(sar)));
+
+ path.arcTo(xs, ys, width, height, sa, span);
+
+ path.addPath(state.matrix.map(path));
+}
+
+void QSGContext2DPrivate::fill()
+{
+ commands.push_back(QSGContext2D::Fill);
+ pathes.push_back(path);
+}
+
+void QSGContext2DPrivate::stroke()
+{
+ commands.push_back(QSGContext2D::Stroke);
+ pathes.push_back(path);
+// painter->setMatrix(state.matrix, false);
+// QPainterPath tmp = state.matrix.inverted().map(path); //why?
+// painter->strokePath(tmp, painter->pen());
+}
+
+void QSGContext2DPrivate::clip()
+{
+ state.clipPath = path;
+ pathes.push_back(state.clipPath);
+ commands.push_back(QSGContext2D::Clip);
+}
+
+void QSGContext2DPrivate::setGlobalAlpha( qreal alpha)
+{
+ state.globalAlpha = alpha;
+ commands.push_back(QSGContext2D::GlobalAlpha);
+ reals.push_back(state.globalAlpha);
+}
+
+void QSGContext2DPrivate::setGlobalCompositeOperation( const QString &op)
+{
+ state.globalCompositeOperation = compositeOperatorFromString(op);
+ commands.push_back(QSGContext2D::GlobalCompositeOperation);
+ ints.push_back(state.globalCompositeOperation);
+}
+
+void QSGContext2DPrivate::setStrokeStyle( const QVariant &style)
+{
+ QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
+ QBrush b;
+ if (gradient) {
+ b = gradient->value();
+ } else {
+ b = colorFromString(style.toString());
+ }
+
+ if (state.strokeStyle != b) {
+ state.strokeStyle = b;
+ state.pen.setBrush(state.strokeStyle);
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+void QSGContext2DPrivate::setStrokeColor(const QColor& color)
+{
+ if (state.strokeStyle != color) {
+ state.strokeStyle = color;
+ commands.push_back(QSGContext2D::UpdatePen);
+ QPen pen;
+ pen.setBrush(state.strokeStyle);
+ pens.push_back(pen);
+ }
+}
+
+void QSGContext2DPrivate::setFillColor(const QColor& color)
+{
+ if (state.fillStyle != color) {
+ state.fillStyle = color;
+ commands.push_back(QSGContext2D::UpdateBrush);
+ brushes.push_back(state.fillStyle);
+ }
+}
+
+void QSGContext2DPrivate::setFillStyle( const QVariant &style)
+{
+ QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
+ QBrush b;
+ if (gradient) {
+ b = gradient->value();
+ } else {
+ b = colorFromString(style.toString());
+ }
+
+ if (state.fillStyle != b) {
+ state.fillStyle = b;
+ commands.push_back(QSGContext2D::UpdateBrush);
+ brushes.push_back(b);
+ }
+}
+
+
+void QSGContext2DPrivate::setLineWidth( qreal w)
+{
+ if (state.lineWidth != w) {
+ state.pen.setWidthF(w);
+ state.lineWidth = w;
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setLineCap( const QString& cap)
+{
+ Qt::PenCapStyle style;
+ if (cap == QLatin1String("round"))
+ style = Qt::RoundCap;
+ else if (cap == QLatin1String("square"))
+ style = Qt::SquareCap;
+ else //if (capString == "butt")
+ style = Qt::FlatCap;
+
+
+ if (state.lineCap != style) {
+ state.pen.setCapStyle(style);
+ state.lineCap = style;
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setLineJoin( const QString& join)
+{
+ Qt::PenJoinStyle style;
+ if (join == QLatin1String("round"))
+ style = Qt::RoundJoin;
+ else if (join == QLatin1String("bevel"))
+ style = Qt::BevelJoin;
+ else //if (joinString == "miter")
+ style = Qt::MiterJoin;
+ if (state.lineJoin != style) {
+ state.lineJoin = style;
+ state.pen.setJoinStyle(style);
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setMiterLimit( qreal limit)
+{
+ if (state.miterLimit != limit) {
+ state.pen.setMiterLimit(limit);
+ state.miterLimit = limit;
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setShadowOffsetX( qreal x)
+{
+ if (state.shadowOffsetX != x) {
+ state.shadowOffsetX = x;
+ commands.push_back(QSGContext2D::ShadowOffsetX);
+ reals.push_back(x);
+ }
+}
+
+void QSGContext2DPrivate::setShadowOffsetY( qreal y)
+{
+ if (state.shadowOffsetY != y) {
+ state.shadowOffsetY = y;
+ commands.push_back(QSGContext2D::ShadowOffsetY);
+ reals.push_back(y);
+ }
+}
+
+void QSGContext2DPrivate::setShadowBlur( qreal b)
+{
+ if (state.shadowBlur != b) {
+ state.shadowBlur = b;
+ commands.push_back(QSGContext2D::ShadowBlur);
+ reals.push_back(b);
+ }
+}
+
+void QSGContext2DPrivate::setShadowColor( const QString& color)
+{
+ QColor c = colorFromString(color);
+ if (state.shadowColor != c) {
+ state.shadowColor = c;
+ commands.push_back(QSGContext2D::ShadowColor);
+ colors.push_back(c);
+ }
+}
+
+void QSGContext2DPrivate::setFont( const QString& fontString)
+{
+ QFont font;
+ // ### this is simplified and incomplete
+ // ### TODO:get code from Qt webkit
+ QStringList tokens = fontString.split(QLatin1String(" "));
+ foreach (const QString &token, tokens) {
+ if (token == QLatin1String("italic"))
+ font.setItalic(true);
+ else if (token == QLatin1String("bold"))
+ font.setBold(true);
+ else if (token.endsWith(QLatin1String("px"))) {
+ QString number = token;
+ number.remove(QLatin1String("px"));
+ font.setPointSizeF(number.trimmed().toFloat());
+ } else
+ font.setFamily(token);
+ }
+
+ if (state.font != font) {
+ state.font = font;
+ commands.push_back(QSGContext2D::Font);
+ fonts.push_back(font);
+ }
+}
+
+void QSGContext2DPrivate::setTextBaseline( const QString& baseline)
+{
+ QSGContext2D::TextBaseLineType tbl;
+ if (baseline==QLatin1String("alphabetic"))
+ tbl = QSGContext2D::Alphabetic;
+ else if (baseline == QLatin1String("hanging"))
+ tbl = QSGContext2D::Hanging;
+ else if (baseline == QLatin1String("top"))
+ tbl = QSGContext2D::Top;
+ else if (baseline == QLatin1String("bottom"))
+ tbl = QSGContext2D::Bottom;
+ else if (baseline == QLatin1String("middle"))
+ tbl = QSGContext2D::Middle;
+ else {
+ tbl = QSGContext2D::Alphabetic;
+ Q_Q(QSGContext2D);
+ qmlInfo(q) << "QSGContext2D: invalid baseline:" << baseline;
+ }
+ if (state.textBaseline != tbl) {
+ state.textBaseline = tbl;
+ commands.push_back(QSGContext2D::TextBaseline);
+ ints.push_back(tbl);
+ }
+}
+
+void QSGContext2DPrivate::setTextAlign(const QString& align)
+{
+ QSGContext2D::TextAlignType ta;
+ if (align==QLatin1String("start"))
+ ta = QSGContext2D::Start;
+ else if (align == QLatin1String("end"))
+ ta = QSGContext2D::End;
+ else if (align == QLatin1String("left"))
+ ta = QSGContext2D::Left;
+ else if (align == QLatin1String("right"))
+ ta = QSGContext2D::Right;
+ else if (align == QLatin1String("center"))
+ ta = QSGContext2D::Center;
+ else {
+ ta = QSGContext2D::Start;
+ Q_Q(QSGContext2D);
+ qmlInfo(q) << "QSGContext2D: invalid text align:" << align;
+ }
+ if (state.textAlign != ta) {
+ state.textAlign = ta;
+ commands.push_back(QSGContext2D::TextAlign);
+ ints.push_back(ta);
+ }
+}
+
+void QSGContext2DPrivate::fillText(const QString& text, qreal x, qreal y)
+{
+ commands.push_back(QSGContext2D::FillText);
+ strings.push_back(text);
+ reals.push_back(x);
+ reals.push_back(y);
+ ints.push_back(state.textAlign);
+ ints.push_back(state.textBaseline);
+}
+
+
+void QSGContext2DPrivate::strokeText( const QString& text, qreal x, qreal y)
+{
+ commands.push_back(QSGContext2D::StrokeText);
+ strings.push_back(text);
+ reals.push_back(x);
+ reals.push_back(y);
+ ints.push_back(state.textAlign);
+ ints.push_back(state.textBaseline);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy)
+{
+ commands.push_back(QSGContext2D::DrawImage1);
+ strings.push_back(url);
+ reals.push_back(dx);
+ reals.push_back(dy);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+ commands.push_back(QSGContext2D::DrawImage2);
+ strings.push_back(url);
+ reals.push_back(dx);
+ reals.push_back(dy);
+ reals.push_back(dw);
+ reals.push_back(dh);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+ commands.push_back(QSGContext2D::DrawImage3);
+ strings.push_back(url);
+ reals.push_back(sx);
+ reals.push_back(sy);
+ reals.push_back(sw);
+ reals.push_back(sh);
+ reals.push_back(dx);
+ reals.push_back(dy);
+ reals.push_back(dw);
+ reals.push_back(dh);
+}
+
+QList<int> QSGContext2DPrivate::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+ Q_Q(QSGContext2D);
+ waitingForPainting = true;
+ commands.push_back(QSGContext2D::GetImageData);
+ reals.push_back(sx);
+ reals.push_back(sy);
+ reals.push_back(sw);
+ reals.push_back(sh);
+ q->sync();
+ return imageData;
+}
+
+void QSGContext2DPrivate::putImageData(const QVariantList& imageData, qreal dx, qreal dy, qreal w, qreal h)
+{
+ QImage image = cachedImage.copy(dx, dy, w, h);
+ uchar* data = image.bits();
+ int i = 0;
+ while(i< imageData.size() && i < image.byteCount()) {
+ //the stored order in QImage:BGRA
+ //the stored order in Canvas:RGBA
+ *(data+i) = imageData[i+2].toInt();//B
+ *(data+i+1) = imageData[i+1].toInt();//G
+ *(data+i+2) = imageData[i].toInt();//R
+ *(data+i+3) = imageData[i+3].toInt();//A
+ i+=4;
+ }
+ commands.push_back(QSGContext2D::PutImageData);
+ images.push_back(image);
+ reals.push_back(dx);
+ reals.push_back(dy);
+}
+
+void QSGContext2D::save()
+{
+ Q_D(QSGContext2D);
+ d->save();
+}
+
+
+void QSGContext2D::restore()
+{
+ Q_D(QSGContext2D);
+ d->restore();
+}
+
+
+void QSGContext2D::scale(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->scale(x, y);
+}
+
+
+void QSGContext2D::rotate(qreal angle)
+{
+ Q_D(QSGContext2D);
+ d->rotate(angle);
+}
+
+
+void QSGContext2D::translate(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->translate(x, y);
+}
+
+void QSGContext2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ Q_D(QSGContext2D);
+ d->transform(m11, m12, m21, m22, dx, dy);
+}
+
+
+void QSGContext2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ Q_D(QSGContext2D);
+ d->setTransform(m11, m12, m21, m22, dx, dy);
+}
+
+QString QSGContext2D::globalCompositeOperation() const
+{
+ Q_D(const QSGContext2D);
+ return compositeOperatorToString(d->state.globalCompositeOperation);
+}
+
+void QSGContext2D::setGlobalCompositeOperation(const QString &op)
+{
+ Q_D(QSGContext2D);
+ d->setGlobalCompositeOperation(op);
+}
+
+QVariant QSGContext2D::strokeStyle() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.strokeStyle;
+}
+
+void QSGContext2D::setStrokeStyle(const QVariant &style)
+{
+ Q_D(QSGContext2D);
+ d->setStrokeStyle(style);
+}
+
+QVariant QSGContext2D::fillStyle() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.fillStyle;
+}
+
+QColor QSGContext2D::strokeColor() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.strokeStyle.color();
+}
+
+QColor QSGContext2D::fillColor() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.fillStyle.color();
+}
+
+void QSGContext2D::setFillStyle(const QVariant &style)
+{
+ Q_D(QSGContext2D);
+ d->setFillStyle(style);
+}
+void QSGContext2D::setStrokeColor(const QColor& color)
+{
+ Q_D(QSGContext2D);
+ d->setStrokeColor(color);
+}
+
+void QSGContext2D::setFillColor(const QColor& color)
+{
+ Q_D(QSGContext2D);
+ d->setFillColor(color);
+}
+
+qreal QSGContext2D::globalAlpha() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.globalAlpha;
+}
+
+void QSGContext2D::setGlobalAlpha(qreal alpha)
+{
+ Q_D(QSGContext2D);
+ d->setGlobalAlpha(alpha);
+}
+
+QSGImage *QSGContext2D::createImage(const QString &url)
+{
+ Q_D(QSGContext2D);
+//### cache image
+ QSGImage* img = new QSGImage(d->canvas);
+ img->setSource(QUrl(url));
+ return img;
+}
+
+QSGCanvasGradient *QSGContext2D::createLinearGradient(qreal x0, qreal y0,
+ qreal x1, qreal y1)
+{
+ QLinearGradient g(x0, y0, x1, y1);
+ return new QSGCanvasGradient(g);
+}
+
+
+QSGCanvasGradient *QSGContext2D::createRadialGradient(qreal x0, qreal y0,
+ qreal r0, qreal x1,
+ qreal y1, qreal r1)
+{
+ QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ return new QSGCanvasGradient(g);
+}
+
+qreal QSGContext2D::lineWidth() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.lineWidth;
+}
+
+void QSGContext2D::setLineWidth(qreal w)
+{
+ Q_D(QSGContext2D);
+ d->setLineWidth(w);
+}
+
+QString QSGContext2D::lineCap() const
+{
+ Q_D(const QSGContext2D);
+ switch(d->state.lineCap) {
+ case Qt::RoundCap:
+ return QLatin1String("round");
+ case Qt::FlatCap:
+ return QLatin1String("butt");
+ case Qt::SquareCap:
+ return QLatin1String("square");
+ default:
+ break;
+ }
+ return QLatin1String("");
+}
+
+void QSGContext2D::setLineCap(const QString &capString)
+{
+ Q_D(QSGContext2D);
+ d->setLineCap(capString);
+}
+
+QString QSGContext2D::lineJoin() const
+{
+ Q_D(const QSGContext2D);
+ switch (d->state.lineJoin) {
+ case Qt::RoundJoin:
+ return QLatin1String("round");
+ case Qt::BevelJoin:
+ return QLatin1String("bevel");
+ case Qt::MiterJoin:
+ return QLatin1String("miter");
+ default:
+ break;
+ }
+ return QLatin1String("");
+}
+
+void QSGContext2D::setLineJoin(const QString &joinString)
+{
+ Q_D(QSGContext2D);
+ d->setLineJoin(joinString);
+}
+
+qreal QSGContext2D::miterLimit() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.miterLimit;
+}
+
+void QSGContext2D::setMiterLimit(qreal m)
+{
+ Q_D(QSGContext2D);
+ d->setMiterLimit(m);
+}
+
+void QSGContext2D::setShadowOffsetX(qreal x)
+{
+ Q_D(QSGContext2D);
+ d->setShadowOffsetX(x);
+}
+
+void QSGContext2D::setShadowOffsetY(qreal y)
+{
+ Q_D(QSGContext2D);
+ d->setShadowOffsetY(y);
+}
+
+void QSGContext2D::setShadowBlur(qreal b)
+{
+ Q_D(QSGContext2D);
+ d->setShadowBlur(b);
+}
+
+void QSGContext2D::setShadowColor(const QString &str)
+{
+ Q_D(QSGContext2D);
+ d->setShadowColor(str);
+}
+
+QString QSGContext2D::textBaseline() const
+{
+ Q_D(const QSGContext2D);
+ switch(d->state.textBaseline) {
+ case QSGContext2D::Alphabetic:
+ return QLatin1String("alphabetic");
+ case QSGContext2D::Hanging:
+ return QLatin1String("hanging");
+ case QSGContext2D::Top:
+ return QLatin1String("top");
+ case QSGContext2D::Bottom:
+ return QLatin1String("bottom");
+ case QSGContext2D::Middle:
+ return QLatin1String("middle");
+ default:
+ break;
+ }
+ return QLatin1String("alphabetic");
+}
+
+void QSGContext2D::setTextBaseline(const QString &baseline)
+{
+ Q_D(QSGContext2D);
+ d->setTextBaseline(baseline);
+}
+
+QString QSGContext2D::textAlign() const
+{
+ Q_D(const QSGContext2D);
+ switch(d->state.textAlign) {
+ case QSGContext2D::Start:
+ return QLatin1String("start");
+ case QSGContext2D::End:
+ return QLatin1String("end");
+ case QSGContext2D::Left:
+ return QLatin1String("left");
+ case QSGContext2D::Right:
+ return QLatin1String("right");
+ case QSGContext2D::Center:
+ return QLatin1String("center");
+ default:
+ break;
+ }
+ return QLatin1String("start");
+}
+
+void QSGContext2D::setTextAlign(const QString &align)
+{
+ Q_D(QSGContext2D);
+ d->setTextAlign(align);
+
+ d->commands.push_back(QSGContext2D::TextAlign);
+ d->ints.push_back(d->state.textAlign);
+}
+
+void QSGContext2D::setFont(const QString &fontString)
+{
+ Q_D(QSGContext2D);
+ d->setFont(fontString);
+}
+
+QString QSGContext2D::font() const
+{
+ //### TODO
+ Q_D(const QSGContext2D);
+ return d->state.font.toString();
+}
+
+qreal QSGContext2D::shadowOffsetX() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowOffsetX;
+}
+
+qreal QSGContext2D::shadowOffsetY() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowOffsetY;
+}
+
+
+qreal QSGContext2D::shadowBlur() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowBlur;
+}
+
+
+QString QSGContext2D::shadowColor() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowColor.name();
+}
+
+
+void QSGContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->clearRect(x, y, w, h);
+}
+
+void QSGContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->fillRect(x, y, w, h);
+}
+
+int QSGContext2DPrivate::baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics)
+{
+ int offset = 0;
+ switch (value) {
+ case QSGContext2D::Top:
+ break;
+ case QSGContext2D::Alphabetic:
+ case QSGContext2D::Middle:
+ case QSGContext2D::Hanging:
+ offset = metrics.ascent();
+ break;
+ case QSGContext2D::Bottom:
+ offset = metrics.height();
+ break;
+ }
+ return offset;
+}
+
+int QSGContext2DPrivate::textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
+{
+ int offset = 0;
+ if (value == QSGContext2D::Start)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right;
+ else if (value == QSGContext2D::End)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left;
+ switch (value) {
+ case QSGContext2D::Center:
+ offset = metrics.width(text)/2;
+ break;
+ case QSGContext2D::Right:
+ offset = metrics.width(text);
+ case QSGContext2D::Left:
+ default:
+ break;
+ }
+ return offset;
+}
+
+void QSGContext2D::fillText(const QString &text, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->fillText(text, x, y);
+}
+
+void QSGContext2D::strokeText(const QString &text, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->strokeText(text, x, y);
+}
+
+void QSGContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->strokeRect(x, y, w, h);
+}
+
+void QSGContext2D::beginPath()
+{
+ Q_D(QSGContext2D);
+ d->beginPath();
+}
+
+
+void QSGContext2D::closePath()
+{
+ Q_D(QSGContext2D);
+ d->closePath();
+}
+
+
+void QSGContext2D::moveTo(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->moveTo(x, y);
+}
+
+
+void QSGContext2D::lineTo(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->lineTo(x, y);
+}
+
+
+void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->quadraticCurveTo(cpx, cpy, x, y);
+}
+
+
+void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+}
+
+
+void QSGContext2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
+{
+ Q_D(QSGContext2D);
+ d->arcTo(x1, y1, x2, y2, radius);
+}
+
+
+void QSGContext2D::rect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->rect(x, y, w, h);
+}
+
+void QSGContext2D::arc(qreal xc, qreal yc, qreal radius,
+ qreal sar, qreal ear,
+ bool anticlockwise)
+{
+ Q_D(QSGContext2D);
+ d->arc(xc, yc, radius, sar, ear, anticlockwise);
+}
+
+
+void QSGContext2D::fill()
+{
+ Q_D(QSGContext2D);
+ d->fill();
+}
+
+
+void QSGContext2D::stroke()
+{
+ Q_D(QSGContext2D);
+ d->stroke();
+}
+
+
+void QSGContext2D::clip()
+{
+ Q_D(QSGContext2D);
+ d->clip();
+}
+
+
+bool QSGContext2D::isPointInPath(qreal x, qreal y) const
+{
+ Q_D(const QSGContext2D);
+ return d->path.contains(QPointF(x, y));
+}
+
+
+QList<int> QSGContext2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+ Q_D(QSGContext2D);
+ return d->getImageData(sx, sy, sw, sh);
+}
+
+void QSGContext2D::putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ return d->putImageData(imageData.toList(), x, y, w, h);
+}
+
+QSGContext2D::QSGContext2D(QObject *parent)
+ : QObject(*(new QSGContext2DPrivate()), parent)
+{
+ Q_D(QSGContext2D);
+ d->canvas = qobject_cast<QSGCanvasItem*>(parent);
+}
+
+QSGContext2D::QSGContext2D(QSGContext2D *orig, QSGContext2DWorkerAgent* agentData)
+ : QObject(*(new QSGContext2DPrivate()), 0)
+{
+ Q_D(QSGContext2D);
+ d->agent = 0;
+ d->agentData = agentData;
+ if (d->agentData) {
+ d->agentData->orig = orig;
+ }
+ d->canvas = qobject_cast<QSGCanvasItem*>(orig);
+}
+
+QSGContext2D::~QSGContext2D()
+{
+ Q_D(QSGContext2D);
+ if (d->agent) {
+ d->agentData->syncDone.wakeAll();
+ d->agent->release();
+ }
+}
+
+bool QSGContext2D::isDirty() const
+{
+ Q_D(const QSGContext2D);
+ return !d->commands.isEmpty();
+}
+
+QScriptValue QSGContext2D::scriptValue() const
+{
+ Q_D(const QSGContext2D);
+ return d->scriptValue;
+}
+
+void QSGContext2D::setScriptEngine(QScriptEngine *eng)
+{
+ Q_D(QSGContext2D);
+ if (d->scriptEngine != eng) {
+ d->scriptEngine = eng;
+// QScriptValue agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent"));
+// if (!agent.isValid()) {
+// d->scriptEngine->evaluate(QLatin1String(
+// "(function CanvasImageData(w, h, d) {"
+// " this.widht = w;"
+// " this.height = h;"
+// " this.data = d;"
+// " })"));
+// d->scriptEngine->evaluate(agentScript());
+// agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent"));
+// if (!agent.isValid()) {
+// qWarning() << "QSGContext2D:error when evaluating context2d script value!";
+// d->scriptValue = QScriptValue();
+// return;
+// }
+// }
+// QScriptValue o = d->scriptEngine->newQObject(this);
+// d->scriptValue = agent.construct(QScriptValueList() << o);
+ }
+}
+
+QScriptEngine *QSGContext2D::scriptEngine() const
+{
+ Q_D(const QSGContext2D);
+ return d->scriptEngine;
+}
+
+void QSGContext2D::addref()
+{
+ Q_D(QSGContext2D);
+ Q_ASSERT(d->agentData);
+ d->agentData->ref.ref();
+}
+
+void QSGContext2D::release()
+{
+ Q_D(QSGContext2D);
+ Q_ASSERT(d->agentData);
+ if (!d->agentData->ref.deref()) {
+ deleteLater();
+ }
+}
+
+
+bool QSGContext2D::inWorkerThread() const
+{
+ Q_D(const QSGContext2D);
+ return d->agentData != 0;
+}
+const QString& QSGContext2D::agentScript() const
+{
+ static QString script;
+ if (script.isEmpty()) {
+ script = QString::fromLatin1(
+ "function CanvasImageData(w, h, d) {"
+ " this.width = w;"
+ " this.height = h;"
+ " this.data = d;"
+ "}"
+ "function Context2DAgent(_ctx2d) {"
+ " this._ctx = _ctx2d;"
+ " this._fillColor = '#000000';"
+ " this._fillStyle = '#000000';"
+ " this._strokeColor = '#000000';"
+ " this._strokeStyle = '#000000';"
+ " this._globalCompositeOperation = \"source-over\";"
+ " this._commands = [];"
+ " this.createImageData = function() {"
+ " var d = null;"
+ " if (arguments.length == 1 && arguments[0] instanceof CanvasImageData) {"
+ " d = new CanvasImageData(arguments[0].width,"
+ " arguments[0].height,"
+ " new Array(arguments[0].width * arguments[0].height * 4));"
+ " } else if (arguments.length == 2) {"
+ " d = new CanvasImageData(arguments[0], arguments[1], new Array(arguments[0] * arguments[1] * 4));"
+ " }"
+ " if (d)"
+ " for (var i=0; i<d.data.length; i++)"
+ " d.data[i] = 255;"
+ " return d;"
+ " };"
+ " this.getImageData = function(sx, sy, sw, sh) {"
+ " var imageData = new CanvasImageData(sw, sh, this._ctx.getImageData(sx, sy, sw, sh));"
+ " return imageData;"
+ " };"
+ " this.sync = function() {"
+ " this._ctx.processCommands(this._commands);"
+ " this._commands.length = 0;"
+ " };");
+
+ script.append(QString::fromLatin1(
+ "this.save = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Save));
+
+ script.append(QString::fromLatin1(
+ "this.restore = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Restore));
+
+ script.append(QString::fromLatin1(
+ "this.scale = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(Scale));
+
+ script.append(QString::fromLatin1(
+ "this.createImage = function(url) {"
+ " return this._ctx.createImage(url);"
+ "};"));
+
+ script.append(QString::fromLatin1(
+ "this.rotate = function(x) {"
+ " this._commands.push([%1, x]);"
+ "};").arg(Rotate));
+
+ script.append(QString::fromLatin1(
+ "this.translate = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(Translate));
+
+ script.append(QString::fromLatin1(
+ "this.transform = function(a1, a2, a3, a4, a5, a6) {"
+ " this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+ "};").arg(Transform));
+
+ script.append(QString::fromLatin1(
+ "this.setTransform = function(a1, a2, a3, a4, a5, a6) {"
+ " this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+ "};").arg(SetTransform));
+
+ script.append(QString::fromLatin1(
+ "this.clearRect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(ClearRect));
+
+ script.append(QString::fromLatin1(
+ "this.fillRect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(FillRect));
+
+ script.append(QString::fromLatin1(
+ "this.strokeRect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(StrokeRect));
+
+ script.append(QString::fromLatin1(
+ "this.beginPath = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(BeginPath));
+
+ script.append(QString::fromLatin1(
+ "this.closePath = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(ClosePath));
+
+ script.append(QString::fromLatin1(
+ "this.moveTo = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(MoveTo));
+
+ script.append(QString::fromLatin1(
+ "this.lineTo = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(LineTo));
+
+ script.append(QString::fromLatin1(
+ "this.quadraticCurveTo = function(a1, a2, a3, a4) {"
+ " this._commands.push([%1, a1, a2, a3, a4]);"
+ "};").arg(QuadraticCurveTo));
+
+ script.append(QString::fromLatin1(
+ "this.bezierCurveTo = function(a1, a2, a3, a4, a5, a6) {"
+ " this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+ "};").arg(BezierCurveTo));
+
+ script.append(QString::fromLatin1(
+ "this.arcTo = function(x1, y1, x2, y2, radius) {"
+ " this._commands.push([%1, x1, y1, x2, y2, radius]);"
+ "};").arg(ArcTo));
+
+ script.append(QString::fromLatin1(
+ "this.rect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(Rect));
+
+ script.append(QString::fromLatin1(
+ "this.rect = function(x, y, radius, startAngle, endAngle, anticlockwise) {"
+ " this._commands.push([%1, x, y, radius, startAngle, endAngle, anticlockwise]);"
+ "};").arg(Arc));
+
+ script.append(QString::fromLatin1(
+ "this.fill = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Fill));
+
+ script.append(QString::fromLatin1(
+ "this.stroke = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Stroke));
+
+ script.append(QString::fromLatin1(
+ "this.clip = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Clip));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"globalAlpha\", function() {"
+ " return this._globalAlpha;"
+ " });"
+ " this.__defineSetter__(\"globalAlpha\", function(v) {"
+ " this._globalAlpha = v;"
+ " this._commands.push([%1, v]);"
+ " });").arg(GlobalAlpha));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"globalCompositeOperation\", function() {"
+ " return this._globalCompositeOperation;"
+ " });"
+ " this.__defineSetter__(\"globalCompositeOperation\", function(v) {"
+ " this._globalCompositeOperation = v;"
+ " this._commands.push([%1, v]);"
+ " });").arg(GlobalCompositeOperation));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"strokeStyle\", function() {return this._strokeStyle; });"
+ " this.__defineSetter__(\"strokeStyle\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._strokeStyle = v;"
+ " });").arg(StrokeStyle));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"fillStyle\", function() {return this._fillStyle; });"
+ " this.__defineSetter__(\"fillStyle\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._fillStyle = v;"
+ " });").arg(FillStyle));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"strokeColor\", function() {return this._strokeColor; });"
+ " this.__defineSetter__(\"strokeColor\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._strokeColor = v;"
+ " });").arg(StrokeColor));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"fillColor\", function() {return this._fillColor; });"
+ " this.__defineSetter__(\"fillColor\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._fillColor = v;"
+ " });").arg(FillColor));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"lineWidth\", function() {return this._lineWidth; });"
+ " this.__defineSetter__(\"lineWidth\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._lineWidth = v;"
+ " });").arg(LineWidth));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"lineCap\", function() {return this._lineCap; });"
+ " this.__defineSetter__(\"lineCap\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._lineCap = v;"
+ " });").arg(LineCap));
+
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"lineJoin\", function() {return this._lineJoin; });"
+ " this.__defineSetter__(\"lineJoin\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._lineJoin = v;"
+ " });").arg(LineJoin));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"miterLimit\", function() {return this._miterLimit; });"
+ " this.__defineSetter__(\"miterLimit\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._miterLimit = v;"
+ " });").arg(MiterLimit));
+
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowOffsetX\", function() {return this._shadowOffsetX; });"
+ " this.__defineSetter__(\"shadowOffsetX\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowOffsetX = v;"
+ " });").arg(ShadowOffsetX));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowOffsetY\", function() {return this._shadowOffsetY; });"
+ " this.__defineSetter__(\"shadowOffsetY\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowOffsetY = v;"
+ " });").arg(ShadowOffsetY));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowBlur\", function() {return this._shadowBlur; });"
+ " this.__defineSetter__(\"shadowBlur\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowBlur = v;"
+ " });").arg(ShadowBlur));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowColor\", function() {return this._shadowColor; });"
+ " this.__defineSetter__(\"shadowColor\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowColor = v;"
+ " });").arg(ShadowColor));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"font\", function() {return this._font; });"
+ " this.__defineSetter__(\"font\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._font = v;"
+ " });").arg(Font));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"textBaseline\", function() {return this._textBaseline; });"
+ " this.__defineSetter__(\"textBaseline\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._textBaseline = v;"
+ " });").arg(TextBaseline));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"textAlign\", function() {return this._textAlign; });"
+ " this.__defineSetter__(\"textAlign\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._textAlign = v;"
+ " });").arg(TextAlign));
+
+ script.append(QString::fromLatin1(
+ "this.fillText = function(text, x, y) {"
+ " this._commands.push([%1, text, x, y]);"
+ "};").arg(FillText));
+
+ script.append(QString::fromLatin1(
+ "this.strokeText = function(text, x, y) {"
+ " this._commands.push([%1, text, x, y]);"
+ "};").arg(StrokeText));
+
+ script.append(QString::fromLatin1(
+ "this.drawImage = function() {"
+ " if (arguments.length == 3) {"
+ " this._commands.push([%1, arguments[0], arguments[1], arguments[2]]);"
+ " } else if (arguments.length == 5) {"
+ " this._commands.push([%2, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]]);"
+ " } else if (arguments.length == 9) {"
+ " this._commands.push([%3, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]]);}"
+ "};").arg(DrawImage1).arg(DrawImage2).arg(DrawImage3));
+
+ script.append(QString::fromLatin1(
+ "this.putImageData = function() {"
+ " var dx = arguments[1];"
+ " var dy = arguments[2];"
+ " if (arguments.length == 3) {"
+ " this._commands.push([%1, arguments[0].data, dx, dy, arguments[0].width, arguments[0].height]);"
+ " } else if (arguments.length == 7) {"
+ " var dirtyX = arguments[3];"
+ " var dirtyY = arguments[4];"
+ " var dirtyWidth = arguments[5];"
+ " var dirtyHeight = arguments[6];"
+ " var width = arguments[0].width;"
+ " var height = arguments[0].heigh;"
+ " var filteredData = arguments[0].data.filter(function(element, index, array){"
+ " var x=index/width;"
+ " var y=index%width-1;"
+ " return x >= dirtyX && x < dirtyX+dirtyWidth"
+ " && y >= dirtyY && y < dirtyY+dirtyHeight;"
+ " });"
+ " this._commands.push([%2, filteredData, dx, dy, dirtyWidth, dirtyHeight]);"
+ " }"
+ "};").arg(PutImageData).arg(PutImageData));
+ script.append(QString::fromLatin1("}"));
+ }
+ return script;
+}
+
+QSGContext2D *QSGContext2D::agent()
+{
+ Q_D(QSGContext2D);
+
+ if (d->agent)
+ return d->agent;
+
+ d->agent = new QSGContext2D(this, new QSGContext2DWorkerAgent);
+ connect(this, SIGNAL(painted()), d->agent, SIGNAL(painted()));
+ d->agent->setSize(size());
+ return d->agent;
+
+}
+void QSGContext2D::processCommands(const QScriptValue& commands)
+{
+#ifdef QSGCANVASITEM_DEBUG
+ QElapsedTimer t;
+ t.start();
+#endif
+ int ii = 0;
+ if (commands.isArray()) {
+ QScriptValue cmd = commands.property(ii);
+ while(cmd.isValid()) {
+ processCommand(cmd);
+ ii++;
+ cmd = commands.property(ii);
+ }
+ }
+
+#ifdef QSGCANVASITEM_DEBUG
+ qDebug() << "processed" << ii << "commands in " << t.nsecsElapsed() << "nsecs";
+#endif
+ sync();
+}
+
+void QSGContext2D::sync()
+{
+ Q_D(QSGContext2D);
+
+#ifdef QSGCANVASITEM_DEBUG
+ QElapsedTimer t;
+ t.start();
+#endif
+ if (d->agentData) {
+ if (d->agentData->ref == 1) return;
+
+ Sync *s = new Sync;
+ s->data = d->agentData;
+
+ d->agentData->mutex.lock();
+ QCoreApplication::postEvent(this, s);
+ d->agentData->syncDone.wait(&d->agentData->mutex);
+ d->agentData->mutex.unlock();
+ } else {
+ //qmlInfo(this) << "Context2D sync() can only be called from a WorkerScript;";
+ emit changed();
+ }
+
+#ifdef QSGCANVASITEM_DEBUG
+ qDebug() << "syncing time:" << t.nsecsElapsed();
+#endif
+}
+
+
+bool QSGContext2D::event(QEvent *e)
+{
+ Q_D(QSGContext2D);
+ if (e->type() == QEvent::User && d->agentData) {
+ QMutexLocker locker(&d->agentData->mutex);
+ Sync *s = static_cast<Sync *>(e);
+
+ QSGContext2DPrivate* origin_d = static_cast<QSGContext2DPrivate*>(s->data->orig->d_func());
+
+ //quick copy
+ memcpy_vector<PaintCommand>(&origin_d->commands, d->commands);
+ memcpy_vector<int>(&origin_d->ints, d->ints);
+ memcpy_vector<qreal>(&origin_d->reals, d->reals);
+ memcpy_vector<QColor>(&origin_d->colors, d->colors);
+ memcpy_vector<QMatrix>(&origin_d->matrixes, d->matrixes);
+ memcpy_vector<QSize>(&origin_d->sizes, d->sizes);
+
+ //slow copy
+ copy_vector<QString>(&origin_d->strings, d->strings);
+ copy_vector<QVariant>(&origin_d->variants, d->variants);
+ copy_vector<QPen>(&origin_d->pens, d->pens);
+ copy_vector<QBrush>(&origin_d->brushes, d->brushes);
+ copy_vector<QPainterPath>(&origin_d->pathes, d->pathes);
+ copy_vector<QFont>(&origin_d->fonts, d->fonts);
+ copy_vector<QImage>(&origin_d->images, d->images);
+ origin_d->state = d->state;
+ d->clearCommands();
+
+ if (d->waitingForPainting) {
+ d->imageData.clear();
+ origin_d->imageData.clear();
+ emit s->data->orig->changed();
+ while(origin_d->imageData.isEmpty()) {
+ QCoreApplication::processEvents();
+ }
+ d->imageData = origin_d->imageData;
+ d->waitingForPainting = false;
+ qDebug() << "imageData size:" << d->imageData.size();
+ } else {
+ emit s->data->orig->changed();
+ }
+
+ d->agentData->syncDone.wakeAll();
+ return true;
+ }
+ return QObject::event(e);
+}
+
+void QSGContext2D::processCommand(const QScriptValue& cmd)
+{
+ int action = cmd.property(0).toInt32();
+ switch (action) {
+ case QSGContext2D::Save:
+ save();
+ break;
+ case QSGContext2D::Restore:
+ restore();
+ break;
+ case QSGContext2D::Scale:
+ scale(cmd.property(1).toNumber(), cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::Rotate:
+ rotate(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::Translate:
+ translate(cmd.property(1).toNumber(), cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::Transform:
+ transform(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber());
+ break;
+ case QSGContext2D::SetTransform:
+ setTransform(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber());
+ break;
+ case QSGContext2D::ClearRect:
+ clearRect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::FillRect:
+ fillRect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::StrokeRect:
+ strokeRect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::BeginPath:
+ beginPath();
+ break;
+ case QSGContext2D::ClosePath:
+ closePath();
+ break;
+ case QSGContext2D::MoveTo:
+ moveTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::LineTo:
+ lineTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::QuadraticCurveTo:
+ quadraticCurveTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::BezierCurveTo:
+ bezierCurveTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber());
+ break;
+ case QSGContext2D::ArcTo:
+ arcTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber());
+ break;
+ case QSGContext2D::Rect:
+ rect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::Arc:
+ arc(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toBool());
+ break;
+ case QSGContext2D::Fill:
+ fill();
+ break;
+ case QSGContext2D::Stroke:
+ stroke();
+ break;
+ case QSGContext2D::Clip:
+ clip();
+ break;
+ case QSGContext2D::GlobalAlpha:
+ setGlobalAlpha(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::GlobalCompositeOperation:
+ setGlobalCompositeOperation(cmd.property(1).toString());
+ break;
+ case QSGContext2D::StrokeStyle:
+ setStrokeStyle(cmd.property(1).toVariant());
+ break;
+ case QSGContext2D::FillStyle:
+ setFillStyle(cmd.property(1).toVariant());
+ break;
+ case QSGContext2D::FillColor:
+ setFillColor(cmd.property(1).toVariant().value<QColor>());
+ break;
+ case QSGContext2D::StrokeColor:
+ setStrokeColor(cmd.property(1).toVariant().value<QColor>());
+ break;
+ case QSGContext2D::LineWidth:
+ setLineWidth(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::LineCap:
+ setLineCap(cmd.property(1).toString());
+ break;
+ case QSGContext2D::LineJoin:
+ setLineJoin(cmd.property(1).toString());
+ break;
+ case QSGContext2D::MiterLimit:
+ setMiterLimit(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowOffsetX:
+ setShadowOffsetX(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowOffsetY:
+ setShadowOffsetY(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowBlur:
+ setShadowBlur(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowColor:
+ setShadowColor(cmd.property(1).toString());
+ break;
+ case QSGContext2D::Font:
+ setFont(cmd.property(1).toString());
+ break;
+ case QSGContext2D::TextBaseline:
+ setTextBaseline(cmd.property(1).toString());
+ break;
+ case QSGContext2D::TextAlign:
+ setTextAlign(cmd.property(1).toString());
+ break;
+ case QSGContext2D::FillText:
+ fillText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
+ break;
+ case QSGContext2D::StrokeText:
+ strokeText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
+ break;
+ case QSGContext2D::DrawImage1:
+ {
+ drawImage(cmd.property(1).toString(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber());
+ break;
+ }
+ case QSGContext2D::DrawImage2:
+ drawImage(cmd.property(1).toString(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber());
+ break;
+ case QSGContext2D::DrawImage3:
+ drawImage(cmd.property(1).toString(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber(),
+ cmd.property(7).toNumber(),
+ cmd.property(8).toNumber(),
+ cmd.property(9).toNumber());
+ break;
+ case QSGContext2D::PutImageData:
+ putImageData(cmd.property(1).toVariant(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber());
+ break;
+ default:
+ break;
+ }
+}
+
+void QSGContext2D::paint(QPainter* p)
+{
+ Q_D(QSGContext2D);
+
+ QTransform transform = p->worldTransform();
+ if (!d->commands.isEmpty()) {
+ int matrix_idx, real_idx, int_idx, variant_idx, string_idx,color_idx,cmd_idx,
+ pen_idx, brush_idx, font_idx, path_idx, image_idx, size_idx;
+
+ matrix_idx = real_idx = int_idx = variant_idx = string_idx =color_idx = cmd_idx
+ = pen_idx = brush_idx = font_idx = path_idx = image_idx = size_idx = 0;
+
+ foreach(PaintCommand cmd, d->commands) {
+ switch (cmd) {
+ case UpdateMatrix:
+ {
+// qDebug() << "update matrix from " << d->state.matrix << " to " << d->matrixes[matrix_idx];
+ //p->setWorldTransform(transform * QTransform(d->matrixes[matrix_idx++]), false);
+ //p->setMatrix(d->matrixes[matrix_idx++]);
+ d->state.matrix = d->matrixes[matrix_idx++];
+ break;
+ }
+ case ClearRect:
+ {
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ qreal w = d->reals[real_idx++];
+ qreal h = d->reals[real_idx++];
+ p->eraseRect(QRectF(x, y, w, h));
+ break;
+ }
+ case FillRect:
+ {
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ qreal w = d->reals[real_idx++];
+ qreal h = d->reals[real_idx++];
+ if (d->hasShadow())
+ d->fillRectShadow(p, QRectF(x, y, w, h));
+ else
+ p->fillRect(QRectF(x, y, w, h), p->brush());
+ break;
+ }
+ case ShadowColor:
+ {
+ QColor c = d->colors[color_idx++];
+ d->state.shadowColor = c;
+ break;
+ }
+ case ShadowBlur:
+ {
+ qreal blur = d->reals[real_idx++];
+ d->state.shadowBlur = blur;
+ break;
+ }
+ case ShadowOffsetX:
+ {
+ qreal x = d->reals[real_idx++];
+ d->state.shadowOffsetX = x;
+ break;
+ }
+ case ShadowOffsetY:
+ {
+ qreal y = d->reals[real_idx++];
+ d->state.shadowOffsetY = y;
+ break;
+ }
+ case Fill:
+ {
+ QPainterPath path = d->pathes[path_idx++];
+ if (d->hasShadow())
+ d->fillShadowPath(p,path);
+ else
+ p->fillPath(path, p->brush());
+ break;
+ }
+ case Stroke:
+ {
+ p->setMatrix(d->state.matrix);
+ QPainterPath path = d->state.matrix.inverted().map(d->pathes[path_idx++]);
+ if (d->hasShadow())
+ d->strokeShadowPath(p,path);
+ else
+ p->strokePath(path, p->pen());
+ break;
+ }
+ case Clip:
+ {
+ QPainterPath clipPath = d->pathes[path_idx++];
+ p->setClipPath(clipPath);
+ p->setClipping(true);
+ break;
+ }
+ case UpdateBrush:
+ {
+ p->setBrush(d->brushes[brush_idx++]);
+ break;
+ }
+ case UpdatePen:
+ {
+ p->setPen(d->pens[pen_idx++]);
+ break;
+ }
+ case GlobalAlpha:
+ {
+ p->setOpacity(d->reals[real_idx++]);
+ break;
+ }
+ case GlobalCompositeOperation:
+ {
+ p->setCompositionMode(static_cast<QPainter::CompositionMode>(d->ints[int_idx++]));
+ break;
+ }
+ case Font:
+ {
+ p->setFont(d->fonts[font_idx++]);
+ break;
+ }
+ case StrokeText:
+ {
+ QString text = d->strings[string_idx++];
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ int align = d->ints[int_idx++];
+ int baseline = d->ints[int_idx++];
+
+ QPen oldPen = p->pen();
+ p->setPen(QPen(p->brush(),0));
+ //p->setMatrix(state.matrix, false); // always set?
+
+ QPainterPath textPath;
+ QFont oldFont = p->font();
+ QFont font = p->font();
+ font.setStyleStrategy(QFont::ForceOutline);
+ p->setFont(font);
+ const QFontMetrics &metrics = p->fontMetrics();
+ int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), metrics);
+ int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), metrics, text);
+ textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
+ if (d->hasShadow())
+ d->strokeShadowPath(p,textPath);
+
+ p->strokePath(textPath, QPen(p->brush(), p->pen().widthF()));
+
+ //reset old font
+ p->setFont(oldFont);
+ p->setPen(oldPen);
+ break;
+ }
+ case FillText:
+ {
+ QString text = d->strings[string_idx++];
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ int align = d->ints[int_idx++];
+ int baseline = d->ints[int_idx++];
+
+ QFont oldFont = p->font();
+ QPen oldPen = p->pen();
+ p->setPen(QPen(p->brush(), p->pen().widthF()));
+ //p->setMatrix(state.matrix, false);
+ //QFont font = p->font();
+ QFont font = d->state.font;
+ font.setBold(true);
+
+ p->setFont(font);
+ int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), p->fontMetrics());
+ int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), p->fontMetrics(), text);
+ QTextOption opt; // Adjust baseLine etc
+ if (d->hasShadow()) {
+ const QFontMetrics &metrics = p->fontMetrics();
+ QPainterPath textPath;
+ textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
+ d->fillShadowPath(p,textPath);
+ }
+ //p->drawText(QRectF(x - xoffset, y - yoffset, QWIDGETSIZE_MAX, p->fontMetrics().height()), text, opt);
+ p->setFont(oldFont);
+ p->setPen(oldPen);
+ break;
+ }
+ case DrawImage1:
+ {
+ QUrl url(d->strings[string_idx++]);
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ QDeclarativePixmap px(qmlEngine(d->canvas), url);
+ qDebug() << "draw image:" << url << px.pixmap().size();
+ if (px.isReady()) {
+ QPixmap pixmap = px.pixmap();
+ if (d->hasShadow()) {
+ QImage shadow = d->makeShadowImage(pixmap);
+ qreal dx = x + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+ qreal dy = y + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+ p->drawImage(QPointF(dx, dy), shadow);
+ }
+ p->drawPixmap(QPointF(x, y), pixmap);
+ }
+ break;
+ }
+ case DrawImage2:
+ {
+ qreal dx = d->reals[real_idx++];
+ qreal dy = d->reals[real_idx++];
+ qreal dw = d->reals[real_idx++];
+ qreal dh = d->reals[real_idx++];
+ QUrl url(d->strings[string_idx++]);
+ QDeclarativePixmap px(qmlEngine(d->canvas), url);
+ if (px.isReady()) {
+ QPixmap pixmap = px.pixmap().scaled(dw, dh);
+ if (d->hasShadow()) {
+ QImage shadow = d->makeShadowImage(pixmap);
+ qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+ qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+ p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
+ }
+ p->drawPixmap(QPointF(dx, dy), pixmap);
+ }
+ break;
+ }
+ case DrawImage3:
+ {
+ qreal sx = d->reals[real_idx++];
+ qreal sy = d->reals[real_idx++];
+ qreal sw = d->reals[real_idx++];
+ qreal sh = d->reals[real_idx++];
+ qreal dx = d->reals[real_idx++];
+ qreal dy = d->reals[real_idx++];
+ qreal dw = d->reals[real_idx++];
+ qreal dh = d->reals[real_idx++];
+ QUrl url(d->strings[string_idx++]);
+ QDeclarativePixmap px(qmlEngine(d->canvas), url);
+ if (px.isReady()) {
+ QPixmap pixmap = px.pixmap().copy(sx, sy, sw, sh).scaled(dw, dh);
+ if (d->hasShadow()) {
+ QImage shadow = d->makeShadowImage(pixmap);
+ qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+ qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+ p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
+ }
+ p->drawPixmap(QPointF(dx, dy), pixmap);
+ }
+ break;
+ }
+ case GetImageData:
+ {
+ qreal sx = d->reals[real_idx++];
+ qreal sy = d->reals[real_idx++];
+ qreal sw = d->reals[real_idx++];
+ qreal sh = d->reals[real_idx++];
+ QImage img = toImage().copy(sx, sy, sw, sh);
+ const uchar* data = img.constBits();
+ int i = 0;
+
+ while(i< img.byteCount()) {
+ //the stored order in QImage:BGRA
+ d->imageData << *(data+i+2);//R
+ d->imageData << *(data+i+1);//G
+ d->imageData << *(data+i);//B
+ d->imageData << *(data+i+3);//A
+ i+=4;
+ }
+ break;
+ }
+ case PutImageData:
+ {
+ QImage image = d->images[image_idx++];
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ p->drawImage(QPointF(x, y), image);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ d->clearCommands();
+ }
+}
+
+QPaintDevice* QSGContext2D::paintDevice()
+{
+ Q_D(QSGContext2D);
+ return &d->cachedImage;
+}
+const QImage& QSGContext2D::toImage() const
+{
+ Q_D(const QSGContext2D);
+ return d->cachedImage;
+}
+bool QSGContext2D::requireCachedImage() const
+{
+ Q_D(const QSGContext2D);
+ return d->waitingForPainting;
+}
+void QSGContext2D::setCachedImage(const QImage& image)
+{
+ Q_D(QSGContext2D);
+#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
+ if (d->waitingForPainting) {
+ d->cachedImage = image;
+ d->waitingForPainting = false;
+ }
+#endif
+ if (inWorkerThread()) {
+ d->agent->setCachedImage(image);
+ }
+}
+
+void QSGContext2D::clear()
+{
+ Q_D(QSGContext2D);
+ d->clear();
+}
+
+void QSGContext2D::reset()
+{
+ Q_D(QSGContext2D);
+ d->reset();
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy)
+{
+ Q_D(QSGContext2D);
+ if (!imgUrl.isEmpty())
+ d->drawImage(imgUrl, dx, dy);
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+ Q_D(QSGContext2D);
+ if (!imgUrl.isEmpty())
+ d->drawImage(imgUrl, sx, sy, sw, sh, dx, dy, dw, dh);
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy,
+ qreal dw, qreal dh)
+{
+ Q_D(QSGContext2D);
+ if (!imgUrl.isEmpty())
+ d->drawImage(imgUrl, dx, dy, dw, dh);
+}
+
+void QSGContext2D::setSize(int width, int height)
+{
+ QSize size(width, height);
+ setSize(size);
+}
+
+void QSGContext2D::setSize(const QSize &size)
+{
+ Q_D(QSGContext2D);
+
+ if (d->size == size)
+ return;
+ d->setSize(size);
+ emit changed();
+}
+
+QSize QSGContext2D::size() const
+{
+ Q_D(const QSGContext2D);
+ return d->size;
+}
+
+QMatrix QSGContext2D::worldMatrix() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.matrix;
+}
+
+QT_END_NAMESPACE