canvas-related doc fixes
[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/qquickwindow_p.h>
65 #include <private/qquickwindowmanager_p.h>
66
67 #ifdef Q_OS_QNX
68 #include <ctype.h>
69 #endif
70
71 QT_BEGIN_NAMESPACE
72 /*!
73     \qmltype Context2D
74     \instantiates QQuickContext2D
75     \inqmlmodule QtQuick 2
76     \ingroup qtquick-canvas
77     \since QtQuick 2.0
78     \brief Provides 2D context for shapes on a Canvas item
79
80     The Context2D object can be created by \c Canvas item's \c getContext()
81     method:
82     \code
83     Canvas {
84       id:canvas
85       onPaint:{
86          var ctx = canvas.getContext('2d');
87          //...
88       }
89     }
90     \endcode
91     The Context2D API implements the same \l
92     {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard} with
93     some enhanced features.
94
95     The Context2D API provides the rendering \b{context} which defines the
96     methods and attributes needed to draw on the \c Canvas item. The following
97     assigns the canvas rendering context to a \c{context} variable:
98     \code
99     var context = mycanvas.getContext("2d")
100     \endcode
101
102     The Context2D API renders the canvas as a coordinate system whose origin
103     (0,0) is at the top left corner, as shown in the figure below. Coordinates
104     increase along the \c{x} axis from left to right and along the \c{y} axis
105     from top to bottom of the canvas.
106     \image qml-item-canvas-context.gif
107 */
108
109
110
111 Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
112
113 static const double Q_PI   = 3.14159265358979323846;   // pi
114
115 #define DEGREES(t) ((t) * 180.0 / Q_PI)
116
117 #define CHECK_CONTEXT(r)     if (!r || !r->context || !r->context->bufferValid()) \
118                                 V8THROW_ERROR("Not a Context2D object");
119
120 #define CHECK_CONTEXT_SETTER(r)     if (!r || !r->context || !r->context->bufferValid()) \
121                                        V8THROW_ERROR_SETTER("Not a Context2D object");
122 #define qClamp(val, min, max) qMin(qMax(val, min), max)
123 #define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
124 QColor qt_color_from_string(v8::Local<v8::Value> name)
125 {
126     v8::String::AsciiValue str(name);
127
128     char *p = *str;
129     int len = str.length();
130     //rgb/hsl color string has at least 7 characters
131     if (!p || len > 255 || len <= 7)
132         return QColor(p);
133     else {
134         bool isRgb(false), isHsl(false), hasAlpha(false);
135         Q_UNUSED(isHsl)
136
137         while (isspace(*p)) p++;
138         if (strncmp(p, "rgb", 3) == 0)
139             isRgb = true;
140         else if (strncmp(p, "hsl", 3) == 0)
141             isHsl = true;
142         else
143             return QColor(p);
144
145         p+=3; //skip "rgb" or "hsl"
146         hasAlpha = (*p == 'a') ? true : false;
147
148         ++p; //skip "("
149
150         if (hasAlpha) ++p; //skip "a"
151
152         int rh, gs, bl, alpha = 255;
153
154         //red
155         while (isspace(*p)) p++;
156         rh = strtol(p, &p, 10);
157         if (*p == '%') {
158             rh = qRound(rh/100.0 * 255);
159             ++p;
160         }
161         if (*p++ != ',') return QColor();
162
163         //green
164         while (isspace(*p)) p++;
165         gs = strtol(p, &p, 10);
166         if (*p == '%') {
167             gs = qRound(gs/100.0 * 255);
168             ++p;
169         }
170         if (*p++ != ',') return QColor();
171
172         //blue
173         while (isspace(*p)) p++;
174         bl = strtol(p, &p, 10);
175         if (*p == '%') {
176             bl = qRound(bl/100.0 * 255);
177             ++p;
178         }
179
180         if (hasAlpha) {
181             if (*p++!= ',') return QColor();
182             while (isspace(*p)) p++;
183             bool ok = false;
184             alpha = qRound(qstrtod(p, const_cast<const char **>(&p), &ok) * 255);
185         }
186
187         if (*p != ')') return QColor();
188         if (isRgb)
189             return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
190         else if (isHsl)
191             return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
192     }
193     return QColor();
194 }
195
196 QFont qt_font_from_string(const QString& fontString) {
197     QFont font;
198      // ### this is simplified and incomplete
199     // ### TODO:get code from Qt webkit
200      const QStringList tokens = fontString.split(QLatin1Char(' '));
201      foreach (const QString &token, tokens) {
202          if (token == QLatin1String("italic"))
203              font.setItalic(true);
204          else if (token == QLatin1String("bold"))
205              font.setBold(true);
206          else if (token.endsWith(QLatin1String("px"))) {
207              QString number = token;
208              number.remove(QLatin1String("px"));
209              //font.setPointSizeF(number.trimmed().toFloat());
210              font.setPixelSize(number.trimmed().toInt());
211          } else
212              font.setFamily(token);
213      }
214
215      return font;
216 }
217
218
219
220 class QQuickContext2DEngineData : public QV8Engine::Deletable
221 {
222 public:
223     QQuickContext2DEngineData(QV8Engine *engine);
224     ~QQuickContext2DEngineData();
225
226     v8::Persistent<v8::Function> constructorContext;
227     v8::Persistent<v8::Function> constructorGradient;
228     v8::Persistent<v8::Function> constructorPattern;
229     v8::Persistent<v8::Function> constructorPixelArray;
230     v8::Persistent<v8::Function> constructorImageData;
231 };
232
233 V8_DEFINE_EXTENSION(QQuickContext2DEngineData, engineData)
234
235 class QV8Context2DResource : public QV8ObjectResource
236 {
237     V8_RESOURCE_TYPE(Context2DType)
238 public:
239     QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e), context(0) {}
240     QQuickContext2D* context;
241 };
242
243 class QV8Context2DStyleResource : public QV8ObjectResource
244 {
245     V8_RESOURCE_TYPE(Context2DStyleType)
246 public:
247     QV8Context2DStyleResource(QV8Engine *e)
248       : QV8ObjectResource(e)
249       , patternRepeatX(false)
250       , patternRepeatY(false)
251     {}
252     QBrush brush;
253     bool patternRepeatX:1;
254     bool patternRepeatY:1;
255 };
256
257 class QV8Context2DPixelArrayResource : public QV8ObjectResource
258 {
259     V8_RESOURCE_TYPE(Context2DPixelArrayType)
260 public:
261     QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {}
262
263     QImage image;
264 };
265
266 QImage qt_image_convolute_filter(const QImage& src, const QVector<qreal>& weights, int radius = 0)
267 {
268     int sides = radius ? radius : qRound(qSqrt(weights.size()));
269     int half = qFloor(sides/2);
270
271     QImage dst = QImage(src.size(), src.format());
272     int w = src.width();
273     int h = src.height();
274     for (int y = 0; y < dst.height(); ++y) {
275       QRgb *dr = (QRgb*)dst.scanLine(y);
276       for (int x = 0; x < dst.width(); ++x) {
277           unsigned char* dRgb = ((unsigned char*)&dr[x]);
278           unsigned char red=0, green=0, blue=0, alpha=0;
279           int sy = y;
280           int sx = x;
281
282           for (int cy=0; cy<sides; cy++) {
283              for (int cx=0; cx<sides; cx++) {
284                int scy = sy + cy - half;
285                int scx = sx + cx - half;
286                if (scy >= 0 && scy < w && scx >= 0 && scx < h) {
287                   const QRgb *sr = (const QRgb*)(src.constScanLine(scy));
288                   const unsigned char* sRgb = ((const unsigned char*)&sr[scx]);
289                   qreal wt = radius ? weights[0] : weights[cy*sides+cx];
290                   red += sRgb[0] * wt;
291                   green += sRgb[1] * wt;
292                   blue += sRgb[2] * wt;
293                   alpha += sRgb[3] * wt;
294                }
295              }
296           }
297           dRgb[0] = red;
298           dRgb[1] = green;
299           dRgb[2] = blue;
300           dRgb[3] = alpha;
301       }
302     }
303     return dst;
304 }
305
306 void qt_image_boxblur(QImage& image, int radius, bool quality)
307 {
308     int passes = quality? 3: 1;
309     for (int i=0; i < passes; i++) {
310         image = qt_image_convolute_filter(image, QVector<qreal>() << 1.0/(radius * radius * 1.0), radius);
311     }
312 }
313
314 static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
315 {
316     if (compositeOperator == QLatin1String("source-over")) {
317         return QPainter::CompositionMode_SourceOver;
318     } else if (compositeOperator == QLatin1String("source-out")) {
319         return QPainter::CompositionMode_SourceOut;
320     } else if (compositeOperator == QLatin1String("source-in")) {
321         return QPainter::CompositionMode_SourceIn;
322     } else if (compositeOperator == QLatin1String("source-atop")) {
323         return QPainter::CompositionMode_SourceAtop;
324     } else if (compositeOperator == QLatin1String("destination-atop")) {
325         return QPainter::CompositionMode_DestinationAtop;
326     } else if (compositeOperator == QLatin1String("destination-in")) {
327         return QPainter::CompositionMode_DestinationIn;
328     } else if (compositeOperator == QLatin1String("destination-out")) {
329         return QPainter::CompositionMode_DestinationOut;
330     } else if (compositeOperator == QLatin1String("destination-over")) {
331         return QPainter::CompositionMode_DestinationOver;
332     } else if (compositeOperator == QLatin1String("lighter")) {
333         return QPainter::CompositionMode_Lighten;
334     } else if (compositeOperator == QLatin1String("copy")) {
335         return QPainter::CompositionMode_Source;
336     } else if (compositeOperator == QLatin1String("xor")) {
337         return QPainter::CompositionMode_Xor;
338     } else if (compositeOperator == QLatin1String("qt-clear")) {
339         return QPainter::CompositionMode_Clear;
340     } else if (compositeOperator == QLatin1String("qt-destination")) {
341         return QPainter::CompositionMode_Destination;
342     } else if (compositeOperator == QLatin1String("qt-multiply")) {
343         return QPainter::CompositionMode_Multiply;
344     } else if (compositeOperator == QLatin1String("qt-screen")) {
345         return QPainter::CompositionMode_Screen;
346     } else if (compositeOperator == QLatin1String("qt-overlay")) {
347         return QPainter::CompositionMode_Overlay;
348     } else if (compositeOperator == QLatin1String("qt-darken")) {
349         return QPainter::CompositionMode_Darken;
350     } else if (compositeOperator == QLatin1String("qt-lighten")) {
351         return QPainter::CompositionMode_Lighten;
352     } else if (compositeOperator == QLatin1String("qt-color-dodge")) {
353         return QPainter::CompositionMode_ColorDodge;
354     } else if (compositeOperator == QLatin1String("qt-color-burn")) {
355         return QPainter::CompositionMode_ColorBurn;
356     } else if (compositeOperator == QLatin1String("qt-hard-light")) {
357         return QPainter::CompositionMode_HardLight;
358     } else if (compositeOperator == QLatin1String("qt-soft-light")) {
359         return QPainter::CompositionMode_SoftLight;
360     } else if (compositeOperator == QLatin1String("qt-difference")) {
361         return QPainter::CompositionMode_Difference;
362     } else if (compositeOperator == QLatin1String("qt-exclusion")) {
363         return QPainter::CompositionMode_Exclusion;
364     }
365     return QPainter::CompositionMode_SourceOver;
366 }
367
368 static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
369 {
370     switch (op) {
371     case QPainter::CompositionMode_SourceOver:
372         return QLatin1String("source-over");
373     case QPainter::CompositionMode_DestinationOver:
374         return QLatin1String("destination-over");
375     case QPainter::CompositionMode_Clear:
376         return QLatin1String("qt-clear");
377     case QPainter::CompositionMode_Source:
378         return QLatin1String("copy");
379     case QPainter::CompositionMode_Destination:
380         return QLatin1String("qt-destination");
381     case QPainter::CompositionMode_SourceIn:
382         return QLatin1String("source-in");
383     case QPainter::CompositionMode_DestinationIn:
384         return QLatin1String("destination-in");
385     case QPainter::CompositionMode_SourceOut:
386         return QLatin1String("source-out");
387     case QPainter::CompositionMode_DestinationOut:
388         return QLatin1String("destination-out");
389     case QPainter::CompositionMode_SourceAtop:
390         return QLatin1String("source-atop");
391     case QPainter::CompositionMode_DestinationAtop:
392         return QLatin1String("destination-atop");
393     case QPainter::CompositionMode_Xor:
394         return QLatin1String("xor");
395     case QPainter::CompositionMode_Plus:
396         return QLatin1String("plus");
397     case QPainter::CompositionMode_Multiply:
398         return QLatin1String("qt-multiply");
399     case QPainter::CompositionMode_Screen:
400         return QLatin1String("qt-screen");
401     case QPainter::CompositionMode_Overlay:
402         return QLatin1String("qt-overlay");
403     case QPainter::CompositionMode_Darken:
404         return QLatin1String("qt-darken");
405     case QPainter::CompositionMode_Lighten:
406         return QLatin1String("lighter");
407     case QPainter::CompositionMode_ColorDodge:
408         return QLatin1String("qt-color-dodge");
409     case QPainter::CompositionMode_ColorBurn:
410         return QLatin1String("qt-color-burn");
411     case QPainter::CompositionMode_HardLight:
412         return QLatin1String("qt-hard-light");
413     case QPainter::CompositionMode_SoftLight:
414         return QLatin1String("qt-soft-light");
415     case QPainter::CompositionMode_Difference:
416         return QLatin1String("qt-difference");
417     case QPainter::CompositionMode_Exclusion:
418         return QLatin1String("qt-exclusion");
419     default:
420         break;
421     }
422     return QString();
423 }
424
425
426 static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image)
427 {
428     QQuickContext2DEngineData *ed = engineData(engine);
429     v8::Local<v8::Object> imageData = ed->constructorImageData->NewInstance();
430     QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine);
431     if (image.isNull()) {
432         r->image = QImage(w, h, QImage::Format_ARGB32);
433         r->image.fill(0x00000000);
434     } else {
435         Q_ASSERT(image.width() == w && image.height() == h);
436         r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
437     }
438     v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
439     pixelData->SetExternalResource(r);
440
441     imageData->SetInternalField(0, pixelData);
442     return imageData;
443 }
444
445 //static script functions
446
447 /*!
448     \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas
449      Holds the canvas item that the context paints on.
450
451      This property is read only.
452 */
453 static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::AccessorInfo &info)
454 {
455     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
456     CHECK_CONTEXT(r)
457
458
459     QV8Engine *engine = V8ENGINE_ACCESSOR();
460
461     return engine->newQObject(r->context->canvas());
462 }
463
464 /*!
465     \qmlmethod object QtQuick2::Context2D::restore()
466     Pops the top state on the stack, restoring the context to that state.
467
468     \sa save()
469 */
470 static v8::Handle<v8::Value> ctx2d_restore(const v8::Arguments &args)
471 {
472     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
473     CHECK_CONTEXT(r)
474
475     r->context->popState();
476     return args.This();
477 }
478
479 /*!
480     \qmlmethod object QtQuick2::Context2D::reset()
481     Resets the context state and properties to the default values.
482 */
483 static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args)
484 {
485     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
486     CHECK_CONTEXT(r)
487
488     r->context->reset();
489
490     return args.This();
491 }
492
493 /*!
494     \qmlmethod object QtQuick2::Context2D::save()
495     Pushes the current state onto the state stack.
496
497     Before changing any state attributes, you should save the current state
498     for future reference. The context maintains a stack of drawing states.
499     Each state consists of the current transformation matrix, clipping region,
500     and values of the following attributes:
501     \list
502     \li strokeStyle
503     \li fillStyle
504     \li fillRule
505     \li globalAlpha
506     \li lineWidth
507     \li lineCap
508     \li lineJoin
509     \li miterLimit
510     \li shadowOffsetX
511     \li shadowOffsetY
512     \li shadowBlur
513     \li shadowColor
514     \li globalCompositeOperation
515     \li \l font
516     \li textAlign
517     \li textBaseline
518     \endlist
519
520     The current path is NOT part of the drawing state. The path can be reset by
521     invoking the beginPath() method.
522 */
523 static v8::Handle<v8::Value> ctx2d_save(const v8::Arguments &args)
524 {
525     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
526     CHECK_CONTEXT(r)
527
528     r->context->pushState();
529
530     return args.This();
531 }
532
533 // transformations
534 /*!
535     \qmlmethod object QtQuick2::Context2D::rotate(real angle)
536     Rotate the canvas around the current origin by \c angle in radians and clockwise direction.
537     \code
538     ctx.rotate(Math.PI/2);
539     \endcode
540     \image qml-item-canvas-rotate.png
541
542     The rotation transformation matrix is as follows:
543
544     \image qml-item-canvas-math-rotate.png
545
546     where the \c angle of rotation is in radians.
547
548 */
549 static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args)
550 {
551     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
552     CHECK_CONTEXT(r)
553
554     if (args.Length() == 1)
555         r->context->rotate(args[0]->NumberValue());
556     return args.This();
557 }
558
559 /*!
560     \qmlmethod object QtQuick2::Context2D::scale(real x, real y)
561     Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors
562     to the current tranform matrix.
563     Where \c x is the scale factor in the horizontal direction and \c y is the scale factor in the
564     vertical direction.
565     The following code doubles the horizontal size of an object drawn on the canvas and half its
566     vertical size:
567     \code
568     ctx.scale(2.0, 0.5);
569     \endcode
570     \image qml-item-canvas-scale.png
571
572 */
573 static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args)
574 {
575     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
576     CHECK_CONTEXT(r)
577
578
579     if (args.Length() == 2)
580         r->context->scale(args[0]->NumberValue(), args[1]->NumberValue());
581     return args.This();
582 }
583
584 /*!
585     \qmlmethod object QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f)
586     Changes the transformation matrix to the matrix given by the arguments as described below.
587
588     Modifying the transformation matrix directly enables you to perform scaling,
589     rotating, and translating transformations in a single step.
590
591     Each point on the canvas is multiplied by the matrix before anything is
592     drawn. The \l{HTML5 Canvas API} defines the transformation matrix as:
593
594     \image qml-item-canvas-math.png
595     where:
596     \list
597     \li \c{a} is the scale factor in the horizontal (x) direction
598     \image qml-item-canvas-scalex.png
599     \li \c{c} is the skew factor in the x direction
600     \image qml-item-canvas-canvas-skewx.png
601     \li \c{e} is the translation in the x direction
602     \image qml-item-canvas-canvas-translate.png
603     \li \c{b} is the skew factor in the y (vertical) direction
604     \image qml-item-canvas-canvas-skewy.png
605     \li \c{d} is the scale factor in the y direction
606     \image qml-item-canvas-canvas-scaley.png
607     \li \c{f} is the translation in the y direction
608     \image qml-item-canvas-canvas-translatey.png
609     \li the last row remains constant
610     \endlist
611     The scale factors and skew factors are multiples; \c{e} and \c{f} are
612     coordinate space units, just like the units in the translate(x,y)
613     method.
614
615     \sa transform()
616 */
617 static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
618 {
619     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
620     CHECK_CONTEXT(r)
621
622
623     if (args.Length() == 6)
624         r->context->setTransform( args[0]->NumberValue()
625                                                         , args[1]->NumberValue()
626                                                         , args[2]->NumberValue()
627                                                         , args[3]->NumberValue()
628                                                         , args[4]->NumberValue()
629                                                         , args[5]->NumberValue());
630
631     return args.This();
632 }
633
634 /*!
635     \qmlmethod object QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f)
636     This method is very similar to setTransform(), but instead of replacing the old
637     tranform matrix, this method applies the given tranform matrix to the current matrix by mulitplying to it.
638
639     The setTransform(a, b, c, d, e, f) method actually resets the current transform to the identity matrix,
640     and then invokes the transform(a, b, c, d, e, f) method with the same arguments.
641
642     \sa setTransform()
643 */
644 static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
645 {
646     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
647     CHECK_CONTEXT(r)
648
649
650     if (args.Length() == 6)
651         r->context->transform( args[0]->NumberValue()
652                                                   , args[1]->NumberValue()
653                                                   , args[2]->NumberValue()
654                                                   , args[3]->NumberValue()
655                                                   , args[4]->NumberValue()
656                                                   , args[5]->NumberValue());
657
658     return args.This();
659 }
660
661 /*!
662     \qmlmethod object QtQuick2::Context2D::translate(real x, real y)
663     Translates the origin of the canvas to point (\c x, \c y).
664
665     \c x is the horizontal distance that the origin is translated, in coordinate space units,
666     \c y is the vertical distance that the origin is translated, in coordinate space units.
667     Translating the origin enables you to draw patterns of different objects on the canvas
668     without having to measure the coordinates manually for each shape.
669 */
670 static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
671 {
672     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
673     CHECK_CONTEXT(r)
674
675
676     if (args.Length() == 2)
677             r->context->translate(args[0]->NumberValue(), args[1]->NumberValue());
678     return args.This();
679 }
680
681
682 /*!
683     \qmlmethod object QtQuick2::Context2D::resetTransform()
684     Reset the transformation matrix to default value.
685
686     \sa transform(), setTransform(), reset()
687 */
688 static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
689 {
690     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
691     CHECK_CONTEXT(r)
692
693     r->context->setTransform(1, 0, 0, 1, 0, 0);
694
695     return args.This();
696 }
697
698
699 /*!
700     \qmlmethod object QtQuick2::Context2D::shear(real sh, real sv )
701     Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction.
702 */
703 static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args)
704 {
705     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
706     CHECK_CONTEXT(r)
707
708     if (args.Length() == 2)
709             r->context->shear(args[0]->NumberValue(), args[1]->NumberValue());
710
711     return args.This();
712 }
713 // compositing
714
715 /*!
716     \qmlproperty real QtQuick2::Context2D::globalAlpha
717      Holds the the current alpha value applied to rendering operations.
718      The value must be in the range from 0.0 (fully transparent) to 1.0 (fully opque).
719      The default value is 1.0.
720 */
721 static v8::Handle<v8::Value> ctx2d_globalAlpha(v8::Local<v8::String>, const v8::AccessorInfo &info)
722 {
723     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
724     CHECK_CONTEXT(r)
725
726     return v8::Number::New(r->context->state.globalAlpha);
727 }
728
729 static void ctx2d_globalAlpha_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
730 {
731     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
732     CHECK_CONTEXT_SETTER(r)
733
734     qreal globalAlpha = value->NumberValue();
735
736     if (!qIsFinite(globalAlpha))
737         return;
738
739     if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) {
740         r->context->state.globalAlpha = globalAlpha;
741         r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha);
742     }
743 }
744
745 /*!
746     \qmlproperty string QtQuick2::Context2D::globalCompositeOperation
747      Holds the the current the current composition operation, from the list below:
748      \list
749      \li source-atop      - A atop B. Display the source image wherever both images are opaque.
750                            Display the destination image wherever the destination image is opaque but the source image is transparent.
751                            Display transparency elsewhere.
752      \li source-in        - A in B. Display the source image wherever both the source image and destination image are opaque.
753                            Display transparency elsewhere.
754      \li source-out       - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
755                            Display transparency elsewhere.
756      \li source-over      - (default) A over B. Display the source image wherever the source image is opaque.
757                            Display the destination image elsewhere.
758      \li destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
759      \li destination-in   - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
760      \li destination-out  - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
761      \li destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
762      \li lighter          - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
763      \li copy             - A (B is ignored). Display the source image instead of the destination image.
764      \li xor              - A xor B. Exclusive OR of the source image and destination image.
765      \endlist
766
767      Additionally, this property also accepts the compositon modes listed in QPainter::CompositionMode. According to the W3C standard, these
768      extension composition modes are provided as "vendorName-operationName" syntax, for example: QPainter::CompositionMode_Exclusion is provided as
769      "qt-exclusion".
770 */
771 static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
772 {
773     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
774     CHECK_CONTEXT(r)
775
776
777     QV8Engine *engine = V8ENGINE_ACCESSOR();
778
779     return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation));
780 }
781
782 static void ctx2d_globalCompositeOperation_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
783 {
784     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
785     CHECK_CONTEXT_SETTER(r)
786
787     QV8Engine *engine = V8ENGINE_ACCESSOR();
788
789
790     QString mode = engine->toString(value);
791     QPainter::CompositionMode cm = qt_composite_mode_from_string(mode);
792     if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over"))
793         return;
794
795     if (cm != r->context->state.globalCompositeOperation) {
796         r->context->state.globalCompositeOperation = cm;
797         r->context->buffer()->setGlobalCompositeOperation(cm);
798     }
799 }
800
801 // colors and styles
802 /*!
803     \qmlproperty variant QtQuick2::Context2D::fillStyle
804      Holds the current style used for filling shapes.
805      The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored.
806      This property accepts several color syntaxes:
807      \list
808      \li 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)'
809      \li 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)'
810      \li 'hsl(hue, saturation, lightness)'
811      \li 'hsla(hue, saturation, lightness, alpha)'
812      \li '#RRGGBB' - for example: '#00FFCC'
813      \li Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0)
814      \endlist
815      If the \c fillStyle or \l strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the
816      best performance, because it's already a valid QColor value, does not need to be parsed everytime.
817
818      The default value is  '#000000'.
819      \sa createLinearGradient()
820      \sa createRadialGradient()
821      \sa createPattern()
822      \sa strokeStyle
823  */
824 static v8::Handle<v8::Value> ctx2d_fillStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
825 {
826     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
827     CHECK_CONTEXT(r)
828
829     QV8Engine *engine = V8ENGINE_ACCESSOR();
830
831     QColor color = r->context->state.fillStyle.color();
832     if (color.isValid()) {
833         if (color.alpha() == 255)
834             return engine->toString(color.name());
835         QString alphaString = QString::number(color.alphaF(), 'f');
836         while (alphaString.endsWith(QLatin1Char('0')))
837             alphaString.chop(1);
838         if (alphaString.endsWith(QLatin1Char('.')))
839             alphaString += QLatin1Char('0');
840         return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
841     }
842     return r->context->m_fillStyle;
843 }
844
845 static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
846 {
847     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
848     CHECK_CONTEXT_SETTER(r)
849
850     QV8Engine *engine = V8ENGINE_ACCESSOR();
851
852    if (value->IsObject()) {
853        QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
854        if (color.isValid()) {
855            r->context->state.fillStyle = color;
856            r->context->buffer()->setFillStyle(color);
857            r->context->m_fillStyle = value;
858        } else {
859            QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
860            if (style && style->brush != r->context->state.fillStyle) {
861                r->context->state.fillStyle = style->brush;
862                r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
863                r->context->m_fillStyle = value;
864                r->context->state.fillPatternRepeatX = style->patternRepeatX;
865                r->context->state.fillPatternRepeatY = style->patternRepeatY;
866            }
867        }
868    } else if (value->IsString()) {
869        QColor color = qt_color_from_string(value);
870        if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
871             r->context->state.fillStyle = QBrush(color);
872             r->context->buffer()->setFillStyle(r->context->state.fillStyle);
873             r->context->m_fillStyle = value;
874        }
875    }
876 }
877 /*!
878     \qmlproperty enumeration QtQuick2::Context2D::fillRule
879      Holds the current fill rule used for filling shapes. The following fill rules supported:
880      \list
881      \li Qt.OddEvenFill
882      \li Qt.WindingFill
883      \endlist
884      Note: Unlike the QPainterPath, the Canvas API uses the winding fill as the default fill rule.
885      The fillRule property is part of the context rendering state.
886
887      \sa fillStyle
888  */
889 static v8::Handle<v8::Value> ctx2d_fillRule(v8::Local<v8::String>, const v8::AccessorInfo &info)
890 {
891     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
892     CHECK_CONTEXT(r)
893     QV8Engine *engine = V8ENGINE_ACCESSOR();
894
895     return engine->fromVariant(r->context->state.fillRule);
896 }
897
898 static void ctx2d_fillRule_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
899 {
900     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
901     CHECK_CONTEXT_SETTER(r)
902
903     QV8Engine *engine = V8ENGINE_ACCESSOR();
904
905     if ((value->IsString() && engine->toString(value) == QStringLiteral("WindingFill"))
906       ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) {
907         r->context->state.fillRule = Qt::WindingFill;
908     } else if ((value->IsString() && engine->toString(value) == QStringLiteral("OddEvenFill"))
909                ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) {
910         r->context->state.fillRule = Qt::OddEvenFill;
911     } else {
912         //error
913     }
914     r->context->m_path.setFillRule(r->context->state.fillRule);
915 }
916 /*!
917     \qmlproperty variant QtQuick2::Context2D::strokeStyle
918      Holds the current color or style to use for the lines around shapes,
919      The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object.
920      Invalid values are ignored.
921
922      The default value is  '#000000'.
923
924      \sa createLinearGradient()
925      \sa createRadialGradient()
926      \sa createPattern()
927      \sa fillStyle
928  */
929 v8::Handle<v8::Value> ctx2d_strokeStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
930 {
931     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
932     CHECK_CONTEXT(r)
933
934     QV8Engine *engine = V8ENGINE_ACCESSOR();
935
936     QColor color = r->context->state.strokeStyle.color();
937     if (color.isValid()) {
938         if (color.alpha() == 255)
939             return engine->toString(color.name());
940         QString alphaString = QString::number(color.alphaF(), 'f');
941         while (alphaString.endsWith(QLatin1Char('0')))
942             alphaString.chop(1);
943         if (alphaString.endsWith(QLatin1Char('.')))
944             alphaString += QLatin1Char('0');
945         return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
946     }
947     return r->context->m_strokeStyle;
948 }
949
950 static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
951 {
952     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
953     CHECK_CONTEXT_SETTER(r)
954
955     QV8Engine *engine = V8ENGINE_ACCESSOR();
956
957     if (value->IsObject()) {
958         QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
959         if (color.isValid()) {
960             r->context->state.fillStyle = color;
961             r->context->buffer()->setStrokeStyle(color);
962             r->context->m_strokeStyle = value;
963         } else {
964             QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
965             if (style && style->brush != r->context->state.strokeStyle) {
966                 r->context->state.strokeStyle = style->brush;
967                 r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
968                 r->context->m_strokeStyle = value;
969                 r->context->state.strokePatternRepeatX = style->patternRepeatX;
970                 r->context->state.strokePatternRepeatY = style->patternRepeatY;
971
972             }
973         }
974     } else if (value->IsString()) {
975         QColor color = qt_color_from_string(value);
976         if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
977              r->context->state.strokeStyle = QBrush(color);
978              r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
979              r->context->m_strokeStyle = value;
980         }
981     }
982 }
983
984 /*!
985   \qmlmethod object QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
986    Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between
987    the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
988
989    A gradient is a smooth transition between colors. There are two types of gradients: linear and radial.
990    Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between
991    to the gradient's starting and end points or circles.
992
993     \sa CanvasGradient::addColorStop()
994     \sa createRadialGradient()
995     \sa createConicalGradient()
996     \sa createPattern()
997     \sa fillStyle
998     \sa strokeStyle
999   */
1000
1001 static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &args)
1002 {
1003     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1004     CHECK_CONTEXT(r)
1005
1006
1007     QV8Engine *engine = V8ENGINE();
1008
1009     if (args.Length() == 4) {
1010         QQuickContext2DEngineData *ed = engineData(engine);
1011         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1012         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1013         qreal x0 = args[0]->NumberValue();
1014         qreal y0 = args[1]->NumberValue();
1015         qreal x1 = args[2]->NumberValue();
1016         qreal y1 = args[3]->NumberValue();
1017
1018         if (!qIsFinite(x0)
1019          || !qIsFinite(y0)
1020          || !qIsFinite(x1)
1021          || !qIsFinite(y1)) {
1022             delete r;
1023             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
1024         }
1025
1026         r->brush = QLinearGradient(x0, y0, x1, y1);
1027         gradient->SetExternalResource(r);
1028         return gradient;
1029     }
1030
1031     return args.This();
1032 }
1033
1034 /*!
1035   \qmlmethod object QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
1036    Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with
1037    origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1.
1038
1039     \sa CanvasGradient::addColorStop()
1040     \sa createLinearGradient()
1041     \sa createConicalGradient()
1042     \sa createPattern()
1043     \sa fillStyle
1044     \sa strokeStyle
1045   */
1046
1047 static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &args)
1048 {
1049     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1050     CHECK_CONTEXT(r)
1051
1052
1053     QV8Engine *engine = V8ENGINE();
1054
1055     if (args.Length() == 6) {
1056         QQuickContext2DEngineData *ed = engineData(engine);
1057         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1058         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1059
1060         qreal x0 = args[0]->NumberValue();
1061         qreal y0 = args[1]->NumberValue();
1062         qreal r0 = args[2]->NumberValue();
1063         qreal x1 = args[3]->NumberValue();
1064         qreal y1 = args[4]->NumberValue();
1065         qreal r1 = args[5]->NumberValue();
1066
1067         if (!qIsFinite(x0)
1068          || !qIsFinite(y0)
1069          || !qIsFinite(x1)
1070          || !qIsFinite(r0)
1071          || !qIsFinite(r1)
1072          || !qIsFinite(y1)) {
1073             delete r;
1074             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
1075         }
1076
1077         if (r0 < 0 || r1 < 0)
1078             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
1079
1080
1081         r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
1082         gradient->SetExternalResource(r);
1083         return gradient;
1084     }
1085
1086     return args.This();
1087 }
1088
1089 /*!
1090   \qmlmethod object QtQuick2::Context2D::createConicalGradient(real x, real y, real angle)
1091    Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y)
1092    with start angle \c angle in units of radians.
1093
1094     \sa CanvasGradient::addColorStop()
1095     \sa createLinearGradient()
1096     \sa createRadialGradient()
1097     \sa createPattern()
1098     \sa fillStyle
1099     \sa strokeStyle
1100   */
1101
1102 static v8::Handle<v8::Value> ctx2d_createConicalGradient(const v8::Arguments &args)
1103 {
1104     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1105     CHECK_CONTEXT(r)
1106
1107
1108     QV8Engine *engine = V8ENGINE();
1109
1110     if (args.Length() == 6) {
1111         QQuickContext2DEngineData *ed = engineData(engine);
1112         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1113         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1114
1115         qreal x = args[0]->NumberValue();
1116         qreal y = args[1]->NumberValue();
1117         qreal angle = DEGREES(args[2]->NumberValue());
1118         if (!qIsFinite(x) || !qIsFinite(y)) {
1119             delete r;
1120             V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
1121         }
1122
1123         if (!qIsFinite(angle)) {
1124             delete r;
1125             V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
1126         }
1127
1128         r->brush = QConicalGradient(x, y, angle);
1129         gradient->SetExternalResource(r);
1130         return gradient;
1131     }
1132
1133     return args.This();
1134 }
1135 /*!
1136   \qmlmethod variant QtQuick2::Context2D::createPattern(color color, enumeration patternMode)
1137   This is a overload function.
1138   Returns a CanvasPattern object that uses the given \a color and \a patternMode.
1139   The valid pattern modes are:
1140     \list
1141     \li Qt.SolidPattern
1142     \li Qt.Dense1Pattern
1143     \li Qt.Dense2Pattern
1144     \li Qt.Dense3Pattern
1145     \li Qt.Dense4Pattern
1146     \li Qt.Dense5Pattern
1147     \li Qt.Dense6Pattern
1148     \li Qt.Dense7Pattern
1149     \li Qt.HorPattern
1150     \li Qt.VerPattern
1151     \li Qt.CrossPattern
1152     \li Qt.BDiagPattern
1153     \li Qt.FDiagPattern
1154     \li Qt.DiagCrossPattern
1155 \endlist
1156     \sa Qt::BrushStyle
1157  */
1158 /*!
1159   \qmlmethod variant QtQuick2::Context2D::createPattern(Image image, string repetition)
1160   Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
1161
1162   The \a image parameter must be a valid Image item, a valid CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception.
1163
1164   The allowed values for \a repetition are:
1165
1166   \list
1167   \li "repeat"    - both directions
1168   \li "repeat-x   - horizontal only
1169   \li "repeat-y"  - vertical only
1170   \li "no-repeat" - neither
1171   \endlist
1172
1173   If the repetition argument is empty or null, the value "repeat" is used.
1174
1175   \sa strokeStyle
1176   \sa fillStyle
1177   */
1178 static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
1179 {
1180     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1181     CHECK_CONTEXT(r)
1182
1183
1184     QV8Engine *engine = V8ENGINE();
1185
1186     if (args.Length() == 2) {
1187         QQuickContext2DEngineData *ed = engineData(engine);
1188         QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine);
1189
1190         QColor color = engine->toVariant(args[0], qMetaTypeId<QColor>()).value<QColor>();
1191         if (color.isValid()) {
1192             int patternMode = args[1]->IntegerValue();
1193             Qt::BrushStyle style = Qt::SolidPattern;
1194             if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
1195                 style = static_cast<Qt::BrushStyle>(patternMode);
1196             }
1197             styleResouce->brush = QBrush(color, style);
1198         } else {
1199             QImage patternTexture;
1200
1201             if (args[0]->IsObject()) {
1202                 QV8Context2DPixelArrayResource *pixelData = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject());
1203                 if (pixelData) {
1204                     patternTexture = pixelData->image;
1205                 }
1206             } else {
1207                 patternTexture = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
1208             }
1209
1210             if (!patternTexture.isNull()) {
1211                 styleResouce->brush.setTextureImage(patternTexture);
1212
1213                 QString repetition = engine->toString(args[1]);
1214                 if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) {
1215                     styleResouce->patternRepeatX = true;
1216                     styleResouce->patternRepeatY = true;
1217                 } else if (repetition == QStringLiteral("repeat-x")) {
1218                     styleResouce->patternRepeatX = true;
1219                 } else if (repetition == QStringLiteral("repeat-y")) {
1220                     styleResouce->patternRepeatY = true;
1221                 } else if (repetition == QStringLiteral("no-repeat")) {
1222                     styleResouce->patternRepeatY = false;
1223                     styleResouce->patternRepeatY = false;
1224                 } else {
1225                     //TODO: exception: SYNTAX_ERR
1226                 }
1227
1228             }
1229         }
1230
1231         v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
1232         pattern->SetExternalResource(styleResouce);
1233         return pattern;
1234
1235     }
1236     return v8::Undefined();
1237 }
1238
1239 // line styles
1240 /*!
1241     \qmlproperty string QtQuick2::Context2D::lineCap
1242      Holds the the current line cap style.
1243      The possible line cap styles are:
1244     \list
1245     \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.
1246     \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.
1247     \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.
1248     \endlist
1249     Other values are ignored.
1250 */
1251 v8::Handle<v8::Value> ctx2d_lineCap(v8::Local<v8::String>, const v8::AccessorInfo &info)
1252 {
1253     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1254     CHECK_CONTEXT(r)
1255
1256
1257     QV8Engine *engine = V8ENGINE_ACCESSOR();
1258     switch (r->context->state.lineCap) {
1259     case Qt::RoundCap:
1260         return engine->toString(QLatin1String("round"));
1261     case Qt::FlatCap:
1262         return engine->toString(QLatin1String("butt"));
1263     case Qt::SquareCap:
1264         return engine->toString(QLatin1String("square"));
1265     default:
1266         break;
1267     }
1268     return engine->toString(QLatin1String("butt"));;
1269 }
1270
1271 static void ctx2d_lineCap_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1272 {
1273     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1274     CHECK_CONTEXT_SETTER(r)
1275
1276     QV8Engine *engine = V8ENGINE_ACCESSOR();
1277
1278     QString lineCap = engine->toString(value);
1279     Qt::PenCapStyle cap;
1280     if (lineCap == QLatin1String("round"))
1281         cap = Qt::RoundCap;
1282     else if (lineCap == QLatin1String("butt"))
1283         cap = Qt::FlatCap;
1284     else if (lineCap == QLatin1String("square"))
1285         cap = Qt::SquareCap;
1286     else
1287         return;
1288
1289     if (cap != r->context->state.lineCap) {
1290         r->context->state.lineCap = cap;
1291         r->context->buffer()->setLineCap(cap);
1292     }
1293 }
1294
1295 /*!
1296     \qmlproperty string QtQuick2::Context2D::lineJoin
1297      Holds the the current line join style. A join exists at any point in a subpath
1298      shared by two consecutive lines. When a subpath is closed, then a join also exists
1299      at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
1300
1301     The possible line join styles are:
1302     \list
1303     \li bevel - this is all that is rendered at joins.
1304     \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.
1305     \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.
1306     \endlist
1307     Other values are ignored.
1308 */
1309 v8::Handle<v8::Value> ctx2d_lineJoin(v8::Local<v8::String>, const v8::AccessorInfo &info)
1310 {
1311     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1312     CHECK_CONTEXT(r)
1313
1314
1315     QV8Engine *engine = V8ENGINE_ACCESSOR();
1316     switch (r->context->state.lineJoin) {
1317     case Qt::RoundJoin:
1318         return engine->toString(QLatin1String("round"));
1319     case Qt::BevelJoin:
1320         return engine->toString(QLatin1String("bevel"));
1321     case Qt::MiterJoin:
1322         return engine->toString(QLatin1String("miter"));
1323     default:
1324         break;
1325     }
1326     return engine->toString(QLatin1String("miter"));
1327 }
1328
1329 static void ctx2d_lineJoin_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1330 {
1331     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1332     CHECK_CONTEXT_SETTER(r)
1333
1334     QV8Engine *engine = V8ENGINE_ACCESSOR();
1335
1336     QString lineJoin = engine->toString(value);
1337     Qt::PenJoinStyle join;
1338     if (lineJoin == QLatin1String("round"))
1339         join = Qt::RoundJoin;
1340     else if (lineJoin == QLatin1String("bevel"))
1341         join = Qt::BevelJoin;
1342     else if (lineJoin == QLatin1String("miter"))
1343         join = Qt::SvgMiterJoin;
1344     else
1345         return;
1346
1347     if (join != r->context->state.lineJoin) {
1348         r->context->state.lineJoin = join;
1349         r->context->buffer()->setLineJoin(join);
1350     }
1351 }
1352
1353 /*!
1354     \qmlproperty real QtQuick2::Context2D::lineWidth
1355      Holds the the current line width. Values that are not finite values greater than zero are ignored.
1356  */
1357 v8::Handle<v8::Value> ctx2d_lineWidth(v8::Local<v8::String>, const v8::AccessorInfo &info)
1358 {
1359     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1360     CHECK_CONTEXT(r)
1361
1362
1363     return v8::Number::New(r->context->state.lineWidth);
1364 }
1365
1366 static void ctx2d_lineWidth_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1367 {
1368     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1369     CHECK_CONTEXT_SETTER(r)
1370
1371     qreal w = value->NumberValue();
1372
1373     if (w > 0 && qIsFinite(w) && w != r->context->state.lineWidth) {
1374         r->context->state.lineWidth = w;
1375         r->context->buffer()->setLineWidth(w);
1376     }
1377 }
1378
1379 /*!
1380     \qmlproperty real QtQuick2::Context2D::miterLimit
1381      Holds the current miter limit ratio.
1382      The default miter limit value is 10.0.
1383  */
1384 v8::Handle<v8::Value> ctx2d_miterLimit(v8::Local<v8::String>, const v8::AccessorInfo &info)
1385 {
1386     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1387     CHECK_CONTEXT(r)
1388
1389
1390     return v8::Number::New(r->context->state.miterLimit);
1391 }
1392
1393 static void ctx2d_miterLimit_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1394 {
1395     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1396     CHECK_CONTEXT_SETTER(r)
1397
1398     qreal ml = value->NumberValue();
1399
1400     if (ml > 0 && qIsFinite(ml) && ml != r->context->state.miterLimit) {
1401         r->context->state.miterLimit = ml;
1402         r->context->buffer()->setMiterLimit(ml);
1403     }
1404 }
1405
1406 // shadows
1407 /*!
1408     \qmlproperty real QtQuick2::Context2D::shadowBlur
1409      Holds the current level of blur applied to shadows
1410  */
1411 v8::Handle<v8::Value> ctx2d_shadowBlur(v8::Local<v8::String>, const v8::AccessorInfo &info)
1412 {
1413     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1414     CHECK_CONTEXT(r)
1415
1416
1417     return v8::Number::New(r->context->state.shadowBlur);
1418 }
1419
1420 static void ctx2d_shadowBlur_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1421 {
1422     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1423     CHECK_CONTEXT_SETTER(r)
1424     qreal blur = value->NumberValue();
1425
1426     if (blur > 0 && qIsFinite(blur) && blur != r->context->state.shadowBlur) {
1427         r->context->state.shadowBlur = blur;
1428         r->context->buffer()->setShadowBlur(blur);
1429     }
1430 }
1431
1432 /*!
1433     \qmlproperty string QtQuick2::Context2D::shadowColor
1434      Holds the current shadow color.
1435  */
1436 v8::Handle<v8::Value> ctx2d_shadowColor(v8::Local<v8::String>, const v8::AccessorInfo &info)
1437 {
1438     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1439     CHECK_CONTEXT(r)
1440
1441
1442     QV8Engine *engine = V8ENGINE_ACCESSOR();
1443
1444     return engine->toString(r->context->state.shadowColor.name());
1445 }
1446
1447 static void ctx2d_shadowColor_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1448 {
1449     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1450     CHECK_CONTEXT_SETTER(r)
1451
1452     QColor color = qt_color_from_string(value);
1453
1454     if (color.isValid() && color != r->context->state.shadowColor) {
1455         r->context->state.shadowColor = color;
1456         r->context->buffer()->setShadowColor(color);
1457     }
1458 }
1459
1460
1461 /*!
1462     \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX
1463      Holds the current shadow offset in the positive horizontal distance.
1464
1465      \sa shadowOffsetY
1466  */
1467 v8::Handle<v8::Value> ctx2d_shadowOffsetX(v8::Local<v8::String>, const v8::AccessorInfo &info)
1468 {
1469     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1470     CHECK_CONTEXT(r)
1471
1472
1473     return v8::Number::New(r->context->state.shadowOffsetX);
1474 }
1475
1476 static void ctx2d_shadowOffsetX_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1477 {
1478     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1479     CHECK_CONTEXT_SETTER(r)
1480
1481     qreal offsetX = value->NumberValue();
1482     if (qIsFinite(offsetX) && offsetX != r->context->state.shadowOffsetX) {
1483         r->context->state.shadowOffsetX = offsetX;
1484         r->context->buffer()->setShadowOffsetX(offsetX);
1485     }
1486 }
1487 /*!
1488     \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY
1489      Holds the current shadow offset in the positive vertical distance.
1490
1491      \sa shadowOffsetX
1492  */
1493 v8::Handle<v8::Value> ctx2d_shadowOffsetY(v8::Local<v8::String>, const v8::AccessorInfo &info)
1494 {
1495     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1496     CHECK_CONTEXT(r)
1497
1498
1499     return v8::Number::New(r->context->state.shadowOffsetY);
1500 }
1501
1502 static void ctx2d_shadowOffsetY_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1503 {
1504     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1505     CHECK_CONTEXT_SETTER(r)
1506
1507     qreal offsetY = value->NumberValue();
1508     if (qIsFinite(offsetY) && offsetY != r->context->state.shadowOffsetY) {
1509         r->context->state.shadowOffsetY = offsetY;
1510         r->context->buffer()->setShadowOffsetY(offsetY);
1511     }
1512 }
1513
1514 v8::Handle<v8::Value> ctx2d_path(v8::Local<v8::String>, const v8::AccessorInfo &info)
1515 {
1516     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1517     CHECK_CONTEXT(r)
1518     return r->context->m_v8path;
1519 }
1520
1521 static void ctx2d_path_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1522 {
1523     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1524     CHECK_CONTEXT_SETTER(r)
1525     QV8Engine *engine = V8ENGINE_ACCESSOR();
1526
1527     r->context->beginPath();
1528     if (value->IsObject()) {
1529         QQuickPath* path = qobject_cast<QQuickPath*>(engine->toQObject(value));
1530         if (path)
1531             r->context->m_path = path->path();
1532     } else {
1533         QString path = engine->toString(value->ToString());
1534         QQuickSvgParser::parsePathDataFast(path, r->context->m_path);
1535     }
1536     r->context->m_v8path = value;
1537 }
1538
1539 //rects
1540 /*!
1541   \qmlmethod object QtQuick2::Context2D::clearRect(real x, real y, real w, real h)
1542   Clears all pixels on the canvas in the given rectangle to transparent black.
1543   */
1544 static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
1545 {
1546     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1547     CHECK_CONTEXT(r)
1548
1549
1550     if (args.Length() == 4)
1551         r->context->clearRect(args[0]->NumberValue(),
1552                               args[1]->NumberValue(),
1553                               args[2]->NumberValue(),
1554                               args[3]->NumberValue());
1555
1556     return args.This();
1557 }
1558 /*!
1559   \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1560    Paint the specified rectangular area using the fillStyle.
1561
1562    \sa fillStyle
1563   */
1564 static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
1565 {
1566     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1567     CHECK_CONTEXT(r)
1568
1569     if (args.Length() == 4)
1570         r->context->fillRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1571     return args.This();
1572 }
1573
1574 /*!
1575   \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1576    Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin,
1577    and (if appropriate) miterLimit attributes.
1578
1579    \sa strokeStyle
1580    \sa lineWidth
1581    \sa lineJoin
1582    \sa miterLimit
1583   */
1584 static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
1585 {
1586     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1587     CHECK_CONTEXT(r)
1588
1589     if (args.Length() == 4)
1590         r->context->strokeRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1591
1592     return args.This();
1593 }
1594
1595 // Complex shapes (paths) API
1596 /*!
1597   \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
1598   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.
1599   \image qml-item-canvas-arcTo2.png
1600   \sa arcTo,
1601       {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc}
1602   */
1603 static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
1604 {
1605     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1606     CHECK_CONTEXT(r)
1607
1608     if (args.Length() >= 5) {
1609         bool antiClockwise = false;
1610
1611         if (args.Length() == 6)
1612             antiClockwise = args[5]->BooleanValue();
1613
1614         qreal radius = args[2]->NumberValue();
1615
1616         if (qIsFinite(radius) && radius < 0)
1617            V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1618
1619         r->context->arc(args[0]->NumberValue(),
1620                         args[1]->NumberValue(),
1621                         radius,
1622                         args[3]->NumberValue(),
1623                         args[4]->NumberValue(),
1624                         antiClockwise);
1625     }
1626
1627     return args.This();
1628 }
1629
1630 /*!
1631   \qmlmethod object QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius)
1632
1633    Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line.
1634    To draw an arc, you begin with the same steps your followed to create a line:
1635     \list
1636     \li Call the context.beginPath() method to set a new path.
1637     \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).
1638     \li To draw an arc or circle, call the context.arcTo(\c x1, \c y1, \c x2, \c y2,\c radius) method.
1639        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
1640        it to the previous subpath by a straight line.
1641     \endlist
1642     \image qml-item-canvas-arcTo.png
1643     Both startAngle and endAngle are measured from the x axis in units of radians.
1644
1645     \image qml-item-canvas-startAngle.png
1646     The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction.
1647   \sa arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d
1648       context standard for arcTo}
1649   */
1650 static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
1651 {
1652     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1653     CHECK_CONTEXT(r)
1654
1655     if (args.Length() == 5) {
1656         qreal radius = args[4]->NumberValue();
1657
1658         if (qIsFinite(radius) && radius < 0)
1659            V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1660
1661         r->context->arcTo(args[0]->NumberValue(),
1662                           args[1]->NumberValue(),
1663                           args[2]->NumberValue(),
1664                           args[3]->NumberValue(),
1665                           radius);
1666     }
1667
1668     return args.This();
1669 }
1670
1671 /*!
1672   \qmlmethod object QtQuick2::Context2D::beginPath()
1673
1674    Resets the current path to a new path.
1675   */
1676 static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
1677 {
1678     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1679     CHECK_CONTEXT(r)
1680
1681
1682     r->context->beginPath();
1683
1684     return args.This();
1685 }
1686
1687 /*!
1688   \qmlmethod object QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
1689
1690   Adds a cubic bezier curve between the current position and the given endPoint using the control points specified by (\c cp1x, cp1y),
1691   and (\c cp2x, \c cp2y).
1692   After the curve is added, the current position is updated to be at the end point (\c x, \c y) of the curve.
1693   The following code produces the path shown below:
1694   \code
1695   ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
1696   ctx.lineWidth = 1;
1697   ctx.beginPath();
1698   ctx.moveTo(20, 0);//start point
1699   ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
1700   ctx.stroke();
1701   \endcode
1702    \image qml-item-canvas-bezierCurveTo.png
1703   \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
1704   \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
1705   */
1706 static v8::Handle<v8::Value> ctx2d_bezierCurveTo(const v8::Arguments &args)
1707 {
1708     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1709     CHECK_CONTEXT(r)
1710
1711
1712     if (args.Length() == 6) {
1713         qreal cp1x = args[0]->NumberValue();
1714         qreal cp1y = args[1]->NumberValue();
1715         qreal cp2x = args[2]->NumberValue();
1716         qreal cp2y = args[3]->NumberValue();
1717         qreal x = args[4]->NumberValue();
1718         qreal y = args[5]->NumberValue();
1719
1720         if (!qIsFinite(cp1x) || !qIsFinite(cp1y) || !qIsFinite(cp2x) || !qIsFinite(cp2y) || !qIsFinite(x) || !qIsFinite(y))
1721             return args.This();
1722
1723         r->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
1724     }
1725
1726     return args.This();
1727 }
1728
1729 /*!
1730   \qmlmethod object QtQuick2::Context2D::clip()
1731
1732    Creates the clipping region from the current path.
1733    Any parts of the shape outside the clipping path are not displayed.
1734    To create a complex shape using the \c clip() method:
1735
1736     \list 1
1737     \li Call the \c{context.beginPath()} method to set the clipping path.
1738     \li Define the clipping path by calling any combination of the \c{lineTo},
1739     \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
1740     \li Call the \c{context.clip()} method.
1741     \endlist
1742
1743     The new shape displays.  The following shows how a clipping path can
1744     modify how an image displays:
1745
1746     \image qml-canvas-clip-complex.png
1747     \sa beginPath()
1748     \sa closePath()
1749     \sa stroke()
1750     \sa fill()
1751    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
1752   */
1753 static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
1754 {
1755     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1756     CHECK_CONTEXT(r)
1757
1758     r->context->clip();
1759     return args.This();
1760 }
1761
1762 /*!
1763   \qmlmethod object QtQuick2::Context2D::closePath()
1764    Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
1765    The current point of the new path is the previous subpath's first point.
1766
1767    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
1768   */
1769 static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
1770 {
1771     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1772     CHECK_CONTEXT(r)
1773
1774
1775     r->context->closePath();
1776
1777     return args.This();
1778 }
1779
1780 /*!
1781   \qmlmethod object QtQuick2::Context2D::fill()
1782
1783    Fills the subpaths with the current fill style.
1784
1785    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
1786
1787    \sa fillStyle
1788   */
1789 static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args)
1790 {
1791     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1792     CHECK_CONTEXT(r);
1793     r->context->fill();
1794     return args.This();
1795 }
1796
1797 /*!
1798   \qmlmethod object QtQuick2::Context2D::lineTo(real x, real y)
1799
1800    Draws a line from the current position to the point (x, y).
1801  */
1802 static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
1803 {
1804     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1805     CHECK_CONTEXT(r)
1806
1807
1808     if (args.Length() == 2) {
1809         qreal x = args[0]->NumberValue();
1810         qreal y = args[1]->NumberValue();
1811
1812         if (!qIsFinite(x) || !qIsFinite(y))
1813             return args.This();
1814
1815         r->context->lineTo(x, y);
1816     }
1817
1818     return args.This();
1819 }
1820
1821 /*!
1822   \qmlmethod object QtQuick2::Context2D::moveTo(real x, real y)
1823
1824    Creates a new subpath with the given point.
1825  */
1826 static v8::Handle<v8::Value> ctx2d_moveTo(const v8::Arguments &args)
1827 {
1828     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1829     CHECK_CONTEXT(r)
1830
1831     if (args.Length() == 2) {
1832         qreal x = args[0]->NumberValue();
1833         qreal y = args[1]->NumberValue();
1834
1835         if (!qIsFinite(x) || !qIsFinite(y))
1836             return args.This();
1837         r->context->moveTo(x, y);
1838     }
1839     return args.This();
1840 }
1841
1842 /*!
1843   \qmlmethod object QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
1844
1845    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).
1846
1847    See \l{http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for  for quadraticCurveTo}
1848  */
1849 static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
1850 {
1851     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1852     CHECK_CONTEXT(r)
1853
1854     if (args.Length() == 4) {
1855         qreal cpx = args[0]->NumberValue();
1856         qreal cpy = args[1]->NumberValue();
1857         qreal x = args[2]->NumberValue();
1858         qreal y = args[3]->NumberValue();
1859
1860         if (!qIsFinite(cpx) || !qIsFinite(cpy) || !qIsFinite(x) || !qIsFinite(y))
1861             return args.This();
1862
1863         r->context->quadraticCurveTo(cpx, cpy, x, y);
1864     }
1865
1866     return args.This();
1867 }
1868
1869 /*!
1870   \qmlmethod object QtQuick2::Context2D::rect(real x, real y, real w, real h)
1871
1872    Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath.
1873  */
1874 static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
1875 {
1876     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1877     CHECK_CONTEXT(r)
1878
1879     if (args.Length() == 4)
1880         r->context->rect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1881     return args.This();
1882 }
1883
1884 /*!
1885   \qmlmethod object QtQuick2::Context2D::roundedRect(real x, real y, real w, real h,  real xRadius, real yRadius)
1886
1887    Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the
1888    ellipses defining the corners of the rounded rectangle.
1889  */
1890 static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args)
1891 {
1892     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1893     CHECK_CONTEXT(r)
1894
1895     if (args.Length() == 6)
1896         r->context->roundedRect(args[0]->NumberValue()
1897                               , args[1]->NumberValue()
1898                               , args[2]->NumberValue()
1899                               , args[3]->NumberValue()
1900                               , args[4]->NumberValue()
1901                               , args[5]->NumberValue());
1902     return args.This();
1903 }
1904
1905 /*!
1906   \qmlmethod object QtQuick2::Context2D::ellipse(real x, real y, real w, real h)
1907
1908   Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h,
1909   and adds it to the path as a closed subpath.
1910
1911   The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position).
1912  */
1913 static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args)
1914 {
1915     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1916     CHECK_CONTEXT(r)
1917
1918
1919     if (args.Length() == 4)
1920         r->context->ellipse(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1921
1922     return args.This();
1923 }
1924
1925 /*!
1926   \qmlmethod object QtQuick2::Context2D::text(string text, real x, real y)
1927
1928   Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied.
1929   The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y).
1930  */
1931 static v8::Handle<v8::Value> ctx2d_text(const v8::Arguments &args)
1932 {
1933     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1934     CHECK_CONTEXT(r)
1935
1936     QV8Engine *engine = V8ENGINE();
1937     if (args.Length() == 3) {
1938         qreal x = args[1]->NumberValue();
1939         qreal y = args[2]->NumberValue();
1940
1941         if (!qIsFinite(x) || !qIsFinite(y))
1942             return args.This();
1943         r->context->text(engine->toString(args[0]), x, y);
1944     }
1945     return args.This();
1946 }
1947
1948 /*!
1949   \qmlmethod object QtQuick2::Context2D::stroke()
1950
1951    Strokes the subpaths with the current stroke style.
1952
1953    See \l{http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
1954
1955    \sa strokeStyle
1956   */
1957 static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
1958 {
1959     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1960     CHECK_CONTEXT(r)
1961
1962     r->context->stroke();
1963     return args.This();
1964 }
1965
1966 /*!
1967   \qmlmethod object QtQuick2::Context2D::isPointInPath(real x, real y)
1968
1969    Returns true if the given point is in the current path.
1970
1971    \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
1972   */
1973 static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
1974 {
1975     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1976     CHECK_CONTEXT(r)
1977
1978     bool pointInPath = false;
1979     if (args.Length() == 2)
1980         pointInPath = r->context->isPointInPath(args[0]->NumberValue(), args[1]->NumberValue());
1981     return v8::Boolean::New(pointInPath);
1982 }
1983
1984 static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
1985 {
1986     Q_UNUSED(args);
1987
1988     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
1989 }
1990
1991 static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
1992 {
1993     Q_UNUSED(args);
1994
1995     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
1996 }
1997
1998 static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
1999 {
2000     Q_UNUSED(args);
2001
2002     V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
2003 }
2004 // text
2005 /*!
2006   \qmlproperty string QtQuick2::Context2D::font
2007   Holds the current font settings.
2008
2009   The default font value is "10px sans-serif".
2010   See \l {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
2011   */
2012 v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &info)
2013 {
2014     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2015     CHECK_CONTEXT(r)
2016
2017     QV8Engine *engine = V8ENGINE_ACCESSOR();
2018
2019     return engine->toString(r->context->state.font.toString());
2020 }
2021
2022 static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2023 {
2024     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2025     CHECK_CONTEXT_SETTER(r)
2026
2027     QV8Engine *engine = V8ENGINE_ACCESSOR();
2028     QString fs = engine->toString(value);
2029     QFont font = qt_font_from_string(fs);
2030     if (font != r->context->state.font) {
2031         r->context->state.font = font;
2032     }
2033 }
2034
2035 /*!
2036   \qmlproperty string QtQuick2::Context2D::textAlign
2037
2038   Holds the current text alignment settings.
2039   The possible values are:
2040   \list
2041     \li start
2042     \li end
2043     \li left
2044     \li right
2045     \li center
2046   \endlist
2047   Other values are ignored. The default value is "start".
2048   */
2049 v8::Handle<v8::Value> ctx2d_textAlign(v8::Local<v8::String>, const v8::AccessorInfo &info)
2050 {
2051     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2052     CHECK_CONTEXT(r)
2053     QV8Engine *engine = V8ENGINE_ACCESSOR();
2054     switch (r->context->state.textAlign) {
2055     case QQuickContext2D::Start:
2056         return engine->toString(QLatin1String("start"));
2057    case QQuickContext2D::End:
2058         return engine->toString(QLatin1String("end"));
2059    case QQuickContext2D::Left:
2060         return engine->toString(QLatin1String("left"));
2061    case QQuickContext2D::Right:
2062         return engine->toString(QLatin1String("right"));
2063    case QQuickContext2D::Center:
2064         return engine->toString(QLatin1String("center"));
2065     default:
2066         break;
2067     }
2068     return engine->toString(QLatin1String("start"));
2069 }
2070
2071 static void ctx2d_textAlign_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2072 {
2073     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2074     CHECK_CONTEXT_SETTER(r)
2075     QV8Engine *engine = V8ENGINE_ACCESSOR();
2076
2077     QString textAlign = engine->toString(value);
2078
2079     QQuickContext2D::TextAlignType ta;
2080     if (textAlign == QLatin1String("start"))
2081         ta = QQuickContext2D::Start;
2082     else if (textAlign == QLatin1String("end"))
2083         ta = QQuickContext2D::End;
2084     else if (textAlign == QLatin1String("left"))
2085         ta = QQuickContext2D::Left;
2086     else if (textAlign == QLatin1String("right"))
2087         ta = QQuickContext2D::Right;
2088     else if (textAlign == QLatin1String("center"))
2089         ta = QQuickContext2D::Center;
2090     else
2091         return;
2092
2093     if (ta != r->context->state.textAlign) {
2094         r->context->state.textAlign = ta;
2095     }
2096 }
2097
2098 /*!
2099   \qmlproperty string QtQuick2::Context2D::textBaseline
2100
2101   Holds the current baseline alignment settings.
2102   The possible values are:
2103   \list
2104     \li top
2105     \li hanging
2106     \li middle
2107     \li alphabetic
2108     \li ideographic
2109     \li bottom
2110   \endlist
2111   Other values are ignored. The default value is "alphabetic".
2112   */
2113 v8::Handle<v8::Value> ctx2d_textBaseline(v8::Local<v8::String>, const v8::AccessorInfo &info)
2114 {
2115     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2116     CHECK_CONTEXT(r)
2117
2118     QV8Engine *engine = V8ENGINE_ACCESSOR();
2119     switch (r->context->state.textBaseline) {
2120     case QQuickContext2D::Alphabetic:
2121         return engine->toString(QLatin1String("alphabetic"));
2122     case QQuickContext2D::Hanging:
2123         return engine->toString(QLatin1String("hanging"));
2124     case QQuickContext2D::Top:
2125         return engine->toString(QLatin1String("top"));
2126     case QQuickContext2D::Bottom:
2127         return engine->toString(QLatin1String("bottom"));
2128     case QQuickContext2D::Middle:
2129         return engine->toString(QLatin1String("middle"));
2130     default:
2131         break;
2132     }
2133     return engine->toString(QLatin1String("alphabetic"));
2134 }
2135
2136 static void ctx2d_textBaseline_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2137 {
2138     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2139     CHECK_CONTEXT_SETTER(r)
2140     QV8Engine *engine = V8ENGINE_ACCESSOR();
2141     QString textBaseline = engine->toString(value);
2142
2143     QQuickContext2D::TextBaseLineType tb;
2144     if (textBaseline == QLatin1String("alphabetic"))
2145         tb = QQuickContext2D::Alphabetic;
2146     else if (textBaseline == QLatin1String("hanging"))
2147         tb = QQuickContext2D::Hanging;
2148     else if (textBaseline == QLatin1String("top"))
2149         tb = QQuickContext2D::Top;
2150     else if (textBaseline == QLatin1String("bottom"))
2151         tb = QQuickContext2D::Bottom;
2152     else if (textBaseline == QLatin1String("middle"))
2153         tb = QQuickContext2D::Middle;
2154     else
2155         return;
2156
2157     if (tb != r->context->state.textBaseline) {
2158         r->context->state.textBaseline = tb;
2159     }
2160 }
2161
2162 /*!
2163   \qmlmethod object QtQuick2::Context2D::fillText(text, x, y)
2164   Fills the given text at the given position.
2165   \sa font
2166   \sa textAlign
2167   \sa textBaseline
2168   \sa strokeText
2169   */
2170 static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
2171 {
2172     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2173     CHECK_CONTEXT(r)
2174
2175     QV8Engine *engine = V8ENGINE();
2176     if (args.Length() == 3) {
2177         qreal x = args[1]->NumberValue();
2178         qreal y = args[2]->NumberValue();
2179         if (!qIsFinite(x) || !qIsFinite(y))
2180             return args.This();
2181         QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0]));
2182         r->context->buffer()->fill(textPath);
2183     }
2184     return args.This();
2185 }
2186 /*!
2187   \qmlmethod object QtQuick2::Context2D::strokeText(text, x, y)
2188   Strokes the given text at the given position.
2189   \sa font
2190   \sa textAlign
2191   \sa textBaseline
2192   \sa fillText
2193   */
2194 static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
2195 {
2196     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2197     CHECK_CONTEXT(r)
2198
2199     QV8Engine *engine = V8ENGINE();
2200     if (args.Length() == 3)
2201         r->context->drawText(engine->toString(args[0]), args[1]->NumberValue(), args[2]->NumberValue(), false);
2202     return args.This();
2203 }
2204
2205 /*!
2206   \qmltype 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 \l{http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details.
2214
2215     \sa Context2D::measureText
2216     \sa 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 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 Canvas::loadImage() method first.
2254   This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
2255
2256   \sa CanvasImageData
2257   \sa Image
2258   \sa Canvas::loadImage
2259   \sa Canvas::isImageLoaded
2260   \sa Canvas::onImageLoaded
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 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 Canvas::loadImage() method first.
2274   This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
2275
2276   \sa CanvasImageData
2277   \sa Image
2278   \sa Canvas::loadImage()
2279   \sa Canvas::isImageLoaded
2280   \sa Canvas::onImageLoaded
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 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 Canvas::loadImage() method first.
2295   This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
2296
2297   \sa CanvasImageData
2298   \sa Image
2299   \sa Canvas::loadImage()
2300   \sa Canvas::isImageLoaded
2301   \sa Canvas::onImageLoaded
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()->window()));
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->window()));
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     \qmltype CanvasImageData
2410     \inqmlmodule QtQuick 2
2411     \ingroup qtquick-canvas
2412     \brief Contains image pixel data in RGBA order
2413
2414      The CanvasImageData object holds the image pixel data.
2415
2416      The 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 width
2421      \sa height
2422      \sa data
2423      \sa Context2D::createImageData()
2424      \sa Context2D::getImageData()
2425      \sa Context2D::putImageData()
2426   */
2427 /*!
2428   \qmlproperty int 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 int 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 object 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     \qmltype 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 CanvasImageData
2470     \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
2471   */
2472
2473 /*!
2474   \qmlproperty int 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 CanvasImageData QtQuick2::Context2D::createImageData(real sw, real sh)
2542    Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
2543   */
2544 /*!
2545   \qmlmethod CanvasImageData QtQuick2::Context2D::createImageData(CanvasImageData imageData)
2546    Creates a CanvasImageData object with the same dimensions as the argument.
2547   */
2548 /*!
2549   \qmlmethod CanvasImageData QtQuick2::Context2D::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 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 CanvasImageData QtQuick2::Canvas::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(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     \qmltype 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 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 void QQuickContext2D::setGrabbedImage(const QImage& grab)
3268 {
3269     m_grabbedImage = grab;
3270     m_grabbed = true;
3271 }
3272
3273 QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
3274 {
3275     return m_canvas->loadedPixmap(url);
3276 }
3277
3278 QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
3279 {
3280     const QFontMetrics metrics(state.font);
3281     int yoffset = baseLineOffset(static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
3282     int xoffset = textAlignOffset(static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
3283
3284     QPainterPath textPath;
3285
3286     textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
3287     textPath = state.matrix.map(textPath);
3288     return textPath;
3289 }
3290
3291
3292 static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
3293 {
3294     // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
3295     return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x()));
3296 }
3297
3298 static inline bool withinRange(qreal p, qreal a, qreal b)
3299 {
3300     return (p >= a && p <= b) || (p >= b && p <= a);
3301 }
3302
3303 bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
3304 {
3305     if (!state.invertibleCTM)
3306         return false;
3307
3308     if (!m_path.elementCount())
3309         return false;
3310
3311     if (!qIsFinite(x) || !qIsFinite(y))
3312         return false;
3313
3314     QPointF point(x, y);
3315     QTransform ctm = state.matrix;
3316     QPointF p = ctm.inverted().map(point);
3317     if (!qIsFinite(p.x()) || !qIsFinite(p.y()))
3318         return false;
3319
3320     const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
3321
3322     bool contains = m_path.contains(p);
3323
3324     if (!contains) {
3325         // check whether the point is on the border
3326         QPolygonF border = m_path.toFillPolygon();
3327
3328         QPointF p1 = border.at(0);
3329         QPointF p2;
3330
3331         for (int i = 1; i < border.size(); ++i) {
3332             p2 = border.at(i);
3333             if (areCollinear(p, p1, p2)
3334                     // Once we know that the points are collinear we
3335                     // only need to check one of the coordinates
3336                     && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ?
3337                         withinRange(p.x(), p1.x(), p2.x()) :
3338                         withinRange(p.y(), p1.y(), p2.y()))) {
3339                 return true;
3340             }
3341             p1 = p2;
3342         }
3343     }
3344     return contains;
3345 }
3346
3347 QQuickContext2D::QQuickContext2D(QObject *parent)
3348     : QQuickCanvasContext(parent)
3349     , m_buffer(new QQuickContext2DCommandBuffer)
3350     , m_v8engine(0)
3351     , m_windowManager(0)
3352     , m_surface(0)
3353     , m_glContext(0)
3354     , m_thread(0)
3355     , m_grabbed(false)
3356 {
3357 }
3358
3359 QQuickContext2D::~QQuickContext2D()
3360 {
3361     delete m_buffer;
3362     m_texture->deleteLater();
3363 }
3364
3365 v8::Handle<v8::Object> QQuickContext2D::v8value() const
3366 {
3367     return m_v8value;
3368 }
3369
3370 QStringList QQuickContext2D::contextNames() const
3371 {
3372     return QStringList() << QLatin1String("2d");
3373 }
3374
3375 void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args)
3376 {
3377     Q_UNUSED(args);
3378
3379     m_canvas = canvasItem;
3380     m_renderTarget = canvasItem->renderTarget();
3381
3382     QQuickWindow *window = canvasItem->window();
3383     m_windowManager =  QQuickWindowPrivate::get(window)->windowManager;
3384     m_renderStrategy = canvasItem->renderStrategy();
3385
3386     switch (m_renderTarget) {
3387     case QQuickCanvasItem::Image:
3388         m_texture = new QQuickContext2DImageTexture;
3389         break;
3390     case QQuickCanvasItem::FramebufferObject:
3391         m_texture = new QQuickContext2DFBOTexture;
3392         break;
3393     }
3394
3395     m_texture->setItem(canvasItem);
3396     m_texture->setCanvasWindow(canvasItem->canvasWindow().toRect());
3397     m_texture->setTileSize(canvasItem->tileSize());
3398     m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
3399     m_texture->setSmooth(canvasItem->smooth());
3400
3401     m_thread = QThread::currentThread();
3402
3403     QThread *renderThread = m_thread;
3404     QThread *sceneGraphThread = window->openglContext() ? window->openglContext()->thread() : 0;
3405
3406     if (m_renderStrategy == QQuickCanvasItem::Threaded)
3407         renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
3408     else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
3409         renderThread = sceneGraphThread;
3410
3411     if (renderThread && renderThread != QThread::currentThread())
3412         m_texture->moveToThread(renderThread);
3413
3414     if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
3415          QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->glContext();
3416          m_surface = window;
3417          m_glContext = new QOpenGLContext;
3418          m_glContext->setFormat(cc->format());
3419          m_glContext->setShareContext(cc);
3420          if (renderThread != QThread::currentThread())
3421              m_glContext->moveToThread(renderThread);
3422     }
3423
3424     connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
3425
3426     reset();
3427 }
3428
3429 void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth)
3430 {
3431     QMetaObject::invokeMethod(m_texture,
3432                                                                "canvasChanged",
3433                                                                Qt::AutoConnection,
3434                                                                Q_ARG(QSize, canvasSize),
3435                                                                Q_ARG(QSize, tileSize),
3436                                                                Q_ARG(QRect, canvasWindow),
3437                                                                Q_ARG(QRect, dirtyRect),
3438                                                                Q_ARG(bool, smooth));
3439 }
3440
3441 void QQuickContext2D::flush()
3442 {
3443     if (m_buffer)
3444         QMetaObject::invokeMethod(m_texture,
3445                                                                    "paint",
3446                                                                    Qt::AutoConnection,
3447                                                                    Q_ARG(QQuickContext2DCommandBuffer*, m_buffer));
3448     m_buffer = new QQuickContext2DCommandBuffer();
3449 }
3450
3451 QSGDynamicTexture *QQuickContext2D::texture() const
3452 {
3453     return m_texture;
3454 }
3455
3456 QImage QQuickContext2D::toImage(const QRectF& bounds)
3457 {
3458     flush();
3459     if (m_texture->thread() == QThread::currentThread())
3460         m_texture->grabImage(bounds);
3461     else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
3462         qWarning() << "Pixel read back is not support in Cooperative mode, please try Theaded or Immediate mode";
3463         return QImage();
3464     } else {
3465         QMetaObject::invokeMethod(m_texture,
3466                                   "grabImage",
3467                                   Qt::BlockingQueuedConnection,
3468                                   Q_ARG(QRectF, bounds));
3469     }
3470     QImage img = m_grabbedImage;
3471     m_grabbedImage = QImage();
3472     m_grabbed = false;
3473     return img;
3474 }
3475
3476
3477 QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine)
3478 {
3479     v8::HandleScope handle_scope;
3480     v8::Context::Scope scope(engine->context());
3481
3482     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
3483     ft->InstanceTemplate()->SetHasExternalResource(true);
3484     ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine));
3485     ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine));
3486     ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine));
3487     ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine));
3488     ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine));
3489     ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine));
3490     ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine));
3491     ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine));
3492     ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine));
3493     ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine));
3494     ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine));
3495     ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine));
3496     ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine));
3497     ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine));
3498     ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine));
3499     ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine));
3500     ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine));
3501     ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine));
3502     ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine));
3503     ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
3504     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine));
3505     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine));
3506     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine));
3507     ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine));
3508     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine));
3509     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine));
3510     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine));
3511     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine));
3512     ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine));
3513     ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine));
3514     ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine));
3515     ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine));
3516     ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine));
3517     ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine));
3518     ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine));
3519     ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine));
3520     ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine));
3521     ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine));
3522     ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine));
3523     ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine));
3524     ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine));
3525     ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine));
3526     ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine));
3527     ft->PrototypeTemplate()->Set(v8::String::New("roundedRect"), V8FUNCTION(ctx2d_roundedRect, engine));
3528     ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine));
3529     ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine));
3530     ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine));
3531     ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine));
3532     ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine));
3533     ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine));
3534     ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine));
3535     ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine));
3536     ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine));
3537     ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine));
3538     ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine));
3539     ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine));
3540     ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine));
3541     ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine));
3542     ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine));
3543     ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine));
3544     ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine));
3545
3546     constructorContext = qPersistentNew(ft->GetFunction());
3547
3548     v8::Local<v8::FunctionTemplate> ftGradient = v8::FunctionTemplate::New();
3549     ftGradient->InstanceTemplate()->SetHasExternalResource(true);
3550     ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine));
3551     constructorGradient = qPersistentNew(ftGradient->GetFunction());
3552
3553     v8::Local<v8::FunctionTemplate> ftPattern = v8::FunctionTemplate::New();
3554     ftPattern->InstanceTemplate()->SetHasExternalResource(true);
3555     constructorPattern = qPersistentNew(ftPattern->GetFunction());
3556
3557     v8::Local<v8::FunctionTemplate> ftPixelArray = v8::FunctionTemplate::New();
3558     ftPixelArray->InstanceTemplate()->SetHasExternalResource(true);
3559     ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine));
3560     ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine));
3561     constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction());
3562
3563     v8::Local<v8::FunctionTemplate> ftImageData = v8::FunctionTemplate::New();
3564     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine));
3565     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine));
3566     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine));
3567     ftImageData->InstanceTemplate()->SetInternalFieldCount(1);
3568     constructorImageData = qPersistentNew(ftImageData->GetFunction());
3569 }
3570
3571 QQuickContext2DEngineData::~QQuickContext2DEngineData()
3572 {
3573     qPersistentDispose(constructorContext);
3574     qPersistentDispose(constructorGradient);
3575     qPersistentDispose(constructorPattern);
3576     qPersistentDispose(constructorImageData);
3577     qPersistentDispose(constructorPixelArray);
3578 }
3579
3580 void QQuickContext2D::popState()
3581 {
3582     if (m_stateStack.isEmpty())
3583         return;
3584
3585     QQuickContext2D::State newState = m_stateStack.pop();
3586
3587     if (state.matrix != newState.matrix)
3588         buffer()->updateMatrix(newState.matrix);
3589
3590     if (newState.globalAlpha != state.globalAlpha)
3591         buffer()->setGlobalAlpha(newState.globalAlpha);
3592
3593     if (newState.globalCompositeOperation != state.globalCompositeOperation)
3594         buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
3595
3596     if (newState.fillStyle != state.fillStyle)
3597         buffer()->setFillStyle(newState.fillStyle);
3598
3599     if (newState.strokeStyle != state.strokeStyle)
3600         buffer()->setStrokeStyle(newState.strokeStyle);
3601
3602     if (newState.lineWidth != state.lineWidth)
3603         buffer()->setLineWidth(newState.lineWidth);
3604
3605     if (newState.lineCap != state.lineCap)
3606         buffer()->setLineCap(newState.lineCap);
3607
3608     if (newState.lineJoin != state.lineJoin)
3609         buffer()->setLineJoin(newState.lineJoin);
3610
3611     if (newState.miterLimit != state.miterLimit)
3612         buffer()->setMiterLimit(newState.miterLimit);
3613
3614     if (newState.clipPath != state.clipPath) {
3615         buffer()->clip(newState.clipPath);
3616     }
3617
3618     if (newState.shadowBlur != state.shadowBlur)
3619         buffer()->setShadowBlur(newState.shadowBlur);
3620
3621     if (newState.shadowColor != state.shadowColor)
3622         buffer()->setShadowColor(newState.shadowColor);
3623
3624     if (newState.shadowOffsetX != state.shadowOffsetX)
3625         buffer()->setShadowOffsetX(newState.shadowOffsetX);
3626
3627     if (newState.shadowOffsetY != state.shadowOffsetY)
3628         buffer()->setShadowOffsetY(newState.shadowOffsetY);
3629     m_path = state.matrix.map(m_path);
3630     state = newState;
3631     m_path = state.matrix.inverted().map(m_path);
3632 }
3633 void QQuickContext2D::pushState()
3634 {
3635     m_stateStack.push(state);
3636 }
3637
3638 void QQuickContext2D::reset()
3639 {
3640     QQuickContext2D::State newState;
3641     newState.matrix = QTransform();
3642
3643     m_path = QPainterPath();
3644
3645     QPainterPath defaultClipPath;
3646
3647     QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
3648     r = r.united(m_canvas->canvasWindow().toRect());
3649     defaultClipPath.addRect(r);
3650     newState.clipPath = defaultClipPath;
3651     newState.clipPath.setFillRule(Qt::WindingFill);
3652
3653     newState.strokeStyle = QColor("#000000");
3654     newState.fillStyle = QColor("#000000");
3655     newState.fillPatternRepeatX = false;
3656     newState.fillPatternRepeatY = false;
3657     newState.strokePatternRepeatX = false;
3658     newState.strokePatternRepeatY = false;
3659     newState.invertibleCTM = true;
3660     newState.fillRule = Qt::WindingFill;
3661     newState.globalAlpha = 1.0;
3662     newState.lineWidth = 1;
3663     newState.lineCap = Qt::FlatCap;
3664     newState.lineJoin = Qt::MiterJoin;
3665     newState.miterLimit = 10;
3666     newState.shadowOffsetX = 0;
3667     newState.shadowOffsetY = 0;
3668     newState.shadowBlur = 0;
3669     newState.shadowColor = qRgba(0, 0, 0, 0);
3670     newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
3671     newState.font = QFont(QLatin1String("sans-serif"), 10);
3672     newState.textAlign = QQuickContext2D::Start;
3673     newState.textBaseline = QQuickContext2D::Alphabetic;
3674
3675     m_stateStack.clear();
3676     m_stateStack.push(newState);
3677     popState();
3678     m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
3679 }
3680
3681 void QQuickContext2D::setV8Engine(QV8Engine *engine)
3682 {
3683     v8::HandleScope handle_scope;
3684     v8::Context::Scope scope(engine->context());
3685
3686     if (m_v8engine != engine) {
3687         m_v8engine = engine;
3688
3689         qPersistentDispose(m_v8value);
3690
3691         if (m_v8engine == 0)
3692             return;
3693
3694         QQuickContext2DEngineData *ed = engineData(engine);
3695         m_v8value = qPersistentNew(ed->constructorContext->NewInstance());
3696         QV8Context2DResource *r = new QV8Context2DResource(engine);
3697         r->context = this;
3698         m_v8value->SetExternalResource(r);
3699     }
3700 }
3701
3702 QQuickContext2DCommandBuffer* QQuickContext2D::nextBuffer()
3703 {
3704     QMutexLocker lock(&m_mutex);
3705     return m_bufferQueue.isEmpty() ? 0 : m_bufferQueue.dequeue();
3706 }
3707
3708 QT_END_NAMESPACE