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