76f795b9b88da2a87fffd25328ed50cd0f105beb
[profile/ivi/qtdeclarative.git] / src / quick / items / context2d / qquickcontext2d.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickcontext2d_p.h"
43 #include "qquickcontext2dcommandbuffer_p.h"
44 #include "qquickcanvasitem_p.h"
45 #include <private/qquickcontext2dtexture_p.h>
46 #include <private/qquickitem_p.h>
47 #include <QtQuick/private/qquickshadereffectsource_p.h>
48 #include <QtGui/qopenglframebufferobject.h>
49
50 #include <QtQuick/private/qsgcontext_p.h>
51 #include <private/qquicksvgparser_p.h>
52 #include <private/qquickpath_p.h>
53
54 #include <private/qquickimage_p_p.h>
55
56 #include <QtGui/qguiapplication.h>
57 #include <qqmlinfo.h>
58 #include <QtCore/qmath.h>
59 #include <private/qv8engine_p.h>
60
61 #include <qqmlengine.h>
62 #include <private/qv8domerrors_p.h>
63 #include <QtCore/qnumeric.h>
64 #include <private/qquickcanvas_p.h>
65 #include <private/qquickwindowmanager_p.h>
66 #include <QtGui/private/qguiapplication_p.h>
67 #include <qpa/qplatformintegration.h>
68
69 #ifdef Q_OS_QNX
70 #include <ctype.h>
71 #endif
72
73 QT_BEGIN_NAMESPACE
74 /*!
75     \qmlclass Context2D QQuickContext2D
76     \inqmlmodule QtQuick 2
77     \ingroup qtquick-canvas
78     \since QtQuick 2.0
79     \brief Provides 2D context for shapes on a Canvas item
80
81     The Context2D object can be created by \c Canvas item's \c getContext()
82     method:
83     \code
84     Canvas {
85       id:canvas
86       onPaint:{
87          var ctx = canvas.getContext('2d');
88          //...
89       }
90     }
91     \endcode
92     The Context2D API implements the same \l
93     {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard} with
94     some enhanced features.
95
96     The Context2D API provides the rendering \b{context} which defines the
97     methods and attributes needed to draw on the \c Canvas item. The following
98     assigns the canvas rendering context to a \c{context} variable:
99     \code
100     var context = mycanvas.getContext("2d")
101     \endcode
102
103     The Context2D API renders the canvas as a coordinate system whose origin
104     (0,0) is at the top left corner, as shown in the figure below. Coordinates
105     increase along the \c{x} axis from left to right and along the \c{y} axis
106     from top to bottom of the canvas.
107     \image qml-item-canvas-context.gif
108 */
109
110
111
112 Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
113
114 static const double Q_PI   = 3.14159265358979323846;   // pi
115
116 #define DEGREES(t) ((t) * 180.0 / Q_PI)
117
118 #define CHECK_CONTEXT(r)     if (!r || !r->context || !r->context->bufferValid()) \
119                                 V8THROW_ERROR("Not a Context2D object");
120
121 #define CHECK_CONTEXT_SETTER(r)     if (!r || !r->context || !r->context->bufferValid()) \
122                                        V8THROW_ERROR_SETTER("Not a Context2D object");
123 #define qClamp(val, min, max) qMin(qMax(val, min), max)
124 #define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
125 QColor qt_color_from_string(v8::Local<v8::Value> name)
126 {
127     v8::String::AsciiValue str(name);
128
129     char *p = *str;
130     int len = str.length();
131     //rgb/hsl color string has at least 7 characters
132     if (!p || len > 255 || len <= 7)
133         return QColor(p);
134     else {
135         bool isRgb(false), isHsl(false), hasAlpha(false);
136         Q_UNUSED(isHsl)
137
138         while (isspace(*p)) p++;
139         if (strncmp(p, "rgb", 3) == 0)
140             isRgb = true;
141         else if (strncmp(p, "hsl", 3) == 0)
142             isHsl = true;
143         else
144             return QColor(p);
145
146         p+=3; //skip "rgb" or "hsl"
147         hasAlpha = (*p == 'a') ? true : false;
148
149         ++p; //skip "("
150
151         if (hasAlpha) ++p; //skip "a"
152
153         int rh, gs, bl, alpha = 255;
154
155         //red
156         while (isspace(*p)) p++;
157         rh = strtol(p, &p, 10);
158         if (*p == '%') {
159             rh = qRound(rh/100.0 * 255);
160             ++p;
161         }
162         if (*p++ != ',') return QColor();
163
164         //green
165         while (isspace(*p)) p++;
166         gs = strtol(p, &p, 10);
167         if (*p == '%') {
168             gs = qRound(gs/100.0 * 255);
169             ++p;
170         }
171         if (*p++ != ',') return QColor();
172
173         //blue
174         while (isspace(*p)) p++;
175         bl = strtol(p, &p, 10);
176         if (*p == '%') {
177             bl = qRound(bl/100.0 * 255);
178             ++p;
179         }
180
181         if (hasAlpha) {
182             if (*p++!= ',') return QColor();
183             while (isspace(*p)) p++;
184             bool ok = false;
185             alpha = qRound(qstrtod(p, const_cast<const char **>(&p), &ok) * 255);
186         }
187
188         if (*p != ')') return QColor();
189         if (isRgb)
190             return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
191         else if (isHsl)
192             return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
193     }
194     return QColor();
195 }
196
197 QFont qt_font_from_string(const QString& fontString) {
198     QFont font;
199      // ### this is simplified and incomplete
200     // ### TODO:get code from Qt webkit
201      const QStringList tokens = fontString.split(QLatin1Char(' '));
202      foreach (const QString &token, tokens) {
203          if (token == QLatin1String("italic"))
204              font.setItalic(true);
205          else if (token == QLatin1String("bold"))
206              font.setBold(true);
207          else if (token.endsWith(QLatin1String("px"))) {
208              QString number = token;
209              number.remove(QLatin1String("px"));
210              //font.setPointSizeF(number.trimmed().toFloat());
211              font.setPixelSize(number.trimmed().toInt());
212          } else
213              font.setFamily(token);
214      }
215
216      return font;
217 }
218
219
220
221 class QQuickContext2DEngineData : public QV8Engine::Deletable
222 {
223 public:
224     QQuickContext2DEngineData(QV8Engine *engine);
225     ~QQuickContext2DEngineData();
226
227     v8::Persistent<v8::Function> constructorContext;
228     v8::Persistent<v8::Function> constructorGradient;
229     v8::Persistent<v8::Function> constructorPattern;
230     v8::Persistent<v8::Function> constructorPixelArray;
231     v8::Persistent<v8::Function> constructorImageData;
232 };
233
234 V8_DEFINE_EXTENSION(QQuickContext2DEngineData, engineData)
235
236 class QV8Context2DResource : public QV8ObjectResource
237 {
238     V8_RESOURCE_TYPE(Context2DType)
239 public:
240     QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e), context(0) {}
241     QQuickContext2D* context;
242 };
243
244 class QV8Context2DStyleResource : public QV8ObjectResource
245 {
246     V8_RESOURCE_TYPE(Context2DStyleType)
247 public:
248     QV8Context2DStyleResource(QV8Engine *e)
249       : QV8ObjectResource(e)
250       , patternRepeatX(false)
251       , patternRepeatY(false)
252     {}
253     QBrush brush;
254     bool patternRepeatX:1;
255     bool patternRepeatY:1;
256 };
257
258 class QV8Context2DPixelArrayResource : public QV8ObjectResource
259 {
260     V8_RESOURCE_TYPE(Context2DPixelArrayType)
261 public:
262     QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {}
263
264     QImage image;
265 };
266
267 QImage qt_image_convolute_filter(const QImage& src, const QVector<qreal>& weights, int radius = 0)
268 {
269     int sides = radius ? radius : qRound(qSqrt(weights.size()));
270     int half = qFloor(sides/2);
271
272     QImage dst = QImage(src.size(), src.format());
273     int w = src.width();
274     int h = src.height();
275     for (int y = 0; y < dst.height(); ++y) {
276       QRgb *dr = (QRgb*)dst.scanLine(y);
277       for (int x = 0; x < dst.width(); ++x) {
278           unsigned char* dRgb = ((unsigned char*)&dr[x]);
279           unsigned char red=0, green=0, blue=0, alpha=0;
280           int sy = y;
281           int sx = x;
282
283           for (int cy=0; cy<sides; cy++) {
284              for (int cx=0; cx<sides; cx++) {
285                int scy = sy + cy - half;
286                int scx = sx + cx - half;
287                if (scy >= 0 && scy < w && scx >= 0 && scx < h) {
288                   const QRgb *sr = (const QRgb*)(src.constScanLine(scy));
289                   const unsigned char* sRgb = ((const unsigned char*)&sr[scx]);
290                   qreal wt = radius ? weights[0] : weights[cy*sides+cx];
291                   red += sRgb[0] * wt;
292                   green += sRgb[1] * wt;
293                   blue += sRgb[2] * wt;
294                   alpha += sRgb[3] * wt;
295                }
296              }
297           }
298           dRgb[0] = red;
299           dRgb[1] = green;
300           dRgb[2] = blue;
301           dRgb[3] = alpha;
302       }
303     }
304     return dst;
305 }
306
307 void qt_image_boxblur(QImage& image, int radius, bool quality)
308 {
309     int passes = quality? 3: 1;
310     for (int i=0; i < passes; i++) {
311         image = qt_image_convolute_filter(image, QVector<qreal>() << 1.0/(radius * radius * 1.0), radius);
312     }
313 }
314
315 static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
316 {
317     if (compositeOperator == QLatin1String("source-over")) {
318         return QPainter::CompositionMode_SourceOver;
319     } else if (compositeOperator == QLatin1String("source-out")) {
320         return QPainter::CompositionMode_SourceOut;
321     } else if (compositeOperator == QLatin1String("source-in")) {
322         return QPainter::CompositionMode_SourceIn;
323     } else if (compositeOperator == QLatin1String("source-atop")) {
324         return QPainter::CompositionMode_SourceAtop;
325     } else if (compositeOperator == QLatin1String("destination-atop")) {
326         return QPainter::CompositionMode_DestinationAtop;
327     } else if (compositeOperator == QLatin1String("destination-in")) {
328         return QPainter::CompositionMode_DestinationIn;
329     } else if (compositeOperator == QLatin1String("destination-out")) {
330         return QPainter::CompositionMode_DestinationOut;
331     } else if (compositeOperator == QLatin1String("destination-over")) {
332         return QPainter::CompositionMode_DestinationOver;
333     } else if (compositeOperator == QLatin1String("lighter")) {
334         return QPainter::CompositionMode_Lighten;
335     } else if (compositeOperator == QLatin1String("copy")) {
336         return QPainter::CompositionMode_Source;
337     } else if (compositeOperator == QLatin1String("xor")) {
338         return QPainter::CompositionMode_Xor;
339     } else if (compositeOperator == QLatin1String("qt-clear")) {
340         return QPainter::CompositionMode_Clear;
341     } else if (compositeOperator == QLatin1String("qt-destination")) {
342         return QPainter::CompositionMode_Destination;
343     } else if (compositeOperator == QLatin1String("qt-multiply")) {
344         return QPainter::CompositionMode_Multiply;
345     } else if (compositeOperator == QLatin1String("qt-screen")) {
346         return QPainter::CompositionMode_Screen;
347     } else if (compositeOperator == QLatin1String("qt-overlay")) {
348         return QPainter::CompositionMode_Overlay;
349     } else if (compositeOperator == QLatin1String("qt-darken")) {
350         return QPainter::CompositionMode_Darken;
351     } else if (compositeOperator == QLatin1String("qt-lighten")) {
352         return QPainter::CompositionMode_Lighten;
353     } else if (compositeOperator == QLatin1String("qt-color-dodge")) {
354         return QPainter::CompositionMode_ColorDodge;
355     } else if (compositeOperator == QLatin1String("qt-color-burn")) {
356         return QPainter::CompositionMode_ColorBurn;
357     } else if (compositeOperator == QLatin1String("qt-hard-light")) {
358         return QPainter::CompositionMode_HardLight;
359     } else if (compositeOperator == QLatin1String("qt-soft-light")) {
360         return QPainter::CompositionMode_SoftLight;
361     } else if (compositeOperator == QLatin1String("qt-difference")) {
362         return QPainter::CompositionMode_Difference;
363     } else if (compositeOperator == QLatin1String("qt-exclusion")) {
364         return QPainter::CompositionMode_Exclusion;
365     }
366     return QPainter::CompositionMode_SourceOver;
367 }
368
369 static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
370 {
371     switch (op) {
372     case QPainter::CompositionMode_SourceOver:
373         return QLatin1String("source-over");
374     case QPainter::CompositionMode_DestinationOver:
375         return QLatin1String("destination-over");
376     case QPainter::CompositionMode_Clear:
377         return QLatin1String("qt-clear");
378     case QPainter::CompositionMode_Source:
379         return QLatin1String("copy");
380     case QPainter::CompositionMode_Destination:
381         return QLatin1String("qt-destination");
382     case QPainter::CompositionMode_SourceIn:
383         return QLatin1String("source-in");
384     case QPainter::CompositionMode_DestinationIn:
385         return QLatin1String("destination-in");
386     case QPainter::CompositionMode_SourceOut:
387         return QLatin1String("source-out");
388     case QPainter::CompositionMode_DestinationOut:
389         return QLatin1String("destination-out");
390     case QPainter::CompositionMode_SourceAtop:
391         return QLatin1String("source-atop");
392     case QPainter::CompositionMode_DestinationAtop:
393         return QLatin1String("destination-atop");
394     case QPainter::CompositionMode_Xor:
395         return QLatin1String("xor");
396     case QPainter::CompositionMode_Plus:
397         return QLatin1String("plus");
398     case QPainter::CompositionMode_Multiply:
399         return QLatin1String("qt-multiply");
400     case QPainter::CompositionMode_Screen:
401         return QLatin1String("qt-screen");
402     case QPainter::CompositionMode_Overlay:
403         return QLatin1String("qt-overlay");
404     case QPainter::CompositionMode_Darken:
405         return QLatin1String("qt-darken");
406     case QPainter::CompositionMode_Lighten:
407         return QLatin1String("lighter");
408     case QPainter::CompositionMode_ColorDodge:
409         return QLatin1String("qt-color-dodge");
410     case QPainter::CompositionMode_ColorBurn:
411         return QLatin1String("qt-color-burn");
412     case QPainter::CompositionMode_HardLight:
413         return QLatin1String("qt-hard-light");
414     case QPainter::CompositionMode_SoftLight:
415         return QLatin1String("qt-soft-light");
416     case QPainter::CompositionMode_Difference:
417         return QLatin1String("qt-difference");
418     case QPainter::CompositionMode_Exclusion:
419         return QLatin1String("qt-exclusion");
420     default:
421         break;
422     }
423     return QString();
424 }
425
426
427 static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image)
428 {
429     QQuickContext2DEngineData *ed = engineData(engine);
430     v8::Local<v8::Object> imageData = ed->constructorImageData->NewInstance();
431     QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine);
432     if (image.isNull()) {
433         r->image = QImage(w, h, QImage::Format_ARGB32);
434         r->image.fill(0x00000000);
435     } else {
436         Q_ASSERT(image.width() == w && image.height() == h);
437         r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
438     }
439     v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
440     pixelData->SetExternalResource(r);
441
442     imageData->SetInternalField(0, pixelData);
443     return imageData;
444 }
445
446 //static script functions
447
448 /*!
449     \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas
450      Holds the canvas item that the context paints on.
451
452      This property is read only.
453 */
454 static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::AccessorInfo &info)
455 {
456     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
457     CHECK_CONTEXT(r)
458
459
460     QV8Engine *engine = V8ENGINE_ACCESSOR();
461
462     return engine->newQObject(r->context->canvas());
463 }
464
465 /*!
466     \qmlmethod object QtQuick2::Context2D::restore()
467     Pops the top state on the stack, restoring the context to that state.
468
469     \sa QtQuick2::Context2D::save()
470 */
471 static v8::Handle<v8::Value> ctx2d_restore(const v8::Arguments &args)
472 {
473     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
474     CHECK_CONTEXT(r)
475
476     r->context->popState();
477     return args.This();
478 }
479
480 /*!
481     \qmlmethod object QtQuick2::Context2D::reset()
482     Resets the context state and properties to the default values.
483 */
484 static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args)
485 {
486     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
487     CHECK_CONTEXT(r)
488
489     r->context->reset();
490
491     return args.This();
492 }
493
494 /*!
495     \qmlmethod object QtQuick2::Context2D::save()
496     Pushes the current state onto the state stack.
497
498     Before changing any state attributes, you should save the current state
499     for future reference. The context maintains a stack of drawing states.
500     Each state consists of the current transformation matrix, clipping region,
501     and values of the following attributes:
502     \list
503     \li\a QtQuick2::Context2D::strokeStyle
504     \li\a QtQuick2::Context2D::fillStyle
505     \li\a QtQuick2::Context2D::fillRule
506     \li\a QtQuick2::Context2D::globalAlpha
507     \li\a QtQuick2::Context2D::lineWidth
508     \li\a QtQuick2::Context2D::lineCap
509     \li\a QtQuick2::Context2D::lineJoin
510     \li\a QtQuick2::Context2D::miterLimit
511     \li\a QtQuick2::Context2D::shadowOffsetX
512     \li\a QtQuick2::Context2D::shadowOffsetY
513     \li\a QtQuick2::Context2D::shadowBlur
514     \li\a QtQuick2::Context2D::shadowColor
515     \li\a QtQuick2::Context2D::globalCompositeOperation
516     \li\a QtQuick2::Context2D::font
517     \li\a QtQuick2::Context2D::textAlign
518     \li\a QtQuick2::Context2D::textBaseline
519     \endlist
520
521     The current path is NOT part of the drawing state. The path can be reset by
522     invoking the \a QtQuick2::Context2D::beginPath() method.
523 */
524 static v8::Handle<v8::Value> ctx2d_save(const v8::Arguments &args)
525 {
526     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
527     CHECK_CONTEXT(r)
528
529     r->context->pushState();
530
531     return args.This();
532 }
533
534 // transformations
535 /*!
536     \qmlmethod object QtQuick2::Context2D::rotate(real angle)
537     Rotate the canvas around the current origin by \c angle in radians and clockwise direction.
538     \code
539     ctx.rotate(Math.PI/2);
540     \endcode
541     \image qml-item-canvas-rotate.png
542
543     The rotation transformation matrix is as follows:
544
545     \image qml-item-canvas-math-rotate.png
546
547     where the \c angle of rotation is in radians.
548
549 */
550 static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args)
551 {
552     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
553     CHECK_CONTEXT(r)
554
555     if (args.Length() == 1)
556         r->context->rotate(args[0]->NumberValue());
557     return args.This();
558 }
559
560 /*!
561     \qmlmethod object QtQuick2::Context2D::scale(real x, real y)
562     Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors
563     to the current tranform matrix.
564     Where \c x is the scale factor in the horizontal direction and \c y is the scale factor in the
565     vertical direction.
566     The following code doubles the horizontal size of an object drawn on the canvas and half its
567     vertical size:
568     \code
569     ctx.scale(2.0, 0.5);
570     \endcode
571     \image qml-item-canvas-scale.png
572
573 */
574 static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args)
575 {
576     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
577     CHECK_CONTEXT(r)
578
579
580     if (args.Length() == 2)
581         r->context->scale(args[0]->NumberValue(), args[1]->NumberValue());
582     return args.This();
583 }
584
585 /*!
586     \qmlmethod object QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f)
587     Changes the transformation matrix to the matrix given by the arguments as described below.
588
589     Modifying the transformation matrix directly enables you to perform scaling,
590     rotating, and translating transformations in a single step.
591
592     Each point on the canvas is multiplied by the matrix before anything is
593     drawn. The \l{HTML5 Canvas API} defines the transformation matrix as:
594
595     \image qml-item-canvas-math.png
596     where:
597     \list
598     \li \c{a} is the scale factor in the horizontal (x) direction
599     \image qml-item-canvas-scalex.png
600     \li \c{c} is the skew factor in the x direction
601     \image qml-item-canvas-canvas-skewx.png
602     \li \c{e} is the translation in the x direction
603     \image qml-item-canvas-canvas-translate.png
604     \li \c{b} is the skew factor in the y (vertical) direction
605     \image qml-item-canvas-canvas-skewy.png
606     \li \c{d} is the scale factor in the y direction
607     \image qml-item-canvas-canvas-scaley.png
608     \li \c{f} is the translation in the y direction
609     \image qml-item-canvas-canvas-translatey.png
610     \li the last row remains constant
611     \endlist
612     The scale factors and skew factors are multiples; \c{e} and \c{f} are
613     coordinate space units, just like the units in the \a QtQuick2::Context2D::translate(x,y)
614     method.
615
616     \sa QtQuick2::Context2D::transform()
617 */
618 static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
619 {
620     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
621     CHECK_CONTEXT(r)
622
623
624     if (args.Length() == 6)
625         r->context->setTransform( args[0]->NumberValue()
626                                                         , args[1]->NumberValue()
627                                                         , args[2]->NumberValue()
628                                                         , args[3]->NumberValue()
629                                                         , args[4]->NumberValue()
630                                                         , args[5]->NumberValue());
631
632     return args.This();
633 }
634
635 /*!
636     \qmlmethod object QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f)
637     This method is very similar to \a QtQuick2::Context2D::setTransform(), but instead of replacing the old
638     tranform matrix, this method applies the given tranform matrix to the current matrix by mulitplying to it.
639
640     The \a setTransform(a, b, c, d, e, f) method actually resets the current transform to the identity matrix,
641     and then invokes the transform(a, b, c, d, e, f) method with the same arguments.
642
643     \sa QtQuick2::Context2D::setTransform()
644 */
645 static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
646 {
647     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
648     CHECK_CONTEXT(r)
649
650
651     if (args.Length() == 6)
652         r->context->transform( args[0]->NumberValue()
653                                                   , args[1]->NumberValue()
654                                                   , args[2]->NumberValue()
655                                                   , args[3]->NumberValue()
656                                                   , args[4]->NumberValue()
657                                                   , args[5]->NumberValue());
658
659     return args.This();
660 }
661
662 /*!
663     \qmlmethod object QtQuick2::Context2D::translate(real x, real y)
664     Translates the origin of the canvas to point (\c x, \c y).
665
666     \c x is the horizontal distance that the origin is translated, in coordinate space units,
667     \c y is the vertical distance that the origin is translated, in coordinate space units.
668     Translating the origin enables you to draw patterns of different objects on the canvas
669     without having to measure the coordinates manually for each shape.
670 */
671 static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
672 {
673     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
674     CHECK_CONTEXT(r)
675
676
677     if (args.Length() == 2)
678             r->context->translate(args[0]->NumberValue(), args[1]->NumberValue());
679     return args.This();
680 }
681
682
683 /*!
684     \qmlmethod object QtQuick2::Context2D::resetTransform()
685     Reset the transformation matrix to default value.
686
687     \sa QtQuick2::Context2D::transform(), QtQuick2::Context2D::setTransform(), QtQuick2::Context2D::reset()
688 */
689 static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
690 {
691     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
692     CHECK_CONTEXT(r)
693
694     r->context->setTransform(1, 0, 0, 1, 0, 0);
695
696     return args.This();
697 }
698
699
700 /*!
701     \qmlmethod object QtQuick2::Context2D::shear(real sh, real sv )
702     Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction.
703 */
704 static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args)
705 {
706     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
707     CHECK_CONTEXT(r)
708
709     if (args.Length() == 2)
710             r->context->shear(args[0]->NumberValue(), args[1]->NumberValue());
711
712     return args.This();
713 }
714 // compositing
715
716 /*!
717     \qmlproperty real QtQuick2::Context2D::globalAlpha
718      Holds the the current alpha value applied to rendering operations.
719      The value must be in the range from 0.0 (fully transparent) to 1.0 (fully opque).
720      The default value is 1.0.
721 */
722 static v8::Handle<v8::Value> ctx2d_globalAlpha(v8::Local<v8::String>, const v8::AccessorInfo &info)
723 {
724     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
725     CHECK_CONTEXT(r)
726
727     return v8::Number::New(r->context->state.globalAlpha);
728 }
729
730 static void ctx2d_globalAlpha_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
731 {
732     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
733     CHECK_CONTEXT_SETTER(r)
734
735     qreal globalAlpha = value->NumberValue();
736
737     if (!qIsFinite(globalAlpha))
738         return;
739
740     if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) {
741         r->context->state.globalAlpha = globalAlpha;
742         r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha);
743     }
744 }
745
746 /*!
747     \qmlproperty string QtQuick2::Context2D::globalCompositeOperation
748      Holds the the current the current composition operation, from the list below:
749      \list
750      \li source-atop      - A atop B. Display the source image wherever both images are opaque.
751                            Display the destination image wherever the destination image is opaque but the source image is transparent.
752                            Display transparency elsewhere.
753      \li source-in        - A in B. Display the source image wherever both the source image and destination image are opaque.
754                            Display transparency elsewhere.
755      \li source-out       - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
756                            Display transparency elsewhere.
757      \li source-over      - (default) A over B. Display the source image wherever the source image is opaque.
758                            Display the destination image elsewhere.
759      \li destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
760      \li destination-in   - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
761      \li destination-out  - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
762      \li destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
763      \li lighter          - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
764      \li copy             - A (B is ignored). Display the source image instead of the destination image.
765      \li xor              - A xor B. Exclusive OR of the source image and destination image.
766      \endlist
767
768      Additionally, this property also accepts the compositon modes listed in \a {QPainter::CompositionMode}. According to the W3C standard, these
769      extension composition modes are provided as "vendorName-operationName" syntax, for example: \c {QPainter::CompositionMode_Exclusion} is porvided as
770      "qt-exclusion".
771 */
772 static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
773 {
774     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
775     CHECK_CONTEXT(r)
776
777
778     QV8Engine *engine = V8ENGINE_ACCESSOR();
779
780     return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation));
781 }
782
783 static void ctx2d_globalCompositeOperation_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
784 {
785     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
786     CHECK_CONTEXT_SETTER(r)
787
788     QV8Engine *engine = V8ENGINE_ACCESSOR();
789
790
791     QString mode = engine->toString(value);
792     QPainter::CompositionMode cm = qt_composite_mode_from_string(mode);
793     if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over"))
794         return;
795
796     if (cm != r->context->state.globalCompositeOperation) {
797         r->context->state.globalCompositeOperation = cm;
798         r->context->buffer()->setGlobalCompositeOperation(cm);
799     }
800 }
801
802 // colors and styles
803 /*!
804     \qmlproperty variant QtQuick2::Context2D::fillStyle
805      Holds the current style used for filling shapes.
806      The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored.
807      This property accepts several color syntaxes:
808      \list
809      \li 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)'
810      \li 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)'
811      \li 'hsl(hue, saturation, lightness)'
812      \li 'hsla(hue, saturation, lightness, alpha)'
813      \li '#RRGGBB' - for example: '#00FFCC'
814      \li Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0)
815      \endlist
816      If the \a fillStyle or \a strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the
817      best performance, because it's already a valid QColor value, does not need to be parsed everytime.
818
819      The default value is  '#000000'.
820      \sa QtQuick2::Context2D::createLinearGradient
821      \sa QtQuick2::Context2D::createRadialGradient
822      \sa QtQuick2::Context2D::createPattern
823      \sa QtQuick2::Context2D::strokeStyle
824  */
825 static v8::Handle<v8::Value> ctx2d_fillStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
826 {
827     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
828     CHECK_CONTEXT(r)
829
830     QV8Engine *engine = V8ENGINE_ACCESSOR();
831
832     QColor color = r->context->state.fillStyle.color();
833     if (color.isValid()) {
834         if (color.alpha() == 255)
835             return engine->toString(color.name());
836         QString alphaString = QString::number(color.alphaF(), 'f');
837         while (alphaString.endsWith(QLatin1Char('0')))
838             alphaString.chop(1);
839         if (alphaString.endsWith(QLatin1Char('.')))
840             alphaString += QLatin1Char('0');
841         return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
842     }
843     return r->context->m_fillStyle;
844 }
845
846 static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
847 {
848     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
849     CHECK_CONTEXT_SETTER(r)
850
851     QV8Engine *engine = V8ENGINE_ACCESSOR();
852
853    if (value->IsObject()) {
854        QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
855        if (color.isValid()) {
856            r->context->state.fillStyle = color;
857            r->context->buffer()->setFillStyle(color);
858            r->context->m_fillStyle = value;
859        } else {
860            QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
861            if (style && style->brush != r->context->state.fillStyle) {
862                r->context->state.fillStyle = style->brush;
863                r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
864                r->context->m_fillStyle = value;
865                r->context->state.fillPatternRepeatX = style->patternRepeatX;
866                r->context->state.fillPatternRepeatY = style->patternRepeatY;
867            }
868        }
869    } else if (value->IsString()) {
870        QColor color = qt_color_from_string(value);
871        if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
872             r->context->state.fillStyle = QBrush(color);
873             r->context->buffer()->setFillStyle(r->context->state.fillStyle);
874             r->context->m_fillStyle = value;
875        }
876    }
877 }
878 /*!
879     \qmlproperty enumeration QtQuick2::Context2D::fillRule
880      Holds the current fill rule used for filling shapes. The following fill rules supported:
881      \list
882      \li Qt.OddEvenFill
883      \li Qt.WindingFill
884      \endlist
885      Note: Unlike the \a QPainterPath, the Canvas API uses the winding fill as the default fill rule.
886      The fillRule property is part of the context rendering state.
887
888      \sa QtQuick2::Context2D::fillStyle
889  */
890 static v8::Handle<v8::Value> ctx2d_fillRule(v8::Local<v8::String>, const v8::AccessorInfo &info)
891 {
892     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
893     CHECK_CONTEXT(r)
894     QV8Engine *engine = V8ENGINE_ACCESSOR();
895
896     return engine->fromVariant(r->context->state.fillRule);
897 }
898
899 static void ctx2d_fillRule_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
900 {
901     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
902     CHECK_CONTEXT_SETTER(r)
903
904     QV8Engine *engine = V8ENGINE_ACCESSOR();
905
906     if ((value->IsString() && engine->toString(value) == QStringLiteral("WindingFill"))
907       ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) {
908         r->context->state.fillRule = Qt::WindingFill;
909     } else if ((value->IsString() && engine->toString(value) == QStringLiteral("OddEvenFill"))
910                ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) {
911         r->context->state.fillRule = Qt::OddEvenFill;
912     } else {
913         //error
914     }
915     r->context->m_path.setFillRule(r->context->state.fillRule);
916 }
917 /*!
918     \qmlproperty variant QtQuick2::Context2D::strokeStyle
919      Holds the current color or style to use for the lines around shapes,
920      The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object.
921      Invalid values are ignored.
922
923      The default value is  '#000000'.
924
925      \sa QtQuick2::Context2D::createLinearGradient
926      \sa QtQuick2::Context2D::createRadialGradient
927      \sa QtQuick2::Context2D::createPattern
928      \sa QtQuick2::Context2D::fillStyle
929  */
930 v8::Handle<v8::Value> ctx2d_strokeStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
931 {
932     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
933     CHECK_CONTEXT(r)
934
935     QV8Engine *engine = V8ENGINE_ACCESSOR();
936
937     QColor color = r->context->state.strokeStyle.color();
938     if (color.isValid()) {
939         if (color.alpha() == 255)
940             return engine->toString(color.name());
941         QString alphaString = QString::number(color.alphaF(), 'f');
942         while (alphaString.endsWith(QLatin1Char('0')))
943             alphaString.chop(1);
944         if (alphaString.endsWith(QLatin1Char('.')))
945             alphaString += QLatin1Char('0');
946         return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
947     }
948     return r->context->m_strokeStyle;
949 }
950
951 static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
952 {
953     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
954     CHECK_CONTEXT_SETTER(r)
955
956     QV8Engine *engine = V8ENGINE_ACCESSOR();
957
958     if (value->IsObject()) {
959         QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
960         if (color.isValid()) {
961             r->context->state.fillStyle = color;
962             r->context->buffer()->setStrokeStyle(color);
963             r->context->m_strokeStyle = value;
964         } else {
965             QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
966             if (style && style->brush != r->context->state.strokeStyle) {
967                 r->context->state.strokeStyle = style->brush;
968                 r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
969                 r->context->m_strokeStyle = value;
970                 r->context->state.strokePatternRepeatX = style->patternRepeatX;
971                 r->context->state.strokePatternRepeatY = style->patternRepeatY;
972
973             }
974         }
975     } else if (value->IsString()) {
976         QColor color = qt_color_from_string(value);
977         if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
978              r->context->state.strokeStyle = QBrush(color);
979              r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
980              r->context->m_strokeStyle = value;
981         }
982     }
983 }
984
985 /*!
986   \qmlmethod object QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
987    Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between
988    the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
989
990    A gradient is a smooth transition between colors. There are two types of gradients: linear and radial.
991    Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between
992    to the gradient's starting and end points or circles.
993
994     \sa QtQuick2::Context2D::CanvasGradient::addColorStop
995     \sa QtQuick2::Context2D::createRadialGradient
996     \sa QtQuick2::Context2D::ctx2d_createConicalGradient
997     \sa QtQuick2::Context2D::createPattern
998     \sa QtQuick2::Context2D::fillStyle
999     \sa QtQuick2::Context2D::strokeStyle
1000   */
1001
1002 static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &args)
1003 {
1004     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1005     CHECK_CONTEXT(r)
1006
1007
1008     QV8Engine *engine = V8ENGINE();
1009
1010     if (args.Length() == 4) {
1011         QQuickContext2DEngineData *ed = engineData(engine);
1012         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1013         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1014         qreal x0 = args[0]->NumberValue();
1015         qreal y0 = args[1]->NumberValue();
1016         qreal x1 = args[2]->NumberValue();
1017         qreal y1 = args[3]->NumberValue();
1018
1019         if (!qIsFinite(x0)
1020          || !qIsFinite(y0)
1021          || !qIsFinite(x1)
1022          || !qIsFinite(y1)) {
1023             delete r;
1024             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
1025         }
1026
1027         r->brush = QLinearGradient(x0, y0, x1, y1);
1028         gradient->SetExternalResource(r);
1029         return gradient;
1030     }
1031
1032     return args.This();
1033 }
1034
1035 /*!
1036   \qmlmethod object QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
1037    Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with
1038    origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1.
1039
1040     \sa QtQuick2::Context2D::CanvasGradient::addColorStop
1041     \sa QtQuick2::Context2D::createLinearGradient
1042     \sa QtQuick2::Context2D::ctx2d_createConicalGradient
1043     \sa QtQuick2::Context2D::createPattern
1044     \sa QtQuick2::Context2D::fillStyle
1045     \sa QtQuick2::Context2D::strokeStyle
1046   */
1047
1048 static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &args)
1049 {
1050     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1051     CHECK_CONTEXT(r)
1052
1053
1054     QV8Engine *engine = V8ENGINE();
1055
1056     if (args.Length() == 6) {
1057         QQuickContext2DEngineData *ed = engineData(engine);
1058         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1059         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1060
1061         qreal x0 = args[0]->NumberValue();
1062         qreal y0 = args[1]->NumberValue();
1063         qreal r0 = args[2]->NumberValue();
1064         qreal x1 = args[3]->NumberValue();
1065         qreal y1 = args[4]->NumberValue();
1066         qreal r1 = args[5]->NumberValue();
1067
1068         if (!qIsFinite(x0)
1069          || !qIsFinite(y0)
1070          || !qIsFinite(x1)
1071          || !qIsFinite(r0)
1072          || !qIsFinite(r1)
1073          || !qIsFinite(y1)) {
1074             delete r;
1075             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
1076         }
1077
1078         if (r0 < 0 || r1 < 0)
1079             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
1080
1081
1082         r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
1083         gradient->SetExternalResource(r);
1084         return gradient;
1085     }
1086
1087     return args.This();
1088 }
1089
1090 /*!
1091   \qmlmethod object QtQuick2::Context2D::createConicalGradient(real x, real y, real angle)
1092    Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y)
1093    with start angle \c angle in units of radians.
1094
1095     \sa QtQuick2::Context2D::CanvasGradient::addColorStop
1096     \sa QtQuick2::Context2D::createLinearGradient
1097     \sa QtQuick2::Context2D::ctx2d_createRadialGradient
1098     \sa QtQuick2::Context2D::createPattern
1099     \sa QtQuick2::Context2D::fillStyle
1100     \sa QtQuick2::Context2D::strokeStyle
1101   */
1102
1103 static v8::Handle<v8::Value> ctx2d_createConicalGradient(const v8::Arguments &args)
1104 {
1105     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1106     CHECK_CONTEXT(r)
1107
1108
1109     QV8Engine *engine = V8ENGINE();
1110
1111     if (args.Length() == 6) {
1112         QQuickContext2DEngineData *ed = engineData(engine);
1113         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1114         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1115
1116         qreal x = args[0]->NumberValue();
1117         qreal y = args[1]->NumberValue();
1118         qreal angle = DEGREES(args[2]->NumberValue());
1119         if (!qIsFinite(x) || !qIsFinite(y)) {
1120             delete r;
1121             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
1122         }
1123
1124         if (!qIsFinite(angle)) {
1125             delete r;
1126             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
1127         }
1128
1129         r->brush = QConicalGradient(x, y, angle);
1130         gradient->SetExternalResource(r);
1131         return gradient;
1132     }
1133
1134     return args.This();
1135 }
1136 /*!
1137   \qmlmethod variant createPattern(Color color, enumeration patternMode)
1138   This is a overload function.
1139   Returns a CanvasPattern object that uses the given \c color and \c patternMode.
1140   The valid pattern modes are:
1141     \list
1142     \li Qt.SolidPattern
1143     \li Qt.Dense1Pattern
1144     \li Qt.Dense2Pattern
1145     \li Qt.Dense3Pattern
1146     \li Qt.Dense4Pattern
1147     \li Qt.Dense5Pattern
1148     \li Qt.Dense6Pattern
1149     \li Qt.Dense7Pattern
1150     \li Qt.HorPattern
1151     \li Qt.VerPattern
1152     \li Qt.CrossPattern
1153     \li Qt.BDiagPattern
1154     \li Qt.FDiagPattern
1155     \li Qt.DiagCrossPattern
1156 \endlist
1157     \sa Qt::BrushStyle
1158  */
1159 /*!
1160   \qmlmethod variant createPattern(Image image, string repetition)
1161   Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
1162
1163   The \a image parameter must be a valid Image item, a valid \a QtQuick2::CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception.
1164
1165   The allowed values for \a repetition are:
1166
1167   \list
1168   \li "repeat"    - both directions
1169   \li "repeat-x   - horizontal only
1170   \li "repeat-y"  - vertical only
1171   \li "no-repeat" - neither
1172   \endlist
1173
1174   If the repetition argument is empty or null, the value "repeat" is used.
1175
1176   \sa QtQuick2::Context2D::strokeStyle
1177   \sa QtQuick2::Context2D::fillStyle
1178   */
1179 static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
1180 {
1181     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1182     CHECK_CONTEXT(r)
1183
1184
1185     QV8Engine *engine = V8ENGINE();
1186
1187     if (args.Length() == 2) {
1188         QQuickContext2DEngineData *ed = engineData(engine);
1189         QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine);
1190
1191         QColor color = engine->toVariant(args[0], qMetaTypeId<QColor>()).value<QColor>();
1192         if (color.isValid()) {
1193             int patternMode = args[1]->IntegerValue();
1194             Qt::BrushStyle style = Qt::SolidPattern;
1195             if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
1196                 style = static_cast<Qt::BrushStyle>(patternMode);
1197             }
1198             styleResouce->brush = QBrush(color, style);
1199         } else {
1200             QImage patternTexture;
1201
1202             if (args[0]->IsObject()) {
1203                 QV8Context2DPixelArrayResource *pixelData = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject());
1204                 if (pixelData) {
1205                     patternTexture = pixelData->image;
1206                 }
1207             } else {
1208                 patternTexture = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
1209             }
1210
1211             if (!patternTexture.isNull()) {
1212                 styleResouce->brush.setTextureImage(patternTexture);
1213
1214                 QString repetition = engine->toString(args[1]);
1215                 if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) {
1216                     styleResouce->patternRepeatX = true;
1217                     styleResouce->patternRepeatY = true;
1218                 } else if (repetition == QStringLiteral("repeat-x")) {
1219                     styleResouce->patternRepeatX = true;
1220                 } else if (repetition == QStringLiteral("repeat-y")) {
1221                     styleResouce->patternRepeatY = true;
1222                 } else if (repetition == QStringLiteral("no-repeat")) {
1223                     styleResouce->patternRepeatY = false;
1224                     styleResouce->patternRepeatY = false;
1225                 } else {
1226                     //TODO: exception: SYNTAX_ERR
1227                 }
1228
1229             }
1230         }
1231
1232         v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
1233         pattern->SetExternalResource(styleResouce);
1234         return pattern;
1235
1236     }
1237     return v8::Undefined();
1238 }
1239
1240 // line styles
1241 /*!
1242     \qmlproperty string QtQuick2::Context2D::lineCap
1243      Holds the the current line cap style.
1244      The possible line cap styles are:
1245     \list
1246     \li butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value.
1247     \li round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line.
1248     \li square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line.
1249     \endlist
1250     Other values are ignored.
1251 */
1252 v8::Handle<v8::Value> ctx2d_lineCap(v8::Local<v8::String>, const v8::AccessorInfo &info)
1253 {
1254     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1255     CHECK_CONTEXT(r)
1256
1257
1258     QV8Engine *engine = V8ENGINE_ACCESSOR();
1259     switch (r->context->state.lineCap) {
1260     case Qt::RoundCap:
1261         return engine->toString(QLatin1String("round"));
1262     case Qt::FlatCap:
1263         return engine->toString(QLatin1String("butt"));
1264     case Qt::SquareCap:
1265         return engine->toString(QLatin1String("square"));
1266     default:
1267         break;
1268     }
1269     return engine->toString(QLatin1String("butt"));;
1270 }
1271
1272 static void ctx2d_lineCap_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1273 {
1274     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1275     CHECK_CONTEXT_SETTER(r)
1276
1277     QV8Engine *engine = V8ENGINE_ACCESSOR();
1278
1279     QString lineCap = engine->toString(value);
1280     Qt::PenCapStyle cap;
1281     if (lineCap == QLatin1String("round"))
1282         cap = Qt::RoundCap;
1283     else if (lineCap == QLatin1String("butt"))
1284         cap = Qt::FlatCap;
1285     else if (lineCap == QLatin1String("square"))
1286         cap = Qt::SquareCap;
1287     else
1288         return;
1289
1290     if (cap != r->context->state.lineCap) {
1291         r->context->state.lineCap = cap;
1292         r->context->buffer()->setLineCap(cap);
1293     }
1294 }
1295
1296 /*!
1297     \qmlproperty string QtQuick2::Context2D::lineJoin
1298      Holds the the current line join style. A join exists at any point in a subpath
1299      shared by two consecutive lines. When a subpath is closed, then a join also exists
1300      at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
1301
1302     The possible line join styles are:
1303     \list
1304     \li bevel - this is all that is rendered at joins.
1305     \li round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins.
1306     \li miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style.
1307     \endlist
1308     Other values are ignored.
1309 */
1310 v8::Handle<v8::Value> ctx2d_lineJoin(v8::Local<v8::String>, const v8::AccessorInfo &info)
1311 {
1312     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1313     CHECK_CONTEXT(r)
1314
1315
1316     QV8Engine *engine = V8ENGINE_ACCESSOR();
1317     switch (r->context->state.lineJoin) {
1318     case Qt::RoundJoin:
1319         return engine->toString(QLatin1String("round"));
1320     case Qt::BevelJoin:
1321         return engine->toString(QLatin1String("bevel"));
1322     case Qt::MiterJoin:
1323         return engine->toString(QLatin1String("miter"));
1324     default:
1325         break;
1326     }
1327     return engine->toString(QLatin1String("miter"));
1328 }
1329
1330 static void ctx2d_lineJoin_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1331 {
1332     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1333     CHECK_CONTEXT_SETTER(r)
1334
1335     QV8Engine *engine = V8ENGINE_ACCESSOR();
1336
1337     QString lineJoin = engine->toString(value);
1338     Qt::PenJoinStyle join;
1339     if (lineJoin == QLatin1String("round"))
1340         join = Qt::RoundJoin;
1341     else if (lineJoin == QLatin1String("bevel"))
1342         join = Qt::BevelJoin;
1343     else if (lineJoin == QLatin1String("miter"))
1344         join = Qt::SvgMiterJoin;
1345     else
1346         return;
1347
1348     if (join != r->context->state.lineJoin) {
1349         r->context->state.lineJoin = join;
1350         r->context->buffer()->setLineJoin(join);
1351     }
1352 }
1353
1354 /*!
1355     \qmlproperty real QtQuick2::Context2D::lineWidth
1356      Holds the the current line width. Values that are not finite values greater than zero are ignored.
1357  */
1358 v8::Handle<v8::Value> ctx2d_lineWidth(v8::Local<v8::String>, const v8::AccessorInfo &info)
1359 {
1360     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1361     CHECK_CONTEXT(r)
1362
1363
1364     return v8::Number::New(r->context->state.lineWidth);
1365 }
1366
1367 static void ctx2d_lineWidth_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1368 {
1369     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1370     CHECK_CONTEXT_SETTER(r)
1371
1372     qreal w = value->NumberValue();
1373
1374     if (w > 0 && qIsFinite(w) && w != r->context->state.lineWidth) {
1375         r->context->state.lineWidth = w;
1376         r->context->buffer()->setLineWidth(w);
1377     }
1378 }
1379
1380 /*!
1381     \qmlproperty real QtQuick2::Context2D::miterLimit
1382      Holds the current miter limit ratio.
1383      The default miter limit value is 10.0.
1384  */
1385 v8::Handle<v8::Value> ctx2d_miterLimit(v8::Local<v8::String>, const v8::AccessorInfo &info)
1386 {
1387     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1388     CHECK_CONTEXT(r)
1389
1390
1391     return v8::Number::New(r->context->state.miterLimit);
1392 }
1393
1394 static void ctx2d_miterLimit_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1395 {
1396     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1397     CHECK_CONTEXT_SETTER(r)
1398
1399     qreal ml = value->NumberValue();
1400
1401     if (ml > 0 && qIsFinite(ml) && ml != r->context->state.miterLimit) {
1402         r->context->state.miterLimit = ml;
1403         r->context->buffer()->setMiterLimit(ml);
1404     }
1405 }
1406
1407 // shadows
1408 /*!
1409     \qmlproperty real QtQuick2::Context2D::shadowBlur
1410      Holds the current level of blur applied to shadows
1411  */
1412 v8::Handle<v8::Value> ctx2d_shadowBlur(v8::Local<v8::String>, const v8::AccessorInfo &info)
1413 {
1414     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1415     CHECK_CONTEXT(r)
1416
1417
1418     return v8::Number::New(r->context->state.shadowBlur);
1419 }
1420
1421 static void ctx2d_shadowBlur_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1422 {
1423     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1424     CHECK_CONTEXT_SETTER(r)
1425     qreal blur = value->NumberValue();
1426
1427     if (blur > 0 && qIsFinite(blur) && blur != r->context->state.shadowBlur) {
1428         r->context->state.shadowBlur = blur;
1429         r->context->buffer()->setShadowBlur(blur);
1430     }
1431 }
1432
1433 /*!
1434     \qmlproperty string QtQuick2::Context2D::shadowColor
1435      Holds the current shadow color.
1436  */
1437 v8::Handle<v8::Value> ctx2d_shadowColor(v8::Local<v8::String>, const v8::AccessorInfo &info)
1438 {
1439     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1440     CHECK_CONTEXT(r)
1441
1442
1443     QV8Engine *engine = V8ENGINE_ACCESSOR();
1444
1445     return engine->toString(r->context->state.shadowColor.name());
1446 }
1447
1448 static void ctx2d_shadowColor_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1449 {
1450     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1451     CHECK_CONTEXT_SETTER(r)
1452
1453     QColor color = qt_color_from_string(value);
1454
1455     if (color.isValid() && color != r->context->state.shadowColor) {
1456         r->context->state.shadowColor = color;
1457         r->context->buffer()->setShadowColor(color);
1458     }
1459 }
1460
1461
1462 /*!
1463     \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX
1464      Holds the current shadow offset in the positive horizontal distance.
1465
1466      \sa QtQuick2::Context2D::shadowOffsetY
1467  */
1468 v8::Handle<v8::Value> ctx2d_shadowOffsetX(v8::Local<v8::String>, const v8::AccessorInfo &info)
1469 {
1470     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1471     CHECK_CONTEXT(r)
1472
1473
1474     return v8::Number::New(r->context->state.shadowOffsetX);
1475 }
1476
1477 static void ctx2d_shadowOffsetX_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1478 {
1479     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1480     CHECK_CONTEXT_SETTER(r)
1481
1482     qreal offsetX = value->NumberValue();
1483     if (qIsFinite(offsetX) && offsetX != r->context->state.shadowOffsetX) {
1484         r->context->state.shadowOffsetX = offsetX;
1485         r->context->buffer()->setShadowOffsetX(offsetX);
1486     }
1487 }
1488 /*!
1489     \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY
1490      Holds the current shadow offset in the positive vertical distance.
1491
1492      \sa QtQuick2::Context2D::shadowOffsetX
1493  */
1494 v8::Handle<v8::Value> ctx2d_shadowOffsetY(v8::Local<v8::String>, const v8::AccessorInfo &info)
1495 {
1496     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1497     CHECK_CONTEXT(r)
1498
1499
1500     return v8::Number::New(r->context->state.shadowOffsetY);
1501 }
1502
1503 static void ctx2d_shadowOffsetY_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1504 {
1505     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1506     CHECK_CONTEXT_SETTER(r)
1507
1508     qreal offsetY = value->NumberValue();
1509     if (qIsFinite(offsetY) && offsetY != r->context->state.shadowOffsetY) {
1510         r->context->state.shadowOffsetY = offsetY;
1511         r->context->buffer()->setShadowOffsetY(offsetY);
1512     }
1513 }
1514
1515 v8::Handle<v8::Value> ctx2d_path(v8::Local<v8::String>, const v8::AccessorInfo &info)
1516 {
1517     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1518     CHECK_CONTEXT(r)
1519     return r->context->m_v8path;
1520 }
1521
1522 static void ctx2d_path_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1523 {
1524     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1525     CHECK_CONTEXT_SETTER(r)
1526     QV8Engine *engine = V8ENGINE_ACCESSOR();
1527
1528     r->context->beginPath();
1529     if (value->IsObject()) {
1530         QQuickPath* path = qobject_cast<QQuickPath*>(engine->toQObject(value));
1531         if (path)
1532             r->context->m_path = path->path();
1533     } else {
1534         QString path = engine->toString(value->ToString());
1535         QQuickSvgParser::parsePathDataFast(path, r->context->m_path);
1536     }
1537     r->context->m_v8path = value;
1538 }
1539
1540 //rects
1541 /*!
1542   \qmlmethod object QtQuick2::Context2D::clearRect(real x, real y, real w, real h)
1543   Clears all pixels on the canvas in the given rectangle to transparent black.
1544   */
1545 static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
1546 {
1547     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1548     CHECK_CONTEXT(r)
1549
1550
1551     if (args.Length() == 4)
1552         r->context->clearRect(args[0]->NumberValue(),
1553                               args[1]->NumberValue(),
1554                               args[2]->NumberValue(),
1555                               args[3]->NumberValue());
1556
1557     return args.This();
1558 }
1559 /*!
1560   \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1561    Paint the specified rectangular area using the fillStyle.
1562
1563    \sa QtQuick2::Context2D::fillStyle
1564   */
1565 static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
1566 {
1567     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1568     CHECK_CONTEXT(r)
1569
1570     if (args.Length() == 4)
1571         r->context->fillRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1572     return args.This();
1573 }
1574
1575 /*!
1576   \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1577    Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin,
1578    and (if appropriate) miterLimit attributes.
1579
1580    \sa QtQuick2::Context2D::strokeStyle
1581    \sa QtQuick2::Context2D::lineWidth
1582    \sa QtQuick2::Context2D::lineJoin
1583    \sa QtQuick2::Context2D::miterLimit
1584   */
1585 static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
1586 {
1587     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1588     CHECK_CONTEXT(r)
1589
1590     if (args.Length() == 4)
1591         r->context->strokeRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1592
1593     return args.This();
1594 }
1595
1596 // Complex shapes (paths) API
1597 /*!
1598   \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
1599   Adds an arc to the current subpath that lies on the circumference of the circle whose center is at the point (\c x,\cy) and whose radius is \c radius.
1600   \image qml-item-canvas-arcTo2.png
1601   \sa QtQuick2::Context2D::arcTo,
1602       {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc}
1603   */
1604 static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
1605 {
1606     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1607     CHECK_CONTEXT(r)
1608
1609     if (args.Length() >= 5) {
1610         bool antiClockwise = false;
1611
1612         if (args.Length() == 6)
1613             antiClockwise = args[5]->BooleanValue();
1614
1615         qreal radius = args[2]->NumberValue();
1616
1617         if (qIsFinite(radius) && radius < 0)
1618            V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1619
1620         r->context->arc(args[0]->NumberValue(),
1621                         args[1]->NumberValue(),
1622                         radius,
1623                         args[3]->NumberValue(),
1624                         args[4]->NumberValue(),
1625                         antiClockwise);
1626     }
1627
1628     return args.This();
1629 }
1630
1631 /*!
1632   \qmlmethod object QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius)
1633
1634    Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line.
1635    To draw an arc, you begin with the same steps your followed to create a line:
1636     \list
1637     \li Call the context.beginPath() method to set a new path.
1638     \li Call the context.moveTo(\c x, \c y) method to set your starting position on the canvas at the point (\c x,\c y).
1639     \li To draw an arc or circle, call the context.arcTo(\c x1, \c y1, \c x2, \c y2,\c radius) method.
1640        This adds an arc with starting point (\c x1,\c y1), ending point (\c x2, \c y2), and radius \c radius to the current subpath and connects
1641        it to the previous subpath by a straight line.
1642     \endlist
1643     \image qml-item-canvas-arcTo.png
1644     Both startAngle and endAngle are measured from the x axis in units of radians.
1645
1646     \image qml-item-canvas-startAngle.png
1647     The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction.
1648   \sa QtQuick2::Context2D::arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d
1649       context standard for arcTo}
1650   */
1651 static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
1652 {
1653     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1654     CHECK_CONTEXT(r)
1655
1656     if (args.Length() == 5) {
1657         qreal radius = args[4]->NumberValue();
1658
1659         if (qIsFinite(radius) && radius < 0)
1660            V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1661
1662         r->context->arcTo(args[0]->NumberValue(),
1663                           args[1]->NumberValue(),
1664                           args[2]->NumberValue(),
1665                           args[3]->NumberValue(),
1666                           radius);
1667     }
1668
1669     return args.This();
1670 }
1671
1672 /*!
1673   \qmlmethod object QtQuick2::Context2D::beginPath()
1674
1675    Resets the current path to a new path.
1676   */
1677 static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
1678 {
1679     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1680     CHECK_CONTEXT(r)
1681
1682
1683     r->context->beginPath();
1684
1685     return args.This();
1686 }
1687
1688 /*!
1689   \qmlmethod object QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
1690
1691   Adds a cubic Bezier curve between the current position and the given endPoint using the control points specified by (\c cp1x, cp1y),
1692   and (\c cp2x, \c cp2y).
1693   After the curve is added, the current position is updated to be at the end point (\c x, \c y) of the curve.
1694   The following code produces the path shown below:
1695   \code
1696   ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
1697   ctx.lineWidth = 1;
1698   ctx.beginPath();
1699   ctx.moveTo(20, 0);//start point
1700   ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
1701   ctx.stroke();
1702   \endcode
1703    \image qml-item-canvas-bezierCurveTo.png
1704   \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
1705   \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
1706   */
1707 static v8::Handle<v8::Value> ctx2d_bezierCurveTo(const v8::Arguments &args)
1708 {
1709     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1710     CHECK_CONTEXT(r)
1711
1712
1713     if (args.Length() == 6) {
1714         qreal cp1x = args[0]->NumberValue();
1715         qreal cp1y = args[1]->NumberValue();
1716         qreal cp2x = args[2]->NumberValue();
1717         qreal cp2y = args[3]->NumberValue();
1718         qreal x = args[4]->NumberValue();
1719         qreal y = args[5]->NumberValue();
1720
1721         if (!qIsFinite(cp1x) || !qIsFinite(cp1y) || !qIsFinite(cp2x) || !qIsFinite(cp2y) || !qIsFinite(x) || !qIsFinite(y))
1722             return args.This();
1723
1724         r->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
1725     }
1726
1727     return args.This();
1728 }
1729
1730 /*!
1731   \qmlmethod object QtQuick2::Context2D::clip()
1732
1733    Creates the clipping region from the current path.
1734    Any parts of the shape outside the clipping path are not displayed.
1735    To create a complex shape using the \a clip() method:
1736
1737     \list 1
1738     \li Call the \c{context.beginPath()} method to set the clipping path.
1739     \li Define the clipping path by calling any combination of the \c{lineTo},
1740     \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
1741     \li Call the \c{context.clip()} method.
1742     \endlist
1743
1744     The new shape displays.  The following shows how a clipping path can
1745     modify how an image displays:
1746
1747     \image qml-canvas-clip-complex.png
1748     \sa QtQuick2::Context2D::beginPath()
1749     \sa QtQuick2::Context2D::closePath()
1750     \sa QtQuick2::Context2D::stroke()
1751     \sa QtQuick2::Context2D::fill()
1752    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
1753   */
1754 static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
1755 {
1756     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1757     CHECK_CONTEXT(r)
1758
1759     r->context->clip();
1760     return args.This();
1761 }
1762
1763 /*!
1764   \qmlmethod object QtQuick2::Context2D::closePath()
1765    Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
1766    The current point of the new path is the previous subpath's first point.
1767
1768    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
1769   */
1770 static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
1771 {
1772     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1773     CHECK_CONTEXT(r)
1774
1775
1776     r->context->closePath();
1777
1778     return args.This();
1779 }
1780
1781 /*!
1782   \qmlmethod object QtQuick2::Context2D::fill()
1783
1784    Fills the subpaths with the current fill style.
1785
1786    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
1787
1788    \sa QtQuick2::Context2D::fillStyle
1789   */
1790 static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args)
1791 {
1792     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1793     CHECK_CONTEXT(r);
1794     r->context->fill();
1795     return args.This();
1796 }
1797
1798 /*!
1799   \qmlmethod object QtQuick2::Context2D::lineTo(real x, real y)
1800
1801    Draws a line from the current position to the point (x, y).
1802  */
1803 static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
1804 {
1805     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1806     CHECK_CONTEXT(r)
1807
1808
1809     if (args.Length() == 2) {
1810         qreal x = args[0]->NumberValue();
1811         qreal y = args[1]->NumberValue();
1812
1813         if (!qIsFinite(x) || !qIsFinite(y))
1814             return args.This();
1815
1816         r->context->lineTo(x, y);
1817     }
1818
1819     return args.This();
1820 }
1821
1822 /*!
1823   \qmlmethod object QtQuick2::Context2D::moveTo(real x, real y)
1824
1825    Creates a new subpath with the given point.
1826  */
1827 static v8::Handle<v8::Value> ctx2d_moveTo(const v8::Arguments &args)
1828 {
1829     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1830     CHECK_CONTEXT(r)
1831
1832     if (args.Length() == 2) {
1833         qreal x = args[0]->NumberValue();
1834         qreal y = args[1]->NumberValue();
1835
1836         if (!qIsFinite(x) || !qIsFinite(y))
1837             return args.This();
1838         r->context->moveTo(x, y);
1839     }
1840     return args.This();
1841 }
1842
1843 /*!
1844   \qmlmethod object QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
1845
1846    Adds a quadratic Bezier curve between the current point and the endpoint (\c x, \c y) with the control point specified by (\c cpx, \c cpy).
1847
1848    See {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for  for quadraticCurveTo}
1849  */
1850 static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
1851 {
1852     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1853     CHECK_CONTEXT(r)
1854
1855     if (args.Length() == 4) {
1856         qreal cpx = args[0]->NumberValue();
1857         qreal cpy = args[1]->NumberValue();
1858         qreal x = args[2]->NumberValue();
1859         qreal y = args[3]->NumberValue();
1860
1861         if (!qIsFinite(cpx) || !qIsFinite(cpy) || !qIsFinite(x) || !qIsFinite(y))
1862             return args.This();
1863
1864         r->context->quadraticCurveTo(cpx, cpy, x, y);
1865     }
1866
1867     return args.This();
1868 }
1869
1870 /*!
1871   \qmlmethod object QtQuick2::Context2D::rect(real x, real y, real w, real h)
1872
1873    Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath.
1874  */
1875 static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
1876 {
1877     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1878     CHECK_CONTEXT(r)
1879
1880     if (args.Length() == 4)
1881         r->context->rect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1882     return args.This();
1883 }
1884
1885 /*!
1886   \qmlmethod object QtQuick2::Context2D::roundedRect(real x, real y, real w, real h,  real xRadius, real yRadius)
1887
1888    Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the
1889    ellipses defining the corners of the rounded rectangle.
1890  */
1891 static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args)
1892 {
1893     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1894     CHECK_CONTEXT(r)
1895
1896     if (args.Length() == 6)
1897         r->context->roundedRect(args[0]->NumberValue()
1898                               , args[1]->NumberValue()
1899                               , args[2]->NumberValue()
1900                               , args[3]->NumberValue()
1901                               , args[4]->NumberValue()
1902                               , args[5]->NumberValue());
1903     return args.This();
1904 }
1905
1906 /*!
1907   \qmlmethod object QtQuick2::Context2D::ellipse(real x, real y, real w, real h)
1908
1909   Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h,
1910   and adds it to the path as a closed subpath.
1911
1912   The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position).
1913  */
1914 static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args)
1915 {
1916     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1917     CHECK_CONTEXT(r)
1918
1919
1920     if (args.Length() == 4)
1921         r->context->ellipse(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1922
1923     return args.This();
1924 }
1925
1926 /*!
1927   \qmlmethod object QtQuick2::Context2D::text(string text, real x, real y)
1928
1929   Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied.
1930   The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y).
1931  */
1932 static v8::Handle<v8::Value> ctx2d_text(const v8::Arguments &args)
1933 {
1934     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1935     CHECK_CONTEXT(r)
1936
1937     QV8Engine *engine = V8ENGINE();
1938     if (args.Length() == 3) {
1939         qreal x = args[1]->NumberValue();
1940         qreal y = args[2]->NumberValue();
1941
1942         if (!qIsFinite(x) || !qIsFinite(y))
1943             return args.This();
1944         r->context->text(engine->toString(args[0]), x, y);
1945     }
1946     return args.This();
1947 }
1948
1949 /*!
1950   \qmlmethod object QtQuick2::Context2D::stroke()
1951
1952    Strokes the subpaths with the current stroke style.
1953
1954    See {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
1955
1956    \sa QtQuick2::Context2D::strokeStyle
1957   */
1958 static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
1959 {
1960     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1961     CHECK_CONTEXT(r)
1962
1963     r->context->stroke();
1964     return args.This();
1965 }
1966
1967 /*!
1968   \qmlmethod object QtQuick2::Context2D::isPointInPath(real x, real y)
1969
1970    Returns true if the given point is in the current path.
1971
1972    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
1973   */
1974 static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
1975 {
1976     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1977     CHECK_CONTEXT(r)
1978
1979     bool pointInPath = false;
1980     if (args.Length() == 2)
1981         pointInPath = r->context->isPointInPath(args[0]->NumberValue(), args[1]->NumberValue());
1982     return v8::Boolean::New(pointInPath);
1983 }
1984
1985 static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
1986 {
1987     Q_UNUSED(args);
1988
1989     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
1990 }
1991
1992 static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
1993 {
1994     Q_UNUSED(args);
1995
1996     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
1997 }
1998
1999 static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
2000 {
2001     Q_UNUSED(args);
2002
2003     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
2004 }
2005 // text
2006 /*!
2007   \qmlproperty string QtQuick2::Context2D::font
2008   Holds the current font settings.
2009
2010   The default font value is "10px sans-serif".
2011   See {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
2012   */
2013 v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &info)
2014 {
2015     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2016     CHECK_CONTEXT(r)
2017
2018     QV8Engine *engine = V8ENGINE_ACCESSOR();
2019
2020     return engine->toString(r->context->state.font.toString());
2021 }
2022
2023 static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2024 {
2025     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2026     CHECK_CONTEXT_SETTER(r)
2027
2028     QV8Engine *engine = V8ENGINE_ACCESSOR();
2029     QString fs = engine->toString(value);
2030     QFont font = qt_font_from_string(fs);
2031     if (font != r->context->state.font) {
2032         r->context->state.font = font;
2033     }
2034 }
2035
2036 /*!
2037   \qmlproperty string QtQuick2::Context2D::textAlign
2038
2039   Holds the current text alignment settings.
2040   The possible values are:
2041   \list
2042     \li start
2043     \li end
2044     \li left
2045     \li right
2046     \li center
2047   \endlist
2048   Other values are ignored. The default value is "start".
2049   */
2050 v8::Handle<v8::Value> ctx2d_textAlign(v8::Local<v8::String>, const v8::AccessorInfo &info)
2051 {
2052     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2053     CHECK_CONTEXT(r)
2054     QV8Engine *engine = V8ENGINE_ACCESSOR();
2055     switch (r->context->state.textAlign) {
2056     case QQuickContext2D::Start:
2057         return engine->toString(QLatin1String("start"));
2058    case QQuickContext2D::End:
2059         return engine->toString(QLatin1String("end"));
2060    case QQuickContext2D::Left:
2061         return engine->toString(QLatin1String("left"));
2062    case QQuickContext2D::Right:
2063         return engine->toString(QLatin1String("right"));
2064    case QQuickContext2D::Center:
2065         return engine->toString(QLatin1String("center"));
2066     default:
2067         break;
2068     }
2069     return engine->toString(QLatin1String("start"));
2070 }
2071
2072 static void ctx2d_textAlign_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2073 {
2074     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2075     CHECK_CONTEXT_SETTER(r)
2076     QV8Engine *engine = V8ENGINE_ACCESSOR();
2077
2078     QString textAlign = engine->toString(value);
2079
2080     QQuickContext2D::TextAlignType ta;
2081     if (textAlign == QLatin1String("start"))
2082         ta = QQuickContext2D::Start;
2083     else if (textAlign == QLatin1String("end"))
2084         ta = QQuickContext2D::End;
2085     else if (textAlign == QLatin1String("left"))
2086         ta = QQuickContext2D::Left;
2087     else if (textAlign == QLatin1String("right"))
2088         ta = QQuickContext2D::Right;
2089     else if (textAlign == QLatin1String("center"))
2090         ta = QQuickContext2D::Center;
2091     else
2092         return;
2093
2094     if (ta != r->context->state.textAlign) {
2095         r->context->state.textAlign = ta;
2096     }
2097 }
2098
2099 /*!
2100   \qmlproperty string QtQuick2::Context2D::textBaseline
2101
2102   Holds the current baseline alignment settings.
2103   The possible values are:
2104   \list
2105     \li top
2106     \li hanging
2107     \li middle
2108     \li alphabetic
2109     \li ideographic
2110     \li bottom
2111   \endlist
2112   Other values are ignored. The default value is "alphabetic".
2113   */
2114 v8::Handle<v8::Value> ctx2d_textBaseline(v8::Local<v8::String>, const v8::AccessorInfo &info)
2115 {
2116     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2117     CHECK_CONTEXT(r)
2118
2119     QV8Engine *engine = V8ENGINE_ACCESSOR();
2120     switch (r->context->state.textBaseline) {
2121     case QQuickContext2D::Alphabetic:
2122         return engine->toString(QLatin1String("alphabetic"));
2123     case QQuickContext2D::Hanging:
2124         return engine->toString(QLatin1String("hanging"));
2125     case QQuickContext2D::Top:
2126         return engine->toString(QLatin1String("top"));
2127     case QQuickContext2D::Bottom:
2128         return engine->toString(QLatin1String("bottom"));
2129     case QQuickContext2D::Middle:
2130         return engine->toString(QLatin1String("middle"));
2131     default:
2132         break;
2133     }
2134     return engine->toString(QLatin1String("alphabetic"));
2135 }
2136
2137 static void ctx2d_textBaseline_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2138 {
2139     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2140     CHECK_CONTEXT_SETTER(r)
2141     QV8Engine *engine = V8ENGINE_ACCESSOR();
2142     QString textBaseline = engine->toString(value);
2143
2144     QQuickContext2D::TextBaseLineType tb;
2145     if (textBaseline == QLatin1String("alphabetic"))
2146         tb = QQuickContext2D::Alphabetic;
2147     else if (textBaseline == QLatin1String("hanging"))
2148         tb = QQuickContext2D::Hanging;
2149     else if (textBaseline == QLatin1String("top"))
2150         tb = QQuickContext2D::Top;
2151     else if (textBaseline == QLatin1String("bottom"))
2152         tb = QQuickContext2D::Bottom;
2153     else if (textBaseline == QLatin1String("middle"))
2154         tb = QQuickContext2D::Middle;
2155     else
2156         return;
2157
2158     if (tb != r->context->state.textBaseline) {
2159         r->context->state.textBaseline = tb;
2160     }
2161 }
2162
2163 /*!
2164   \qmlmethod object QtQuick2::Context2D::fillText(text, x, y)
2165   Fills the given text at the given position.
2166   \sa QtQuick2::Context2D::font
2167   \sa QtQuick2::Context2D::textAlign
2168   \sa QtQuick2::Context2D::textBaseline
2169   \sa QtQuick2::Context2D::strokeText
2170   */
2171 static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
2172 {
2173     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2174     CHECK_CONTEXT(r)
2175
2176     QV8Engine *engine = V8ENGINE();
2177     if (args.Length() == 3) {
2178         qreal x = args[1]->NumberValue();
2179         qreal y = args[2]->NumberValue();
2180         if (!qIsFinite(x) || !qIsFinite(y))
2181             return args.This();
2182         QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0]));
2183         r->context->buffer()->fill(textPath);
2184     }
2185     return args.This();
2186 }
2187 /*!
2188   \qmlmethod object QtQuick2::Context2D::strokeText(text, x, y)
2189   Strokes the given text at the given position.
2190   \sa QtQuick2::Context2D::font
2191   \sa QtQuick2::Context2D::textAlign
2192   \sa QtQuick2::Context2D::textBaseline
2193   \sa QtQuick2::Context2D::fillText
2194   */
2195 static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
2196 {
2197     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2198     CHECK_CONTEXT(r)
2199
2200     QV8Engine *engine = V8ENGINE();
2201     if (args.Length() == 3)
2202         r->context->drawText(engine->toString(args[0]), args[1]->NumberValue(), args[2]->NumberValue(), false);
2203     return args.This();
2204 }
2205 /*!
2206   \qmlclass TextMetrics
2207     \inqmlmodule QtQuick 2
2208     \since QtQuick 2.0
2209     \ingroup qtquick-canvas
2210     \brief Provides a Context2D TextMetrics interface
2211
2212     The TextMetrics object can be created by QtQuick2::Context2D::measureText method.
2213     See {http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details.
2214
2215     \sa QtQuick2::Context2D::measureText
2216     \sa QtQuick2::TextMetrics::width
2217   */
2218
2219 /*!
2220   \qmlproperty int QtQuick2::TextMetrics::width
2221   Holds the advance width of the text that was passed to the QtQuick2::Context2D::measureText() method.
2222   This property is read only.
2223   */
2224
2225 /*!
2226   \qmlmethod variant QtQuick2::Context2D::measureText(text)
2227   Returns a TextMetrics object with the metrics of the given text in the current font.
2228   */
2229 static v8::Handle<v8::Value> ctx2d_measureText(const v8::Arguments &args)
2230 {
2231     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2232     CHECK_CONTEXT(r)
2233
2234     QV8Engine *engine = V8ENGINE();
2235
2236     if (args.Length() == 1) {
2237         QFontMetrics fm(r->context->state.font);
2238         uint width = fm.width(engine->toString(args[0]));
2239         v8::Local<v8::Object> tm = v8::Object::New();
2240         tm->Set(v8::String::New("width"), v8::Number::New(width));
2241         return tm;
2242     }
2243     return v8::Undefined();
2244 }
2245
2246 // drawing images
2247 /*!
2248   \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy)
2249   Draws the given \a image on the canvas at position (\a dx, \a dy).
2250   Note:
2251   The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object.
2252   When given as Image item, if the image isn't fully loaded, this method draws nothing.
2253   When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
2254   This image been drawing is subject to the current context clip path, even the given \c image is a  {QtQuick2::CanvasImageData} object.
2255
2256   \sa QtQuick2::CanvasImageData
2257   \sa QtQuick2::Image
2258   \sa QtQuick2::Canvas::loadImage
2259   \sa QtQuick2::Canvas::isImageLoaded
2260   \sa QtQuick2::Canvas::imageLoaded
2261
2262   \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
2263   */
2264 /*!
2265   \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
2266   This is an overloaded function.
2267   Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
2268   height \a dh.
2269
2270   Note:
2271   The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object.
2272   When given as Image item, if the image isn't fully loaded, this method draws nothing.
2273   When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
2274   This image been drawing is subject to the current context clip path, even the given \c image is a  {QtQuick2::CanvasImageData} object.
2275
2276   \sa QtQuick2::CanvasImageData
2277   \sa QtQuick2::Image
2278   \sa QtQuick2::Canvas::loadImage
2279   \sa QtQuick2::Canvas::isImageLoaded
2280   \sa QtQuick2::Canvas::imageLoaded
2281
2282   \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
2283   */
2284 /*!
2285   \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh)
2286   This is an overloaded function.
2287   Draws the given item as \a image from source point (\a sx, \a sy) and source width \a sw, source height \a sh
2288   onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
2289
2290
2291   Note:
2292   The \a image type can be an Image or Canvas item, an image url or a \a {QtQuick2::CanvasImageData} object.
2293   When given as Image item, if the image isn't fully loaded, this method draws nothing.
2294   When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
2295   This image been drawing is subject to the current context clip path, even the given \c image is a  {QtQuick2::CanvasImageData} object.
2296
2297   \sa QtQuick2::CanvasImageData
2298   \sa QtQuick2::Image
2299   \sa QtQuick2::Canvas::loadImage
2300   \sa QtQuick2::Canvas::isImageLoaded
2301   \sa QtQuick2::Canvas::imageLoaded
2302
2303   \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
2304 */
2305 static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
2306 {
2307     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2308     CHECK_CONTEXT(r)
2309
2310     QV8Engine *engine = V8ENGINE();
2311     qreal sx, sy, sw, sh, dx, dy, dw, dh;
2312
2313     if (!args.Length())
2314         return args.This();
2315
2316     //FIXME:This function should be moved to QQuickContext2D::drawImage(...)
2317     if (!r->context->state.invertibleCTM)
2318         return args.This();
2319
2320     QQmlRefPointer<QQuickCanvasPixmap> pixmap;
2321
2322     if (args[0]->IsString()) {
2323         QUrl url(engine->toString(args[0]->ToString()));
2324         if (!url.isValid())
2325             V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
2326
2327         pixmap = r->context->createPixmap(url);
2328     } else if (args[0]->IsObject()) {
2329         QQuickImage *imageItem = qobject_cast<QQuickImage*>(engine->toQObject(args[0]->ToObject()));
2330         QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(engine->toQObject(args[0]->ToObject()));
2331
2332         QV8Context2DPixelArrayResource *pix = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->GetInternalField(0)->ToObject());
2333         if (pix && !pix->image.isNull()) {
2334             pixmap.take(new QQuickCanvasPixmap(pix->image, r->context->canvas()->canvas()));
2335         } else if (imageItem) {
2336             pixmap.take(r->context->createPixmap(imageItem->source()));
2337         } else if (canvas) {
2338             QImage img = canvas->toImage();
2339             if (!img.isNull())
2340                 pixmap.take(new QQuickCanvasPixmap(img, canvas->canvas()));
2341         } else {
2342             V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
2343         }
2344     } else {
2345         V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
2346     }
2347
2348     if (pixmap.isNull() || !pixmap->isValid())
2349         return args.This();
2350
2351     if (args.Length() == 3) {
2352         dx = args[1]->NumberValue();
2353         dy = args[2]->NumberValue();
2354         sx = 0;
2355         sy = 0;
2356         sw = pixmap->width();
2357         sh = pixmap->height();
2358         dw = sw;
2359         dh = sh;
2360     } else if (args.Length() == 5) {
2361         sx = 0;
2362         sy = 0;
2363         sw = pixmap->width();
2364         sh = pixmap->height();
2365         dx = args[1]->NumberValue();
2366         dy = args[2]->NumberValue();
2367         dw = args[3]->NumberValue();
2368         dh = args[4]->NumberValue();
2369     } else if (args.Length() == 9) {
2370         sx = args[1]->NumberValue();
2371         sy = args[2]->NumberValue();
2372         sw = args[3]->NumberValue();
2373         sh = args[4]->NumberValue();
2374         dx = args[5]->NumberValue();
2375         dy = args[6]->NumberValue();
2376         dw = args[7]->NumberValue();
2377         dh = args[8]->NumberValue();
2378     } else {
2379         return args.This();
2380     }
2381
2382     if (!qIsFinite(sx)
2383      || !qIsFinite(sy)
2384      || !qIsFinite(sw)
2385      || !qIsFinite(sh)
2386      || !qIsFinite(dx)
2387      || !qIsFinite(dy)
2388      || !qIsFinite(dw)
2389      || !qIsFinite(dh))
2390         return args.This();
2391
2392     if (sx < 0
2393     || sy < 0
2394     || sw == 0
2395     || sh == 0
2396     || sx + sw > pixmap->width()
2397     || sy + sh > pixmap->height()
2398     || sx + sw < 0 || sy + sh < 0) {
2399             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error");
2400     }
2401
2402     r->context->buffer()->drawPixmap(pixmap, QRectF(sx, sy, sw, sh), QRectF(dx, dy, dw, dh));
2403
2404     return args.This();
2405 }
2406
2407 // pixel manipulation
2408 /*!
2409     \qmlclass CanvasImageData
2410     \inqmlmodule QtQuick 2
2411     \ingroup qtquick-canvas
2412     \brief Contains image pixel data in RGBA order
2413
2414      The \a QtQuick2::CanvasImageData object holds the image pixel data.
2415
2416      The \a QtQuick2::CanvasImageData object has the actual dimensions of the data stored in
2417      this object and holds the one-dimensional array containing the data in RGBA order,
2418      as integers in the range 0 to 255.
2419
2420      \sa QtQuick2::CanvasImageData::width
2421      \sa QtQuick2::CanvasImageData::height
2422      \sa QtQuick2::CanvasImageData::data
2423      \sa QtQuick2::Context2D::createImageData
2424      \sa QtQuick2::Context2D::getImageData
2425      \sa QtQuick2::Context2D::putImageData
2426   */
2427 /*!
2428   \qmlproperty QtQuick2::CanvasImageData::width
2429   Holds the actual width dimension of the data in the ImageData object, in device pixels.
2430  */
2431 v8::Handle<v8::Value> ctx2d_imageData_width(v8::Local<v8::String>, const v8::AccessorInfo &args)
2432 {
2433     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2434     if (!r)
2435         return v8::Integer::New(0);
2436     return v8::Integer::New(r->image.width());
2437 }
2438
2439 /*!
2440   \qmlproperty QtQuick2::CanvasImageData::height
2441   Holds the actual height dimension of the data in the ImageData object, in device pixels.
2442   */
2443 v8::Handle<v8::Value> ctx2d_imageData_height(v8::Local<v8::String>, const v8::AccessorInfo &args)
2444 {
2445     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2446     if (!r)
2447         return v8::Integer::New(0);
2448
2449     return v8::Integer::New(r->image.height());
2450 }
2451
2452 /*!
2453   \qmlproperty QtQuick2::CanvasImageData::data
2454   Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
2455  */
2456 v8::Handle<v8::Value> ctx2d_imageData_data(v8::Local<v8::String>, const v8::AccessorInfo &args)
2457 {
2458     return args.This()->GetInternalField(0);
2459 }
2460
2461 /*!
2462     \qmlclass CanvasPixelArray
2463     \inqmlmodule QtQuick 2
2464     \ingroup qtquick-canvas
2465     \brief Provides ordered and indexed access to the components of each pixel in image data
2466
2467   The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
2468   The CanvasPixelArray can be accessed as normal Javascript array.
2469     \sa QtQuick2::CanvasImageData
2470     \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
2471   */
2472
2473 /*!
2474   \qmlproperty QtQuick2::CanvasPixelArray::length
2475   The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
2476   The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
2477   This property is read only.
2478 */
2479 v8::Handle<v8::Value> ctx2d_pixelArray_length(v8::Local<v8::String>, const v8::AccessorInfo &args)
2480 {
2481     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
2482     if (!r || r->image.isNull()) return v8::Undefined();
2483
2484     return v8::Integer::New(r->image.width() * r->image.height() * 4);
2485 }
2486
2487 v8::Handle<v8::Value> ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args)
2488 {
2489     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
2490
2491     if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4)) {
2492         const quint32 w = r->image.width();
2493         const quint32 row = (index / 4) / w;
2494         const quint32 col = (index / 4) % w;
2495         const QRgb* pixel = reinterpret_cast<const QRgb*>(r->image.constScanLine(row));
2496         pixel += col;
2497         switch (index % 4) {
2498         case 0:
2499             return v8::Integer::New(qRed(*pixel));
2500         case 1:
2501             return v8::Integer::New(qGreen(*pixel));
2502         case 2:
2503             return v8::Integer::New(qBlue(*pixel));
2504         case 3:
2505             return v8::Integer::New(qAlpha(*pixel));
2506         }
2507     }
2508     return v8::Undefined();
2509 }
2510
2511 v8::Handle<v8::Value> ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
2512 {
2513     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(info.This());
2514
2515     const int v = value->Uint32Value();
2516     if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4) && v > 0 && v <= 255) {
2517         const quint32 w = r->image.width();
2518         const quint32 row = (index / 4) / w;
2519         const quint32 col = (index / 4) % w;
2520
2521         QRgb* pixel = reinterpret_cast<QRgb*>(r->image.scanLine(row));
2522         pixel += col;
2523         switch (index % 4) {
2524         case 0:
2525             *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel));
2526             break;
2527         case 1:
2528             *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel));
2529             break;
2530         case 2:
2531             *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel));
2532             break;
2533         case 3:
2534             *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v);
2535             break;
2536         }
2537     }
2538     return v8::Undefined();
2539 }
2540 /*!
2541   \qmlmethod QtQuick2::CanvasImageData createImageData(real sw, real sh)
2542    Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
2543   */
2544 /*!
2545   \qmlmethod QtQuick2::CanvasImageData createImageData(QtQuick2::CanvasImageData imageData)
2546    Creates a CanvasImageData object with the same dimensions as the argument.
2547   */
2548 /*!
2549   \qmlmethod QtQuick2::CanvasImageData createImageData(Url imageUrl)
2550    Creates a CanvasImageData object with the given image loaded from \a imageUrl.
2551    Note:The \a imageUrl must be already loaded before this function call, if not, an empty
2552    CanvasImageData obect will be returned.
2553
2554    \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::isImageLoaded
2555   */
2556 static v8::Handle<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
2557 {
2558     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2559     CHECK_CONTEXT(r)
2560
2561     QV8Engine *engine = V8ENGINE();
2562
2563     if (args.Length() == 1) {
2564         if (args[0]->IsObject()) {
2565             v8::Local<v8::Object> imgData = args[0]->ToObject();
2566             QV8Context2DPixelArrayResource *pa = v8_resource_cast<QV8Context2DPixelArrayResource>(imgData->GetInternalField(0)->ToObject());
2567             if (pa) {
2568                 qreal w = imgData->Get(v8::String::New("width"))->NumberValue();
2569                 qreal h = imgData->Get(v8::String::New("height"))->NumberValue();
2570                 return qt_create_image_data(w, h, engine, QImage());
2571             }
2572         } else if (args[0]->IsString()) {
2573             QImage image = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
2574             return qt_create_image_data(image.width(), image.height(), engine, image);
2575         }
2576     } else if (args.Length() == 2) {
2577         qreal w = args[0]->NumberValue();
2578         qreal h = args[1]->NumberValue();
2579
2580         if (!qIsFinite(w) || !qIsFinite(h))
2581             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments");
2582
2583         if (w > 0 && h > 0)
2584             return qt_create_image_data(w, h, engine, QImage());
2585         else
2586             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments");
2587     }
2588     return v8::Undefined();
2589 }
2590
2591 /*!
2592   \qmlmethod QtQuick2::CanvasImageData getImageData(real sx, real sy, real sw, real sh)
2593   Returns an CanvasImageData object containing the image data for the given rectangle of the canvas.
2594   */
2595 static v8::Handle<v8::Value> ctx2d_getImageData(const v8::Arguments &args)
2596 {
2597     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2598     CHECK_CONTEXT(r)
2599
2600     QV8Engine *engine = V8ENGINE();
2601     if (args.Length() == 4) {
2602         qreal x = args[0]->NumberValue();
2603         qreal y = args[1]->NumberValue();
2604         qreal w = args[2]->NumberValue();
2605         qreal h = args[3]->NumberValue();
2606         if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(w))
2607             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments");
2608
2609         if (w <= 0 || h <= 0)
2610             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
2611
2612         QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h));
2613         v8::Local<v8::Object> imageData = qt_create_image_data(w, h, engine, image);
2614
2615         return imageData;
2616     }
2617     return v8::Null();
2618 }
2619
2620 /*!
2621   \qmlmethod object QtQuick2::Context2D::putImageData(QtQuick2::CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
2622   Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted.
2623   */
2624 static v8::Handle<v8::Value> ctx2d_putImageData(const v8::Arguments &args)
2625 {
2626     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2627     CHECK_CONTEXT(r)
2628     if (args.Length() != 3 && args.Length() != 7)
2629         return v8::Undefined();
2630
2631     if (args[0]->IsNull() || !args[0]->IsObject()) {
2632         V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch");
2633     }
2634     qreal dx = args[1]->NumberValue();
2635     qreal dy = args[2]->NumberValue();
2636     qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
2637
2638     if (!qIsFinite(dx) || !qIsFinite(dy))
2639         V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
2640
2641     v8::Local<v8::Object> imageData = args[0]->ToObject();
2642     QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast<QV8Context2DPixelArrayResource>(imageData->Get(v8::String::New("data"))->ToObject());
2643     if (pixelArray) {
2644         w = imageData->Get(v8::String::New("width"))->NumberValue();
2645         h = imageData->Get(v8::String::New("height"))->NumberValue();
2646
2647         if (args.Length() == 7) {
2648             dirtyX = args[3]->NumberValue();
2649             dirtyY = args[4]->NumberValue();
2650             dirtyWidth = args[5]->NumberValue();
2651             dirtyHeight = args[6]->NumberValue();
2652
2653             if (!qIsFinite(dirtyX) || !qIsFinite(dirtyY) || !qIsFinite(dirtyWidth) || !qIsFinite(dirtyHeight))
2654                 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
2655
2656
2657             if (dirtyWidth < 0) {
2658                 dirtyX = dirtyX+dirtyWidth;
2659                 dirtyWidth = -dirtyWidth;
2660             }
2661
2662             if (dirtyHeight < 0) {
2663                 dirtyY = dirtyY+dirtyHeight;
2664                 dirtyHeight = -dirtyHeight;
2665             }
2666
2667             if (dirtyX < 0) {
2668                 dirtyWidth = dirtyWidth+dirtyX;
2669                 dirtyX = 0;
2670             }
2671
2672             if (dirtyY < 0) {
2673                 dirtyHeight = dirtyHeight+dirtyY;
2674                 dirtyY = 0;
2675             }
2676
2677             if (dirtyX+dirtyWidth > w) {
2678                 dirtyWidth = w - dirtyX;
2679             }
2680
2681             if (dirtyY+dirtyHeight > h) {
2682                 dirtyHeight = h - dirtyY;
2683             }
2684
2685             if (dirtyWidth <=0 || dirtyHeight <= 0)
2686                 return args.This();
2687         } else {
2688             dirtyX = 0;
2689             dirtyY = 0;
2690             dirtyWidth = w;
2691             dirtyHeight = h;
2692         }
2693
2694         QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2695         r->context->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
2696     }
2697     return args.This();
2698 }
2699
2700 /*!
2701     \qmlclass CanvasGradient
2702     \inqmlmodule QtQuick 2
2703     \since QtQuick 2.0
2704     \ingroup qtquick-canvas
2705     \brief Provides an opaque CanvasGradient interface
2706   */
2707
2708 /*!
2709   \qmlmethod QtQuick2::CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color)
2710   Adds a color stop with the given color to the gradient at the given offset.
2711   0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
2712
2713   For example:
2714   \code
2715   var gradient = ctx.createLinearGradient(0, 0, 100, 100);
2716   gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
2717   gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1');
2718   \endcode
2719   */
2720 static v8::Handle<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &args)
2721 {
2722     QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(args.This());
2723     if (!style)
2724         V8THROW_ERROR("Not a CanvasGradient object");
2725
2726     QV8Engine *engine = V8ENGINE();
2727
2728     if (args.Length() == 2) {
2729
2730         if (!style->brush.gradient())
2731             V8THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
2732         QGradient gradient = *(style->brush.gradient());
2733         qreal pos = args[0]->NumberValue();
2734         QColor color;
2735
2736         if (args[1]->IsObject()) {
2737             color = engine->toVariant(args[1], qMetaTypeId<QColor>()).value<QColor>();
2738         } else {
2739             color = qt_color_from_string(args[1]);
2740         }
2741         if (pos < 0.0 || pos > 1.0 || !qIsFinite(pos)) {
2742             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range");
2743         }
2744
2745         if (color.isValid()) {
2746             gradient.setColorAt(pos, color);
2747         } else {
2748             V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
2749         }
2750         style->brush = gradient;
2751     }
2752
2753     return args.This();
2754 }
2755
2756 void QQuickContext2D::scale(qreal x,  qreal y)
2757 {
2758     if (!state.invertibleCTM)
2759         return;
2760
2761     if (!qIsFinite(x) || !qIsFinite(y))
2762         return;
2763
2764     QTransform newTransform = state.matrix;
2765     newTransform.scale(x, y);
2766
2767     if (!newTransform.isInvertible()) {
2768         state.invertibleCTM = false;
2769         return;
2770     }
2771
2772     state.matrix = newTransform;
2773     buffer()->updateMatrix(state.matrix);
2774     m_path = QTransform().scale(1.0 / x, 1.0 / y).map(m_path);
2775 }
2776
2777 void QQuickContext2D::rotate(qreal angle)
2778 {
2779     if (!state.invertibleCTM)
2780         return;
2781
2782     if (!qIsFinite(angle))
2783         return;
2784
2785     QTransform newTransform =state.matrix;
2786     newTransform.rotate(DEGREES(angle));
2787
2788     if (!newTransform.isInvertible()) {
2789         state.invertibleCTM = false;
2790         return;
2791     }
2792
2793     state.matrix = newTransform;
2794     buffer()->updateMatrix(state.matrix);
2795     m_path = QTransform().rotate(-DEGREES(angle)).map(m_path);
2796 }
2797
2798 void QQuickContext2D::shear(qreal h, qreal v)
2799 {
2800     if (!state.invertibleCTM)
2801         return;
2802
2803     if (!qIsFinite(h) || !qIsFinite(v))
2804         return ;
2805
2806     QTransform newTransform = state.matrix;
2807     newTransform.shear(h, v);
2808
2809     if (!newTransform.isInvertible()) {
2810         state.invertibleCTM = false;
2811         return;
2812     }
2813
2814     state.matrix = newTransform;
2815     buffer()->updateMatrix(state.matrix);
2816     m_path = QTransform().shear(-h, -v).map(m_path);
2817 }
2818
2819 void QQuickContext2D::translate(qreal x, qreal y)
2820 {
2821     if (!state.invertibleCTM)
2822         return;
2823
2824     if (!qIsFinite(x) || !qIsFinite(y))
2825         return ;
2826
2827     QTransform newTransform = state.matrix;
2828     newTransform.translate(x, y);
2829
2830     if (!newTransform.isInvertible()) {
2831         state.invertibleCTM = false;
2832         return;
2833     }
2834
2835     state.matrix = newTransform;
2836     buffer()->updateMatrix(state.matrix);
2837     m_path = QTransform().translate(-x, -y).map(m_path);
2838 }
2839
2840 void QQuickContext2D::transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
2841 {
2842     if (!state.invertibleCTM)
2843         return;
2844
2845     if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f))
2846         return;
2847
2848     QTransform transform(a, b, c, d, e, f);
2849     QTransform newTransform = state.matrix * transform;
2850
2851     if (!newTransform.isInvertible()) {
2852         state.invertibleCTM = false;
2853         return;
2854     }
2855     state.matrix = newTransform;
2856     buffer()->updateMatrix(state.matrix);
2857     m_path = transform.inverted().map(m_path);
2858 }
2859
2860 void QQuickContext2D::setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
2861 {
2862     if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f))
2863         return;
2864
2865     QTransform ctm = state.matrix;
2866     if (!ctm.isInvertible())
2867         return;
2868
2869     state.matrix = ctm.inverted() * state.matrix;
2870     m_path = ctm.map(m_path);
2871     state.invertibleCTM = true;
2872     transform(a, b, c, d, e, f);
2873 }
2874
2875 void QQuickContext2D::fill()
2876 {
2877     if (!state.invertibleCTM)
2878         return;
2879
2880     if (!m_path.elementCount())
2881         return;
2882
2883     m_path.setFillRule(state.fillRule);
2884     buffer()->fill(m_path);
2885 }
2886
2887 void QQuickContext2D::clip()
2888 {
2889     if (!state.invertibleCTM)
2890         return;
2891
2892     QPainterPath clipPath = m_path;
2893     clipPath.closeSubpath();
2894     if (!state.clipPath.isEmpty())
2895         state.clipPath = clipPath.intersected(state.clipPath);
2896     else
2897         state.clipPath = clipPath;
2898     buffer()->clip(state.clipPath);
2899 }
2900
2901 void QQuickContext2D::stroke()
2902 {
2903     if (!state.invertibleCTM)
2904         return;
2905
2906     if (!m_path.elementCount())
2907         return;
2908
2909     buffer()->stroke(m_path);
2910 }
2911
2912 void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
2913 {
2914     if (!state.invertibleCTM)
2915         return;
2916
2917     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
2918         return;
2919
2920     buffer()->fillRect(QRectF(x, y, w, h));
2921 }
2922
2923 void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
2924 {
2925     if (!state.invertibleCTM)
2926         return;
2927
2928     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
2929         return;
2930
2931     buffer()->strokeRect(QRectF(x, y, w, h));
2932 }
2933
2934 void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
2935 {
2936     if (!state.invertibleCTM)
2937         return;
2938
2939     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
2940         return;
2941
2942     buffer()->clearRect(QRectF(x, y, w, h));
2943 }
2944
2945 void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill)
2946 {
2947     if (!state.invertibleCTM)
2948         return;
2949
2950     if (!qIsFinite(x) || !qIsFinite(y))
2951         return;
2952
2953     QPainterPath textPath = createTextGlyphs(x, y, text);
2954     if (fill)
2955         buffer()->fill(textPath);
2956     else
2957         buffer()->stroke(textPath);
2958 }
2959
2960
2961 void QQuickContext2D::beginPath()
2962 {
2963     if (!m_path.elementCount())
2964         return;
2965     m_path = QPainterPath();
2966 }
2967
2968 void QQuickContext2D::closePath()
2969 {
2970     if (!m_path.elementCount())
2971         return;
2972
2973     QRectF boundRect = m_path.boundingRect();
2974     if (boundRect.width() || boundRect.height())
2975         m_path.closeSubpath();
2976     //FIXME:QPainterPath set the current point to (0,0) after close subpath
2977     //should be the first point of the previous subpath
2978 }
2979
2980 void QQuickContext2D::moveTo( qreal x, qreal y)
2981 {
2982     if (!state.invertibleCTM)
2983         return;
2984
2985     //FIXME: moveTo should not close the previous subpath
2986     m_path.moveTo(QPointF(x, y));
2987 }
2988
2989 void QQuickContext2D::lineTo( qreal x, qreal y)
2990 {
2991     if (!state.invertibleCTM)
2992         return;
2993
2994     QPointF pt(x, y);
2995
2996     if (!m_path.elementCount())
2997         m_path.moveTo(pt);
2998     else if (m_path.currentPosition() != pt)
2999         m_path.lineTo(pt);
3000 }
3001
3002 void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
3003                                            qreal x, qreal y)
3004 {
3005     if (!state.invertibleCTM)
3006         return;
3007
3008     if (!m_path.elementCount())
3009         m_path.moveTo(QPointF(cpx, cpy));
3010
3011     QPointF pt(x, y);
3012     if (m_path.currentPosition() != pt)
3013         m_path.quadTo(QPointF(cpx, cpy), pt);
3014 }
3015
3016 void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
3017                                         qreal cp2x, qreal cp2y,
3018                                         qreal x, qreal y)
3019 {
3020     if (!state.invertibleCTM)
3021         return;
3022
3023     if (!m_path.elementCount())
3024         m_path.moveTo(QPointF(cp1x, cp1y));
3025
3026     QPointF pt(x, y);
3027     if (m_path.currentPosition() != pt)
3028         m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y),  pt);
3029 }
3030
3031 void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
3032 {
3033     QPointF p0(m_path.currentPosition());
3034
3035     QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
3036     QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
3037     float p1p0_length = qSqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y());
3038     float p1p2_length = qSqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y());
3039
3040     double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length);
3041
3042     // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
3043     // We could have used areCollinear() here, but since we're reusing
3044     // the variables computed above later on we keep this logic.
3045     if (qFuzzyCompare(qAbs(cos_phi), 1.0)) {
3046         m_path.lineTo(p1);
3047         return;
3048     }
3049
3050     float tangent = radius / tan(acos(cos_phi) / 2);
3051     float factor_p1p0 = tangent / p1p0_length;
3052     QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
3053
3054     QPointF orth_p1p0(p1p0.y(), -p1p0.x());
3055     float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y());
3056     float factor_ra = radius / orth_p1p0_length;
3057
3058     // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
3059     double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length);
3060     if (cos_alpha < 0.f)
3061         orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3062
3063     QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
3064
3065     // calculate angles for addArc
3066     orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3067     float sa = acos(orth_p1p0.x() / orth_p1p0_length);
3068     if (orth_p1p0.y() < 0.f)
3069         sa = 2 * Q_PI - sa;
3070
3071     // anticlockwise logic
3072     bool anticlockwise = false;
3073
3074     float factor_p1p2 = tangent / p1p2_length;
3075     QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
3076     QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
3077     float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y());
3078     float ea = acos(orth_p1p2.x() / orth_p1p2_length);
3079     if (orth_p1p2.y() < 0)
3080         ea = 2 * Q_PI - ea;
3081     if ((sa > ea) && ((sa - ea) < Q_PI))
3082         anticlockwise = true;
3083     if ((sa < ea) && ((ea - sa) > Q_PI))
3084         anticlockwise = true;
3085
3086     arc(p.x(), p.y(), radius, sa, ea, anticlockwise);
3087 }
3088
3089 void QQuickContext2D::arcTo(qreal x1, qreal y1,
3090                                 qreal x2, qreal y2,
3091                                 qreal radius)
3092 {
3093     if (!state.invertibleCTM)
3094         return;
3095
3096     if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2) || !qIsFinite(radius))
3097         return;
3098
3099     QPointF st(x1, y1);
3100     QPointF end(x2, y2);
3101
3102     if (!m_path.elementCount())
3103         m_path.moveTo(st);
3104     else if (st == m_path.currentPosition() || st == end || !radius)
3105         lineTo(x1, y1);
3106     else
3107         addArcTo(st, end, radius);
3108  }
3109
3110 void QQuickContext2D::rect(qreal x, qreal y, qreal w, qreal h)
3111 {
3112     if (!state.invertibleCTM)
3113         return;
3114     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
3115         return;
3116
3117     if (!w && !h) {
3118         m_path.moveTo(x, y);
3119         return;
3120     }
3121     m_path.addRect(x, y, w, h);
3122 }
3123
3124 void QQuickContext2D::roundedRect(qreal x, qreal y,
3125                                qreal w, qreal h,
3126                                qreal xr, qreal yr)
3127 {
3128     if (!state.invertibleCTM)
3129         return;
3130
3131     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h) || !qIsFinite(xr) || !qIsFinite(yr))
3132         return;
3133
3134     if (!w && !h) {
3135         m_path.moveTo(x, y);
3136         return;
3137     }
3138     m_path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
3139 }
3140
3141 void QQuickContext2D::ellipse(qreal x, qreal y,
3142                            qreal w, qreal h)
3143 {
3144     if (!state.invertibleCTM)
3145         return;
3146
3147     if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
3148         return;
3149
3150     if (!w && !h) {
3151         m_path.moveTo(x, y);
3152         return;
3153     }
3154
3155     m_path.addEllipse(x, y, w, h);
3156 }
3157
3158 void QQuickContext2D::text(const QString& str, qreal x, qreal y)
3159 {
3160     if (!state.invertibleCTM)
3161         return;
3162
3163     QPainterPath path;
3164     path.addText(x, y, state.font, str);
3165     m_path.addPath(path);
3166 }
3167
3168 void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise)
3169 {
3170     if (!state.invertibleCTM)
3171         return;
3172
3173     if (!qIsFinite(xc) || !qIsFinite(yc) || !qIsFinite(sar) || !qIsFinite(ear) || !qIsFinite(radius))
3174         return;
3175
3176     if (sar == ear)
3177         return;
3178
3179
3180     //### HACK
3181
3182     // In Qt we don't switch the coordinate system for degrees
3183     // and still use the 0,0 as bottom left for degrees so we need
3184     // to switch
3185     sar = -sar;
3186     ear = -ear;
3187     antiClockWise = !antiClockWise;
3188     //end hack
3189
3190     float sa = DEGREES(sar);
3191     float ea = DEGREES(ear);
3192
3193     double span = 0;
3194
3195     double xs     = xc - radius;
3196     double ys     = yc - radius;
3197     double width  = radius*2;
3198     double height = radius*2;
3199     if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360)))
3200         // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
3201         // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
3202         // circumference of this circle.
3203         span = 360;
3204     else {
3205         if (!antiClockWise && (ea < sa)) {
3206             span += 360;
3207         } else if (antiClockWise && (sa < ea)) {
3208             span -= 360;
3209         }
3210         //### this is also due to switched coordinate system
3211         // we would end up with a 0 span instead of 360
3212         if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
3213               qFuzzyCompare(qAbs(span), 360))) {
3214             span   += ea - sa;
3215         }
3216     }
3217
3218     // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
3219     if (!m_path.elementCount())
3220         m_path.arcMoveTo(xs, ys, width, height, sa);
3221     else if (!radius) {
3222         m_path.lineTo(xc, yc);
3223         return;
3224     }
3225
3226     m_path.arcTo(xs, ys, width, height, sa, span);
3227 }
3228
3229 int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
3230 {
3231     int offset = 0;
3232     switch (value) {
3233     case QQuickContext2D::Top:
3234         break;
3235     case QQuickContext2D::Alphabetic:
3236     case QQuickContext2D::Middle:
3237     case QQuickContext2D::Hanging:
3238         offset = metrics.ascent();
3239         break;
3240     case QQuickContext2D::Bottom:
3241         offset = metrics.height();
3242        break;
3243     }
3244     return offset;
3245 }
3246
3247 static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
3248 {
3249     int offset = 0;
3250     if (value == QQuickContext2D::Start)
3251         value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Left : QQuickContext2D::Right;
3252     else if (value == QQuickContext2D::End)
3253         value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Right: QQuickContext2D::Left;
3254     switch (value) {
3255     case QQuickContext2D::Center:
3256         offset = metrics.width(text)/2;
3257         break;
3258     case QQuickContext2D::Right:
3259         offset = metrics.width(text);
3260     case QQuickContext2D::Left:
3261     default:
3262         break;
3263     }
3264     return offset;
3265 }
3266
3267
3268 QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
3269 {
3270     return m_canvas->loadedPixmap(url);
3271 }
3272
3273 QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
3274 {
3275     const QFontMetrics metrics(state.font);
3276     int yoffset = baseLineOffset(static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
3277     int xoffset = textAlignOffset(static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
3278
3279     QPainterPath textPath;
3280
3281     textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
3282     textPath = state.matrix.map(textPath);
3283     return textPath;
3284 }
3285
3286
3287 static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
3288 {
3289     // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
3290     return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x()));
3291 }
3292
3293 static inline bool withinRange(qreal p, qreal a, qreal b)
3294 {
3295     return (p >= a && p <= b) || (p >= b && p <= a);
3296 }
3297
3298 bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
3299 {
3300     if (!state.invertibleCTM)
3301         return false;
3302
3303     if (!m_path.elementCount())
3304         return false;
3305
3306     if (!qIsFinite(x) || !qIsFinite(y))
3307         return false;
3308
3309     QPointF point(x, y);
3310     QTransform ctm = state.matrix;
3311     QPointF p = ctm.inverted().map(point);
3312     if (!qIsFinite(p.x()) || !qIsFinite(p.y()))
3313         return false;
3314
3315     const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
3316
3317     bool contains = m_path.contains(p);
3318
3319     if (!contains) {
3320         // check whether the point is on the border
3321         QPolygonF border = m_path.toFillPolygon();
3322
3323         QPointF p1 = border.at(0);
3324         QPointF p2;
3325
3326         for (int i = 1; i < border.size(); ++i) {
3327             p2 = border.at(i);
3328             if (areCollinear(p, p1, p2)
3329                     // Once we know that the points are collinear we
3330                     // only need to check one of the coordinates
3331                     && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ?
3332                         withinRange(p.x(), p1.x(), p2.x()) :
3333                         withinRange(p.y(), p1.y(), p2.y()))) {
3334                 return true;
3335             }
3336             p1 = p2;
3337         }
3338     }
3339     return contains;
3340 }
3341
3342 QQuickContext2D::QQuickContext2D(QObject *parent)
3343     : QQuickCanvasContext(parent)
3344     , m_buffer(new QQuickContext2DCommandBuffer)
3345     , m_v8engine(0)
3346     , m_windowManager(0)
3347     , m_surface(0)
3348     , m_glContext(0)
3349 {
3350 }
3351
3352 QQuickContext2D::~QQuickContext2D()
3353 {
3354     delete m_buffer;
3355 }
3356
3357 v8::Handle<v8::Object> QQuickContext2D::v8value() const
3358 {
3359     return m_v8value;
3360 }
3361
3362 QStringList QQuickContext2D::contextNames() const
3363 {
3364     return QStringList() << QLatin1String("2d");
3365 }
3366
3367 void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args)
3368 {
3369     Q_UNUSED(args);
3370
3371     m_canvas = canvasItem;
3372     m_renderTarget = canvasItem->renderTarget();
3373
3374     QQuickCanvas *canvas = canvasItem->canvas();
3375     m_windowManager =  QQuickCanvasPrivate::get(canvas)->windowManager;
3376     m_renderStrategy = canvasItem->renderStrategy();
3377
3378     switch (m_renderTarget) {
3379     case QQuickCanvasItem::Image:
3380         m_texture = new QQuickContext2DImageTexture(m_renderStrategy == QQuickCanvasItem::Threaded);
3381         break;
3382     case QQuickCanvasItem::FramebufferObject:
3383     {
3384         m_texture = new QQuickContext2DFBOTexture;
3385         // No BufferQueueingOpenGL, falls back to Cooperative mode
3386         if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL))
3387             m_renderStrategy = QQuickCanvasItem::Cooperative;
3388     }
3389         break;
3390     }
3391
3392     m_texture->setItem(canvasItem);
3393     m_texture->setCanvasWindow(canvasItem->canvasWindow().toRect());
3394     m_texture->setTileSize(canvasItem->tileSize());
3395     m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
3396     m_texture->setSmooth(canvasItem->smooth());
3397
3398     QThread *renderThread = QThread::currentThread();
3399     QThread *sceneGraphThread = canvas->openglContext() ? canvas->openglContext()->thread() : 0;
3400
3401     if (m_renderStrategy == QQuickCanvasItem::Threaded)
3402         renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
3403     else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
3404         renderThread = sceneGraphThread;
3405
3406     if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
3407          QOpenGLContext *cc = QQuickCanvasPrivate::get(canvas)->context->glContext();
3408          m_surface = canvas;
3409          m_glContext = new QOpenGLContext;
3410          m_glContext->setFormat(cc->format());
3411          m_glContext->setShareContext(cc);
3412          if (renderThread != QThread::currentThread())
3413              m_glContext->moveToThread(renderThread);
3414     }
3415
3416     connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
3417
3418     reset();
3419 }
3420
3421 void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth)
3422 {
3423     m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth);
3424 }
3425
3426 void QQuickContext2D::flush()
3427 {
3428     if (!m_buffer->isEmpty()) {
3429         QMutexLocker lock(&m_bufferMutex);
3430         m_bufferQueue.enqueue(m_buffer);
3431         m_buffer = new QQuickContext2DCommandBuffer;
3432     } else
3433         return;
3434
3435     switch (m_renderStrategy) {
3436     case QQuickCanvasItem::Immediate:
3437         // Cause the texture to consume paint commands immediately
3438         m_texture->paint();
3439         break;
3440     case QQuickCanvasItem::Threaded:
3441         // wake up thread to consume paint commands
3442         m_texture->paint();
3443         break;
3444     case QQuickCanvasItem::Cooperative:
3445         // NOTE: On SG Thread
3446         m_texture->paint();
3447         break;
3448     }
3449 }
3450
3451 QSGDynamicTexture *QQuickContext2D::texture() const
3452 {
3453     return m_texture;
3454 }
3455
3456 QImage QQuickContext2D::toImage(const QRectF& bounds)
3457 {
3458     switch (m_renderStrategy) {
3459     case QQuickCanvasItem::Immediate:
3460     case QQuickCanvasItem::Threaded:
3461         flush();
3462         break;
3463     case QQuickCanvasItem::Cooperative:
3464         break;
3465     }
3466
3467     return m_texture->toImage(bounds);
3468 }
3469
3470
3471 QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine)
3472 {
3473     v8::HandleScope handle_scope;
3474     v8::Context::Scope scope(engine->context());
3475
3476     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
3477     ft->InstanceTemplate()->SetHasExternalResource(true);
3478     ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine));
3479     ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine));
3480     ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine));
3481     ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine));
3482     ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine));
3483     ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine));
3484     ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine));
3485     ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine));
3486     ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine));
3487     ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine));
3488     ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine));
3489     ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine));
3490     ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine));
3491     ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine));
3492     ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine));
3493     ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine));
3494     ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine));
3495     ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine));
3496     ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine));
3497     ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
3498     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine));
3499     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine));
3500     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine));
3501     ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine));
3502     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine));
3503     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine));
3504     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine));
3505     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine));
3506     ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine));
3507     ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine));
3508     ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine));
3509     ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine));
3510     ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine));
3511     ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine));
3512     ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine));
3513     ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine));
3514     ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine));
3515     ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine));
3516     ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine));
3517     ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine));
3518     ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine));
3519     ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine));
3520     ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine));
3521     ft->PrototypeTemplate()->Set(v8::String::New("roundedRect"), V8FUNCTION(ctx2d_roundedRect, engine));
3522     ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine));
3523     ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine));
3524     ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine));
3525     ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine));
3526     ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine));
3527     ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine));
3528     ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine));
3529     ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine));
3530     ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine));
3531     ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine));
3532     ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine));
3533     ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine));
3534     ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine));
3535     ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine));
3536     ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine));
3537     ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine));
3538     ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine));
3539
3540     constructorContext = qPersistentNew(ft->GetFunction());
3541
3542     v8::Local<v8::FunctionTemplate> ftGradient = v8::FunctionTemplate::New();
3543     ftGradient->InstanceTemplate()->SetHasExternalResource(true);
3544     ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine));
3545     constructorGradient = qPersistentNew(ftGradient->GetFunction());
3546
3547     v8::Local<v8::FunctionTemplate> ftPattern = v8::FunctionTemplate::New();
3548     ftPattern->InstanceTemplate()->SetHasExternalResource(true);
3549     constructorPattern = qPersistentNew(ftPattern->GetFunction());
3550
3551     v8::Local<v8::FunctionTemplate> ftPixelArray = v8::FunctionTemplate::New();
3552     ftPixelArray->InstanceTemplate()->SetHasExternalResource(true);
3553     ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine));
3554     ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine));
3555     constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction());
3556
3557     v8::Local<v8::FunctionTemplate> ftImageData = v8::FunctionTemplate::New();
3558     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine));
3559     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine));
3560     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine));
3561     ftImageData->InstanceTemplate()->SetInternalFieldCount(1);
3562     constructorImageData = qPersistentNew(ftImageData->GetFunction());
3563 }
3564
3565 QQuickContext2DEngineData::~QQuickContext2DEngineData()
3566 {
3567     qPersistentDispose(constructorContext);
3568     qPersistentDispose(constructorGradient);
3569     qPersistentDispose(constructorPattern);
3570     qPersistentDispose(constructorImageData);
3571     qPersistentDispose(constructorPixelArray);
3572 }
3573
3574 void QQuickContext2D::popState()
3575 {
3576     if (m_stateStack.isEmpty())
3577         return;
3578
3579     QQuickContext2D::State newState = m_stateStack.pop();
3580
3581     if (state.matrix != newState.matrix)
3582         buffer()->updateMatrix(newState.matrix);
3583
3584     if (newState.globalAlpha != state.globalAlpha)
3585         buffer()->setGlobalAlpha(newState.globalAlpha);
3586
3587     if (newState.globalCompositeOperation != state.globalCompositeOperation)
3588         buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
3589
3590     if (newState.fillStyle != state.fillStyle)
3591         buffer()->setFillStyle(newState.fillStyle);
3592
3593     if (newState.strokeStyle != state.strokeStyle)
3594         buffer()->setStrokeStyle(newState.strokeStyle);
3595
3596     if (newState.lineWidth != state.lineWidth)
3597         buffer()->setLineWidth(newState.lineWidth);
3598
3599     if (newState.lineCap != state.lineCap)
3600         buffer()->setLineCap(newState.lineCap);
3601
3602     if (newState.lineJoin != state.lineJoin)
3603         buffer()->setLineJoin(newState.lineJoin);
3604
3605     if (newState.miterLimit != state.miterLimit)
3606         buffer()->setMiterLimit(newState.miterLimit);
3607
3608     if (newState.clipPath != state.clipPath) {
3609         buffer()->clip(newState.clipPath);
3610     }
3611
3612     if (newState.shadowBlur != state.shadowBlur)
3613         buffer()->setShadowBlur(newState.shadowBlur);
3614
3615     if (newState.shadowColor != state.shadowColor)
3616         buffer()->setShadowColor(newState.shadowColor);
3617
3618     if (newState.shadowOffsetX != state.shadowOffsetX)
3619         buffer()->setShadowOffsetX(newState.shadowOffsetX);
3620
3621     if (newState.shadowOffsetY != state.shadowOffsetY)
3622         buffer()->setShadowOffsetY(newState.shadowOffsetY);
3623     m_path = state.matrix.map(m_path);
3624     state = newState;
3625     m_path = state.matrix.inverted().map(m_path);
3626 }
3627 void QQuickContext2D::pushState()
3628 {
3629     m_stateStack.push(state);
3630 }
3631
3632 void QQuickContext2D::reset()
3633 {
3634     QQuickContext2D::State newState;
3635     newState.matrix = QTransform();
3636
3637     m_path = QPainterPath();
3638
3639     QPainterPath defaultClipPath;
3640
3641     QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
3642     r = r.united(m_canvas->canvasWindow().toRect());
3643     defaultClipPath.addRect(r);
3644     newState.clipPath = defaultClipPath;
3645     newState.clipPath.setFillRule(Qt::WindingFill);
3646
3647     newState.strokeStyle = QColor("#000000");
3648     newState.fillStyle = QColor("#000000");
3649     newState.fillPatternRepeatX = false;
3650     newState.fillPatternRepeatY = false;
3651     newState.strokePatternRepeatX = false;
3652     newState.strokePatternRepeatY = false;
3653     newState.invertibleCTM = true;
3654     newState.fillRule = Qt::WindingFill;
3655     newState.globalAlpha = 1.0;
3656     newState.lineWidth = 1;
3657     newState.lineCap = Qt::FlatCap;
3658     newState.lineJoin = Qt::MiterJoin;
3659     newState.miterLimit = 10;
3660     newState.shadowOffsetX = 0;
3661     newState.shadowOffsetY = 0;
3662     newState.shadowBlur = 0;
3663     newState.shadowColor = qRgba(0, 0, 0, 0);
3664     newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
3665     newState.font = QFont(QLatin1String("sans-serif"), 10);
3666     newState.textAlign = QQuickContext2D::Start;
3667     newState.textBaseline = QQuickContext2D::Alphabetic;
3668
3669     m_stateStack.clear();
3670     m_stateStack.push(newState);
3671     popState();
3672     m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
3673 }
3674
3675 void QQuickContext2D::setV8Engine(QV8Engine *engine)
3676 {
3677     v8::HandleScope handle_scope;
3678     v8::Context::Scope scope(engine->context());
3679
3680     if (m_v8engine != engine) {
3681         m_v8engine = engine;
3682
3683         qPersistentDispose(m_v8value);
3684
3685         if (m_v8engine == 0)
3686             return;
3687
3688         QQuickContext2DEngineData *ed = engineData(engine);
3689         m_v8value = qPersistentNew(ed->constructorContext->NewInstance());
3690         QV8Context2DResource *r = new QV8Context2DResource(engine);
3691         r->context = this;
3692         m_v8value->SetExternalResource(r);
3693     }
3694 }
3695
3696 QQuickContext2DCommandBuffer* QQuickContext2D::nextBuffer()
3697 {
3698     QMutexLocker lock(&m_bufferMutex);
3699     return m_bufferQueue.isEmpty() ? 0 : m_bufferQueue.dequeue();
3700 }
3701
3702 QT_END_NAMESPACE