Reset on system change
[profile/ivi/qtdeclarative.git] / src / declarative / items / context2d / qsgcontext2d.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgcontext2d_p.h"
43 #include "qsgcontext2dcommandbuffer_p.h"
44 #include "qsgcanvasitem_p.h"
45 #include "qsgitem_p.h"
46 #include "qsgshadereffectsource_p.h"
47
48 #include <QtCore/qdebug.h>
49 #include "private/qsgcontext_p.h"
50 #include "private/qdeclarativesvgparser_p.h"
51 #include "private/qdeclarativepath_p.h"
52
53 #include "private/qsgimage_p_p.h"
54
55 #include <qdeclarativeinfo.h>
56 #include <QtCore/qmath.h>
57 #include "qv8engine_p.h"
58
59 #include <QtOpenGL/QGLFramebufferObjectFormat>
60 #include <QtOpenGL/QGLFramebufferObject>
61 #include "qdeclarativeengine.h"
62 QT_BEGIN_NAMESPACE
63 /*!
64     \qmlclass Context2D QSGContext2D
65     \inqmlmodule QtQuick 2
66     \since QtQuick 2.0
67     \brief The Context2D element allows you to draw 2d graphic shapes on Canvas item.
68 */
69 static const double Q_PI   = 3.14159265358979323846;   // pi
70
71
72 #define DEGREES(t) ((t) * 180.0 / Q_PI)
73 #define qClamp(val, min, max) qMin(qMax(val, min), max)
74
75 #define CHECK_CONTEXT(r)     if (!r || !r->context || !r->context->buffer()) \
76                                 V8THROW_ERROR("Not a Context2D object");
77
78 #define CHECK_CONTEXT_SETTER(r)     if (!r || !r->context || !r->context->buffer()) \
79                                        V8THROW_ERROR_SETTER("Not a Context2D object");
80
81 static inline int extractInt(const char **name)
82 {
83     int result = 0;
84     bool negative = false;
85
86     //eat leading whitespace
87     while (isspace(*name[0]))
88         ++*name;
89
90     if (*name[0] == '-') {
91         ++*name;
92         negative = true;
93     } /*else if (name[0] == '+')
94         ++name;     //ignore*/
95
96     //construct number
97     while (isdigit(*name[0])) {
98         result = result * 10 + (*name[0] - '0');
99         ++*name;
100     }
101     if (negative)
102         result = -result;
103
104     //handle optional percentage
105     if (*name[0] == '%')
106         result *= qreal(255)/100;  //### floor or round?
107
108     //eat trailing whitespace
109     while (isspace(*name[0]))
110         ++*name;
111
112     return result;
113 }
114
115 static bool qt_get_rgb(const QString &string, QRgb *rgb)
116 {
117     const char *name = string.toLatin1().constData();
118     int len = qstrlen(name);
119
120     if (len < 5)
121         return false;
122
123     bool handleAlpha = false;
124
125     if (name[0] != 'r')
126         return false;
127     if (name[1] != 'g')
128         return false;
129     if (name[2] != 'b')
130         return false;
131     if (name[3] == 'a') {
132         handleAlpha = true;
133         if(name[3] != '(')
134             return false;
135     } else if (name[3] != '(')
136         return false;
137
138     name += 4;
139
140     int r, g, b, a = 1;
141     int result;
142
143     //red
144     result = extractInt(&name);
145     if (name[0] == ',') {
146         r = result;
147         ++name;
148     } else
149         return false;
150
151     //green
152     result = extractInt(&name);
153     if (name[0] == ',') {
154         g = result;
155         ++name;
156     } else
157         return false;
158
159     char nextChar = handleAlpha ? ',' : ')';
160
161     //blue
162     result = extractInt(&name);
163     if (name[0] == nextChar) {
164         b = result;
165         ++name;
166     } else
167         return false;
168
169     //alpha
170     if (handleAlpha) {
171         result = extractInt(&name);
172         if (name[0] == ')') {
173             a = result * 255;   //map 0-1 to 0-255
174             ++name;
175         } else
176             return false;
177     }
178
179     if (name[0] != '\0')
180         return false;
181
182     *rgb = qRgba(qClamp(r,0,255), qClamp(g,0,255), qClamp(b,0,255), qClamp(a,0,255));
183     return true;
184 }
185
186 //### unify with qt_get_rgb?
187 static bool qt_get_hsl(const QString &string, QColor *color)
188 {
189     const char *name = string.toLatin1().constData();
190     int len = qstrlen(name);
191
192     if (len < 5)
193         return false;
194
195     bool handleAlpha = false;
196
197     if (name[0] != 'h')
198         return false;
199     if (name[1] != 's')
200         return false;
201     if (name[2] != 'l')
202         return false;
203     if (name[3] == 'a') {
204         handleAlpha = true;
205         if(name[3] != '(')
206             return false;
207     } else if (name[3] != '(')
208         return false;
209
210     name += 4;
211
212     int h, s, l, a = 1;
213     int result;
214
215     //hue
216     result = extractInt(&name);
217     if (name[0] == ',') {
218         h = result;
219         ++name;
220     } else
221         return false;
222
223     //saturation
224     result = extractInt(&name);
225     if (name[0] == ',') {
226         s = result;
227         ++name;
228     } else
229         return false;
230
231     char nextChar = handleAlpha ? ',' : ')';
232
233     //lightness
234     result = extractInt(&name);
235     if (name[0] == nextChar) {
236         l = result;
237         ++name;
238     } else
239         return false;
240
241     //alpha
242     if (handleAlpha) {
243         result = extractInt(&name);
244         if (name[0] == ')') {
245             a = result * 255;   //map 0-1 to 0-255
246             ++name;
247         } else
248             return false;
249     }
250
251     if (name[0] != '\0')
252         return false;
253
254     *color = QColor::fromHsl(qClamp(h,0,255), qClamp(s,0,255), qClamp(l,0,255), qClamp(a,0,255));
255     return true;
256 }
257
258 //### optimize further
259 QColor qt_color_from_string(const QString &name)
260 {
261     if (name.startsWith(QLatin1String("rgb"))) {
262         QRgb rgb;
263         if (qt_get_rgb(name, &rgb))
264             return QColor(rgb);
265     } else if (name.startsWith(QLatin1String("hsl"))) {
266         QColor color;
267         if (qt_get_hsl(name, &color))
268             return color;
269     }
270
271     return QColor(name);
272 }
273
274 QFont qt_font_from_string(const QString& fontString) {
275     QFont font;
276      // ### this is simplified and incomplete
277     // ### TODO:get code from Qt webkit
278      QStringList tokens = fontString.split(QLatin1String(" "));
279      foreach (const QString &token, tokens) {
280          if (token == QLatin1String("italic"))
281              font.setItalic(true);
282          else if (token == QLatin1String("bold"))
283              font.setBold(true);
284          else if (token.endsWith(QLatin1String("px"))) {
285              QString number = token;
286              number.remove(QLatin1String("px"));
287              //font.setPointSizeF(number.trimmed().toFloat());
288              font.setPixelSize(number.trimmed().toInt());
289          } else
290              font.setFamily(token);
291      }
292
293      return font;
294 }
295
296
297
298 class QSGContext2DEngineData : public QV8Engine::Deletable
299 {
300 public:
301     QSGContext2DEngineData(QV8Engine *engine);
302     ~QSGContext2DEngineData();
303
304     v8::Persistent<v8::Function> constructorContext;
305     v8::Persistent<v8::Function> constructorGradient;
306     v8::Persistent<v8::Function> constructorPattern;
307     v8::Persistent<v8::Function> constructorPixelArray;
308     v8::Persistent<v8::Function> constructorImageData;
309 };
310 V8_DEFINE_EXTENSION(QSGContext2DEngineData, engineData);
311
312
313
314 class QV8Context2DResource : public QV8ObjectResource
315 {
316     V8_RESOURCE_TYPE(Context2DType)
317 public:
318     QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e) {}
319     QSGContext2D* context;
320 };
321
322 class QV8Context2DStyleResource : public QV8ObjectResource
323 {
324     V8_RESOURCE_TYPE(Context2DStyleType)
325 public:
326     QV8Context2DStyleResource(QV8Engine *e) : QV8ObjectResource(e) {}
327     QBrush brush;
328 };
329
330 class QV8Context2DPixelArrayResource : public QV8ObjectResource
331 {
332     V8_RESOURCE_TYPE(Context2DPixelArrayType)
333 public:
334     QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {}
335
336     QImage image;
337 };
338
339 static QImage qt_texture_to_image(QSGTexture* texture)
340 {
341     if (!texture || !texture->textureId())
342         return QImage();
343     QGLFramebufferObjectFormat format;
344     format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
345     format.setInternalTextureFormat(GL_RGBA);
346     format.setMipmap(false);
347     QGLFramebufferObject* fbo = new QGLFramebufferObject(texture->textureSize(), format);
348     fbo->drawTexture(QPointF(0,0), texture->textureId(), GL_TEXTURE_2D);
349     return fbo->toImage();
350 }
351
352 static QSGTexture* qt_item_to_texture(QSGItem* item)
353 {
354     if (!item)
355         return 0;
356     QSGShaderEffectTexture* texture = new QSGShaderEffectTexture(item);
357     texture->setItem(QSGItemPrivate::get(item)->itemNode());
358     texture->setLive(true);
359
360     QRectF sourceRect = QRectF(0, 0, item->width(), item->height());
361
362     texture->setRect(sourceRect);
363     QSize textureSize = QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())));
364     texture->setSize(textureSize);
365     texture->setRecursive(false);
366     texture->setFormat(GL_RGBA);
367     texture->setHasMipmaps(false);
368     texture->markDirtyTexture();
369     texture->updateTexture();
370     return texture;
371 }
372
373 static QImage qt_item_to_image(QSGItem* item) {
374     return qt_texture_to_image(qt_item_to_texture(item));
375 }
376
377 static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
378 {
379     if (compositeOperator == QLatin1String("source-over")) {
380         return QPainter::CompositionMode_SourceOver;
381     } else if (compositeOperator == QLatin1String("source-out")) {
382         return QPainter::CompositionMode_SourceOut;
383     } else if (compositeOperator == QLatin1String("source-in")) {
384         return QPainter::CompositionMode_SourceIn;
385     } else if (compositeOperator == QLatin1String("source-atop")) {
386         return QPainter::CompositionMode_SourceAtop;
387     } else if (compositeOperator == QLatin1String("destination-atop")) {
388         return QPainter::CompositionMode_DestinationAtop;
389     } else if (compositeOperator == QLatin1String("destination-in")) {
390         return QPainter::CompositionMode_DestinationIn;
391     } else if (compositeOperator == QLatin1String("destination-out")) {
392         return QPainter::CompositionMode_DestinationOut;
393     } else if (compositeOperator == QLatin1String("destination-over")) {
394         return QPainter::CompositionMode_DestinationOver;
395     } else if (compositeOperator == QLatin1String("lighter")) {
396         return QPainter::CompositionMode_Plus;
397     } else if (compositeOperator == QLatin1String("copy")) {
398         return QPainter::CompositionMode_Source;
399     } else if (compositeOperator == QLatin1String("xor")) {
400         return QPainter::CompositionMode_Xor;
401     } else if (compositeOperator == QLatin1String("qt-clear")) {
402         return QPainter::CompositionMode_Clear;
403     } else if (compositeOperator == QLatin1String("qt-destination")) {
404         return QPainter::CompositionMode_Destination;
405     } else if (compositeOperator == QLatin1String("qt-multiply")) {
406         return QPainter::CompositionMode_Multiply;
407     } else if (compositeOperator == QLatin1String("qt-screen")) {
408         return QPainter::CompositionMode_Screen;
409     } else if (compositeOperator == QLatin1String("qt-overlay")) {
410         return QPainter::CompositionMode_Overlay;
411     } else if (compositeOperator == QLatin1String("qt-darken")) {
412         return QPainter::CompositionMode_Darken;
413     } else if (compositeOperator == QLatin1String("qt-lighten")) {
414         return QPainter::CompositionMode_Lighten;
415     } else if (compositeOperator == QLatin1String("qt-color-dodge")) {
416         return QPainter::CompositionMode_ColorDodge;
417     } else if (compositeOperator == QLatin1String("qt-color-burn")) {
418         return QPainter::CompositionMode_ColorBurn;
419     } else if (compositeOperator == QLatin1String("qt-hard-light")) {
420         return QPainter::CompositionMode_HardLight;
421     } else if (compositeOperator == QLatin1String("qt-soft-light")) {
422         return QPainter::CompositionMode_SoftLight;
423     } else if (compositeOperator == QLatin1String("qt-difference")) {
424         return QPainter::CompositionMode_Difference;
425     } else if (compositeOperator == QLatin1String("qt-exclusion")) {
426         return QPainter::CompositionMode_Exclusion;
427     }
428     return QPainter::CompositionMode_SourceOver;
429 }
430
431 static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
432 {
433     switch (op) {
434     case QPainter::CompositionMode_SourceOver:
435         return QLatin1String("source-over");
436     case QPainter::CompositionMode_DestinationOver:
437         return QLatin1String("destination-over");
438     case QPainter::CompositionMode_Clear:
439         return QLatin1String("qt-clear");
440     case QPainter::CompositionMode_Source:
441         return QLatin1String("copy");
442     case QPainter::CompositionMode_Destination:
443         return QLatin1String("qt-destination");
444     case QPainter::CompositionMode_SourceIn:
445         return QLatin1String("source-in");
446     case QPainter::CompositionMode_DestinationIn:
447         return QLatin1String("destination-in");
448     case QPainter::CompositionMode_SourceOut:
449         return QLatin1String("source-out");
450     case QPainter::CompositionMode_DestinationOut:
451         return QLatin1String("destination-out");
452     case QPainter::CompositionMode_SourceAtop:
453         return QLatin1String("source-atop");
454     case QPainter::CompositionMode_DestinationAtop:
455         return QLatin1String("destination-atop");
456     case QPainter::CompositionMode_Xor:
457         return QLatin1String("xor");
458     case QPainter::CompositionMode_Plus:
459         return QLatin1String("lighter");
460     case QPainter::CompositionMode_Multiply:
461         return QLatin1String("qt-multiply");
462     case QPainter::CompositionMode_Screen:
463         return QLatin1String("qt-screen");
464     case QPainter::CompositionMode_Overlay:
465         return QLatin1String("qt-overlay");
466     case QPainter::CompositionMode_Darken:
467         return QLatin1String("qt-darken");
468     case QPainter::CompositionMode_Lighten:
469         return QLatin1String("qt-lighten");
470     case QPainter::CompositionMode_ColorDodge:
471         return QLatin1String("qt-color-dodge");
472     case QPainter::CompositionMode_ColorBurn:
473         return QLatin1String("qt-color-burn");
474     case QPainter::CompositionMode_HardLight:
475         return QLatin1String("qt-hard-light");
476     case QPainter::CompositionMode_SoftLight:
477         return QLatin1String("qt-soft-light");
478     case QPainter::CompositionMode_Difference:
479         return QLatin1String("qt-difference");
480     case QPainter::CompositionMode_Exclusion:
481         return QLatin1String("qt-exclusion");
482     default:
483         break;
484     }
485     return QString();
486 }
487
488
489 static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image)
490 {
491     QSGContext2DEngineData *ed = engineData(engine);
492     v8::Local<v8::Object> imageData = ed->constructorImageData->NewInstance();
493     QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine);
494     if (image.isNull()) {
495         r->image = QImage(w, h, QImage::Format_ARGB32);
496         r->image.fill(Qt::transparent);
497     } else {
498         Q_ASSERT(image.width() == w && image.height() == h);
499         r->image = image;
500     }
501     v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
502     pixelData->SetExternalResource(r);
503
504     imageData->SetInternalField(0, pixelData);
505     return imageData;
506 }
507
508 //static script functions
509
510 /*!
511     \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas
512      Holds the canvas item that the context paints on.
513 */
514 static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::AccessorInfo &info)
515 {
516     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
517     CHECK_CONTEXT(r)
518
519
520     QV8Engine *engine = V8ENGINE_ACCESSOR();
521
522     return engine->newQObject(r->context->canvas());
523 }
524
525 /*!
526     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::restore()
527     Pops the top state on the stack, restoring the context to that state.
528 */
529 static v8::Handle<v8::Value> ctx2d_restore(const v8::Arguments &args)
530 {
531     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
532     CHECK_CONTEXT(r)
533
534     r->context->popState();
535     return args.This();
536 }
537
538 /*!
539     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::reset()
540     Resets the context state and properties to the default values.
541 */
542 static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args)
543 {
544     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
545     CHECK_CONTEXT(r)
546
547     r->context->reset();
548     return args.This();
549 }
550
551 /*!
552     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::save()
553     Pushes the current state onto the stack.
554 */
555 static v8::Handle<v8::Value> ctx2d_save(const v8::Arguments &args)
556 {
557     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
558     CHECK_CONTEXT(r)
559
560     r->context->pushState();
561
562     return args.This();
563 }
564
565 // transformations
566 /*!
567     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::rotate(real angle)
568     Changes the transformation matrix to apply a rotation transformation with the given characteristics.
569     Note: The angle is in radians.
570 */
571 static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args)
572 {
573     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
574     CHECK_CONTEXT(r)
575
576
577     if (args.Length() == 1)  {
578         qreal angle = args[0]->NumberValue();
579         r->context->state.matrix.rotate(DEGREES(angle));
580         r->context->buffer()->updateMatrix(r->context->state.matrix);
581     }
582
583     return args.This();
584 }
585
586 /*!
587     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::scale(real x, real y)
588     Changes the transformation matrix to apply a scaling transformation with the given characteristics.
589 */
590 static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args)
591 {
592     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
593     CHECK_CONTEXT(r)
594
595
596     if (args.Length() == 2) {
597         qreal x, y;
598         x = args[0]->NumberValue();
599         y = args[1]->NumberValue();
600         r->context->state.matrix.scale(x, y);
601         r->context->buffer()->updateMatrix(r->context->state.matrix);
602     }
603
604     return args.This();
605 }
606
607 /*!
608     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f)
609     Changes the transformation matrix to the matrix given by the arguments as described below.
610
611     \sa QtQuick2::Context2D::transform()
612 */
613 static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
614 {
615     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
616     CHECK_CONTEXT(r)
617
618
619     if (args.Length() == 6) {
620         r->context->state.matrix = QTransform(args[0]->NumberValue(),
621                                               args[1]->NumberValue(),
622                                               args[2]->NumberValue(),
623                                               args[3]->NumberValue(),
624                                               args[4]->NumberValue(),
625                                               args[5]->NumberValue());
626         r->context->buffer()->updateMatrix(r->context->state.matrix);
627     }
628
629     return args.This();
630 }
631
632 /*!
633     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f)
634     Changes the transformation matrix to apply the matrix given by the arguments as described below.
635
636     \sa QtQuick2::Context2D::setTransform()
637 */
638 static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
639 {
640     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
641     CHECK_CONTEXT(r)
642
643
644     if (args.Length() == 6) {
645         r->context->state.matrix *= QTransform(args[0]->NumberValue(),
646                                                args[1]->NumberValue(),
647                                                args[2]->NumberValue(),
648                                                args[3]->NumberValue(),
649                                                args[4]->NumberValue(),
650                                                args[5]->NumberValue());
651         r->context->buffer()->updateMatrix(r->context->state.matrix);
652     }
653
654     return args.This();
655 }
656
657 /*!
658     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::translate(real x, real y)
659     Changes the transformation matrix to apply a translation transformation with the given characteristics.
660 */
661 static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
662 {
663     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
664     CHECK_CONTEXT(r)
665
666
667     if (args.Length() == 2) {
668         r->context->state.matrix.translate(args[0]->NumberValue(),
669                                            args[1]->NumberValue());
670         r->context->buffer()->updateMatrix(r->context->state.matrix);
671     }
672
673     return args.This();
674 }
675
676
677 /*!
678     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::resetTransform()
679     Reset the transformation matrix default value.
680
681     \sa QtQuick2::Context2D::transform(), QtQuick2::Context2D::setTransform()
682 */
683 static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
684 {
685     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
686     CHECK_CONTEXT(r)
687
688     r->context->state.matrix = QTransform();
689     r->context->buffer()->updateMatrix(r->context->state.matrix);
690
691     return args.This();
692 }
693
694
695 /*!
696     \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::shear(real sh, real sv )
697     Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction.
698 */
699 static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args)
700 {
701     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
702     CHECK_CONTEXT(r)
703
704     r->context->state.matrix.shear(args[0]->NumberValue(),
705                                    args[1]->NumberValue());
706     r->context->buffer()->updateMatrix(r->context->state.matrix);
707
708     return args.This();
709 }
710 // compositing
711
712 /*!
713     \qmlproperty real QtQuick2::Context2D::globalAlpha
714      Holds the the current alpha value applied to rendering operations.
715      The value must be in the range from 0.0 (fully transparent) to 1.0 (no additional transparency).
716 */
717 static v8::Handle<v8::Value> ctx2d_globalAlpha(v8::Local<v8::String>, const v8::AccessorInfo &info)
718 {
719     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
720     CHECK_CONTEXT(r)
721
722
723     return v8::Number::New(r->context->state.globalAlpha);
724 }
725
726 static void ctx2d_globalAlpha_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
727 {
728     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
729     CHECK_CONTEXT_SETTER(r)
730
731     qreal globalAlpha = value->NumberValue();
732
733     if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) {
734         r->context->state.globalAlpha = globalAlpha;
735         r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha);
736     }
737 }
738
739 /*!
740     \qmlproperty string QtQuick2::Context2D::globalCompositeOperation
741      Holds the the current the current composition operation, from the list below:
742      \list
743      \o source-atop      - A atop B. Display the source image wherever both images are opaque.
744                            Display the destination image wherever the destination image is opaque but the source image is transparent.
745                            Display transparency elsewhere.
746      \o source-in        - A in B. Display the source image wherever both the source image and destination image are opaque.
747                            Display transparency elsewhere.
748      \o source-out       - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
749                            Display transparency elsewhere.
750      \o source-over      - (default) A over B. Display the source image wherever the source image is opaque.
751                            Display the destination image elsewhere.
752      \o destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
753      \o destination-in   - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
754      \o destination-out  - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
755      \o destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
756      \o lighter          - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
757      \o copy             - A (B is ignored). Display the source image instead of the destination image.
758      \o xor              - A xor B. Exclusive OR of the source image and destination image.
759      \endlist
760 */
761 static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
762 {
763     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
764     CHECK_CONTEXT(r)
765
766
767     QV8Engine *engine = V8ENGINE_ACCESSOR();
768
769     return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation));
770 }
771
772 static void ctx2d_globalCompositeOperation_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
773 {
774     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
775     CHECK_CONTEXT_SETTER(r)
776
777     QV8Engine *engine = V8ENGINE_ACCESSOR();
778
779     QPainter::CompositionMode cm = qt_composite_mode_from_string(engine->toString(value));
780     if (cm != r->context->state.globalCompositeOperation) {
781         r->context->state.globalCompositeOperation = cm;
782         r->context->buffer()->setGlobalCompositeOperation(cm);
783     }
784 }
785
786 // colors and styles
787 /*!
788     \qmlproperty variant QtQuick2::Context2D::fillStyle
789      Holds the current style used for filling shapes.
790      The style can be either a string containing a CSS color, or a CanvasGradient or CanvasPattern object. Invalid values are ignored.
791      \sa QtQuick2::Context2D::createLinearGradient
792      \sa QtQuick2::Context2D::createRadialGradient
793      \sa QtQuick2::Context2D::createPattern
794      \sa QtQuick2::Context2D::strokeStyle
795  */
796 static v8::Handle<v8::Value> ctx2d_fillStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
797 {
798     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
799     CHECK_CONTEXT(r)
800
801     return r->context->m_fillStyle;
802 }
803
804 static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
805 {
806     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
807     CHECK_CONTEXT_SETTER(r)
808
809     QV8Engine *engine = V8ENGINE_ACCESSOR();
810
811    r->context->m_fillStyle = value;
812    if (value->IsObject()) {
813        QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
814        if (color.isValid()) {
815            r->context->state.fillStyle = color;
816            r->context->buffer()->setFillStyle(color);
817        } else {
818            QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
819            if (style && style->brush != r->context->state.fillStyle) {
820                r->context->state.fillStyle = style->brush;
821                r->context->buffer()->setFillStyle(style->brush);
822            }
823        }
824    } else if (value->IsString()) {
825        QColor color = qt_color_from_string(engine->toString(value));
826        if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
827             r->context->state.fillStyle = QBrush(color);
828             r->context->buffer()->setFillStyle(r->context->state.fillStyle);
829        }
830    }
831 }
832
833 static v8::Handle<v8::Value> ctx2d_fillRule(v8::Local<v8::String>, const v8::AccessorInfo &info)
834 {
835     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
836     CHECK_CONTEXT(r)
837     QV8Engine *engine = V8ENGINE_ACCESSOR();
838
839     return engine->fromVariant(r->context->state.fillRule);
840 }
841
842 static void ctx2d_fillRule_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
843 {
844     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
845     CHECK_CONTEXT_SETTER(r)
846
847     QV8Engine *engine = V8ENGINE_ACCESSOR();
848
849     if ((value->IsString() && engine->toString(value) == "WindingFill")
850       ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) {
851         r->context->state.fillRule = Qt::WindingFill;
852     } else if ((value->IsString() && engine->toString(value) == "OddEvenFill")
853                ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) {
854         r->context->state.fillRule = Qt::OddEvenFill;
855     } else {
856         //error
857     }
858     r->context->m_path.setFillRule(r->context->state.fillRule);
859 }
860 /*!
861     \qmlproperty variant QtQuick2::Context2D::strokeStyle
862      Holds the current color or style to use for the lines around shapes,
863      The style can be either a string containing a CSS color, or a CanvasGradient or CanvasPattern object. Invalid values are ignored.
864      \sa QtQuick2::Context2D::createLinearGradient
865      \sa QtQuick2::Context2D::createRadialGradient
866      \sa QtQuick2::Context2D::createPattern
867      \sa QtQuick2::Context2D::fillStyle
868  */
869 v8::Handle<v8::Value> ctx2d_strokeStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
870 {
871     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
872     CHECK_CONTEXT(r)
873
874
875     return r->context->m_strokeStyle;
876 }
877
878 static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
879 {
880     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
881     CHECK_CONTEXT_SETTER(r)
882
883     QV8Engine *engine = V8ENGINE_ACCESSOR();
884
885     r->context->m_strokeStyle = value;
886     if (value->IsObject()) {
887         QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
888         if (color.isValid()) {
889             r->context->state.fillStyle = color;
890             r->context->buffer()->setStrokeStyle(color);
891         } else {
892             QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
893             if (style && style->brush != r->context->state.strokeStyle) {
894                 r->context->state.strokeStyle = style->brush;
895                 r->context->buffer()->setStrokeStyle(style->brush);
896             }
897         }
898     } else if (value->IsString()) {
899         QColor color = qt_color_from_string(engine->toString(value));
900         if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
901              r->context->state.strokeStyle = QBrush(color);
902              r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
903         }
904     }
905 }
906
907 /*!
908   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
909    Returns a CanvasGradient object that represents a linear gradient that paints along the line given by the coordinates
910    represented by the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
911     \sa QtQuick2::Context2D::createRadialGradient
912     \sa QtQuick2::Context2D::createPattern
913     \sa QtQuick2::Context2D::fillStyle
914     \sa QtQuick2::Context2D::strokeStyle
915   */
916
917 static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &args)
918 {
919     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
920     CHECK_CONTEXT(r)
921
922
923     QV8Engine *engine = V8ENGINE();
924
925     if (args.Length() == 4) {
926         //TODO:infinite or NaN, the method must raise a NOT_SUPPORTED_ERR
927         QSGContext2DEngineData *ed = engineData(engine);
928         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
929         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
930         r->brush = QLinearGradient(args[0]->NumberValue(),
931                                    args[1]->NumberValue(),
932                                    args[2]->NumberValue(),
933                                    args[3]->NumberValue());
934         gradient->SetExternalResource(r);
935         return gradient;
936     }
937
938     return args.This();
939 }
940
941 /*!
942   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
943    Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with
944    origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1.
945
946     \sa QtQuick2::Context2D::createLinearGradient
947     \sa QtQuick2::Context2D::createPattern
948     \sa QtQuick2::Context2D::fillStyle
949     \sa QtQuick2::Context2D::strokeStyle
950   */
951
952 static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &args)
953 {
954     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
955     CHECK_CONTEXT(r)
956
957
958     QV8Engine *engine = V8ENGINE();
959
960     if (args.Length() == 6) {
961         QSGContext2DEngineData *ed = engineData(engine);
962         v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
963         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
964
965         qreal x0 = args[0]->NumberValue();
966         qreal y0 = args[1]->NumberValue();
967         qreal r0 = args[2]->NumberValue();
968         qreal x1 = args[3]->NumberValue();
969         qreal y1 = args[4]->NumberValue();
970         qreal r1 = args[5]->NumberValue();
971         //TODO:infinite or NaN, a NOT_SUPPORTED_ERR exception must be raised.
972         //If either of r0 or r1 are negative, an INDEX_SIZE_ERR exception must be raised.
973         r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
974         gradient->SetExternalResource(r);
975         return gradient;
976     }
977
978     return args.This();
979 }
980
981 /*!
982   \qmlmethod variant createPattern(Image image, string repetition)
983   Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
984
985   The \a image parameter must be a valid Image item, if there is no image data, throws an INVALID_STATE_ERR exception.
986
987   The allowed values for \a repetition are:
988
989   \list
990   \o "repeat"    - both directions
991   \o "repeat-x   - horizontal only
992   \o "repeat-y"  - vertical only
993   \o "no-repeat" - neither
994   \endlist
995
996   If the repetition argument is empty or null, the value "repeat" is used.
997
998   \sa QtQuick2::Context2D::strokeStyle
999   \sa QtQuick2::Context2D::fillStyle
1000   */
1001 static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
1002 {
1003     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1004     CHECK_CONTEXT(r)
1005
1006
1007     QV8Engine *engine = V8ENGINE();
1008
1009     if (args.Length() == 2) {
1010         QSGContext2DEngineData *ed = engineData(engine);
1011         v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
1012         QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1013
1014         QImage img;
1015
1016         QSGItem* item = qobject_cast<QSGItem*>(engine->toQObject(args[0]));
1017         if (item) {
1018             img = qt_item_to_image(item);
1019 //            if (img.isNull()) {
1020 //                //exception: INVALID_STATE_ERR
1021 //            }
1022         } /*else {
1023             //exception: TYPE_MISMATCH_ERR
1024         }*/
1025
1026         QString repetition = engine->toString(args[1]);
1027
1028 //        if (repetition == "repeat" || repetition.isEmpty()) {
1029 //            //TODO
1030 //        } else if (repetition == "repeat-x") {
1031 //            //TODO
1032 //        } else if (repetition == "repeat-y") {
1033 //            //TODO
1034 //        } else if (repetition == "no-repeat") {
1035 //            //TODO
1036 //        } else {
1037 //            //TODO: exception: SYNTAX_ERR
1038 //        }
1039         r->brush = img;
1040         pattern->SetExternalResource(r);
1041         return pattern;
1042     }
1043     return v8::Null();
1044 }
1045
1046 // line styles
1047 /*!
1048     \qmlproperty string QtQuick2::Context2D::lineCap
1049      Holds the the current line cap style.
1050      The possible line cap styles are:
1051     \list
1052     \o butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value.
1053     \o 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.
1054     \o 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.
1055     \endlist
1056     Other values are ignored.
1057 */
1058 v8::Handle<v8::Value> ctx2d_lineCap(v8::Local<v8::String>, const v8::AccessorInfo &info)
1059 {
1060     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1061     CHECK_CONTEXT(r)
1062
1063
1064     QV8Engine *engine = V8ENGINE_ACCESSOR();
1065     switch (r->context->state.lineCap) {
1066     case Qt::RoundCap:
1067         return engine->toString(QLatin1String("round"));
1068     case Qt::FlatCap:
1069         return engine->toString(QLatin1String("butt"));
1070     case Qt::SquareCap:
1071         return engine->toString(QLatin1String("square"));
1072     default:
1073         break;
1074     }
1075     return engine->toString(QLatin1String("butt"));;
1076 }
1077
1078 static void ctx2d_lineCap_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1079 {
1080     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1081     CHECK_CONTEXT_SETTER(r)
1082
1083     QV8Engine *engine = V8ENGINE_ACCESSOR();
1084
1085     QString lineCap = engine->toString(value);
1086     Qt::PenCapStyle cap;
1087     if (lineCap == QLatin1String("round"))
1088         cap = Qt::RoundCap;
1089     else if (lineCap == QLatin1String("butt"))
1090         cap = Qt::FlatCap;
1091     else if (lineCap == QLatin1String("square"))
1092         cap = Qt::SquareCap;
1093
1094     if (cap != r->context->state.lineCap) {
1095         r->context->state.lineCap = cap;
1096         r->context->buffer()->setLineCap(cap);
1097     }
1098 }
1099
1100 /*!
1101     \qmlproperty string QtQuick2::Context2D::lineJoin
1102      Holds the the current line join style. A join exists at any point in a subpath
1103      shared by two consecutive lines. When a subpath is closed, then a join also exists
1104      at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
1105
1106     The possible line join styles are:
1107     \list
1108     \o bevel - this is all that is rendered at joins.
1109     \o 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.
1110     \o 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.
1111     \endlist
1112     Other values are ignored.
1113 */
1114 v8::Handle<v8::Value> ctx2d_lineJoin(v8::Local<v8::String>, const v8::AccessorInfo &info)
1115 {
1116     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1117     CHECK_CONTEXT(r)
1118
1119
1120     QV8Engine *engine = V8ENGINE_ACCESSOR();
1121     switch (r->context->state.lineJoin) {
1122     case Qt::RoundJoin:
1123         return engine->toString(QLatin1String("round"));
1124     case Qt::BevelJoin:
1125         return engine->toString(QLatin1String("bevel"));
1126     case Qt::MiterJoin:
1127         return engine->toString(QLatin1String("miter"));
1128     default:
1129         break;
1130     }
1131     return engine->toString(QLatin1String("miter"));
1132 }
1133
1134 static void ctx2d_lineJoin_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1135 {
1136     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1137     CHECK_CONTEXT_SETTER(r)
1138
1139     QV8Engine *engine = V8ENGINE_ACCESSOR();
1140
1141     QString lineJoin = engine->toString(value);
1142     Qt::PenJoinStyle join;
1143     if (lineJoin == QLatin1String("round"))
1144         join = Qt::RoundJoin;
1145     else if (lineJoin == QLatin1String("bevel"))
1146         join = Qt::BevelJoin;
1147     else if (lineJoin == QLatin1String("miter"))
1148         join = Qt::MiterJoin;
1149
1150     if (join != r->context->state.lineJoin) {
1151         r->context->state.lineJoin = join;
1152         r->context->buffer()->setLineJoin(join);
1153     }
1154 }
1155
1156 /*!
1157     \qmlproperty real QtQuick2::Context2D::lineWidth
1158      Holds the the current line width. Values that are not finite values greater than zero are ignored.
1159  */
1160 v8::Handle<v8::Value> ctx2d_lineWidth(v8::Local<v8::String>, const v8::AccessorInfo &info)
1161 {
1162     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1163     CHECK_CONTEXT(r)
1164
1165
1166     return v8::Number::New(r->context->state.lineWidth);
1167 }
1168
1169 static void ctx2d_lineWidth_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1170 {
1171     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1172     CHECK_CONTEXT_SETTER(r)
1173
1174     qreal w = value->NumberValue();
1175
1176     if (w > 0 && w != r->context->state.lineWidth) {
1177         r->context->state.lineWidth = w;
1178         r->context->buffer()->setLineWidth(w);
1179     }
1180 }
1181
1182 /*!
1183     \qmlproperty real QtQuick2::Context2D::miterLimit
1184      Holds the current miter limit ratio.
1185      The default miter limit value is 10.0.
1186  */
1187 v8::Handle<v8::Value> ctx2d_miterLimit(v8::Local<v8::String>, const v8::AccessorInfo &info)
1188 {
1189     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1190     CHECK_CONTEXT(r)
1191
1192
1193     return v8::Number::New(r->context->state.miterLimit);
1194 }
1195
1196 static void ctx2d_miterLimit_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1197 {
1198     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1199     CHECK_CONTEXT_SETTER(r)
1200
1201     qreal ml = value->NumberValue();
1202
1203     if (ml > 0 && ml != r->context->state.miterLimit) {
1204         r->context->state.miterLimit = ml;
1205         r->context->buffer()->setMiterLimit(ml);
1206     }
1207 }
1208
1209 // shadows
1210 /*!
1211     \qmlproperty real QtQuick2::Context2D::shadowBlur
1212      Holds the current level of blur applied to shadows
1213  */
1214 v8::Handle<v8::Value> ctx2d_shadowBlur(v8::Local<v8::String>, const v8::AccessorInfo &info)
1215 {
1216     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1217     CHECK_CONTEXT(r)
1218
1219
1220     return v8::Number::New(r->context->state.shadowBlur);
1221 }
1222
1223 static void ctx2d_shadowBlur_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1224 {
1225     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1226     CHECK_CONTEXT_SETTER(r)
1227     qreal blur = value->NumberValue();
1228
1229     if (blur > 0 && blur != r->context->state.shadowBlur) {
1230         r->context->state.shadowBlur = blur;
1231         r->context->buffer()->setShadowBlur(blur);
1232     }
1233 }
1234
1235 /*!
1236     \qmlproperty string QtQuick2::Context2D::shadowColor
1237      Holds the current shadow color.
1238  */
1239 v8::Handle<v8::Value> ctx2d_shadowColor(v8::Local<v8::String>, const v8::AccessorInfo &info)
1240 {
1241     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1242     CHECK_CONTEXT(r)
1243
1244
1245     QV8Engine *engine = V8ENGINE_ACCESSOR();
1246
1247     return engine->toString(r->context->state.shadowColor.name());
1248 }
1249
1250 static void ctx2d_shadowColor_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1251 {
1252     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1253     CHECK_CONTEXT_SETTER(r)
1254
1255     QV8Engine *engine = V8ENGINE_ACCESSOR();
1256
1257     QColor color = qt_color_from_string(engine->toString(value));
1258
1259     if (color.isValid() && color != r->context->state.shadowColor) {
1260         r->context->state.shadowColor = color;
1261         r->context->buffer()->setShadowColor(color);
1262     }
1263 }
1264
1265
1266 /*!
1267     \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX
1268      Holds the current shadow offset in the positive horizontal distance.
1269
1270      \sa QtQuick2::Context2D::shadowOffsetY
1271  */
1272 v8::Handle<v8::Value> ctx2d_shadowOffsetX(v8::Local<v8::String>, const v8::AccessorInfo &info)
1273 {
1274     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1275     CHECK_CONTEXT(r)
1276
1277
1278     return v8::Number::New(r->context->state.shadowOffsetX);
1279 }
1280
1281 static void ctx2d_shadowOffsetX_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1282 {
1283     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1284     CHECK_CONTEXT_SETTER(r)
1285
1286     //TODO: check value:infinite or NaN
1287     qreal offsetX = value->NumberValue();
1288     if (offsetX != r->context->state.shadowOffsetX) {
1289         r->context->state.shadowOffsetX = offsetX;
1290         r->context->buffer()->setShadowOffsetX(offsetX);
1291     }
1292 }
1293 /*!
1294     \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY
1295      Holds the current shadow offset in the positive vertical distance.
1296
1297      \sa QtQuick2::Context2D::shadowOffsetX
1298  */
1299 v8::Handle<v8::Value> ctx2d_shadowOffsetY(v8::Local<v8::String>, const v8::AccessorInfo &info)
1300 {
1301     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1302     CHECK_CONTEXT(r)
1303
1304
1305     return v8::Number::New(r->context->state.shadowOffsetY);
1306 }
1307
1308 static void ctx2d_shadowOffsetY_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1309 {
1310     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1311     CHECK_CONTEXT_SETTER(r)
1312     //TODO: check value:infinite or NaN
1313     qreal offsetY = value->NumberValue();
1314     if (offsetY != r->context->state.shadowOffsetY) {
1315         r->context->state.shadowOffsetY = offsetY;
1316         r->context->buffer()->setShadowOffsetY(offsetY);
1317     }
1318 }
1319
1320 v8::Handle<v8::Value> ctx2d_path(v8::Local<v8::String>, const v8::AccessorInfo &info)
1321 {
1322     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1323     CHECK_CONTEXT(r)
1324     return r->context->m_v8path;
1325 }
1326
1327 static void ctx2d_path_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1328 {
1329     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1330     CHECK_CONTEXT_SETTER(r)
1331     QV8Engine *engine = V8ENGINE_ACCESSOR();
1332
1333     r->context->beginPath();
1334     if (value->IsObject()) {
1335         QDeclarativePath* path = qobject_cast<QDeclarativePath*>(engine->toQObject(value));
1336         if (path)
1337             r->context->m_path = path->path();
1338     } else {
1339         QString path = engine->toString(value->ToString());
1340         QDeclarativeSvgParser::parsePathDataFast(path, r->context->m_path);
1341     }
1342     r->context->m_v8path = value;
1343 }
1344
1345 //rects
1346 /*!
1347   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::clearRect(real x, real y, real w, real h)
1348   Clears all pixels on the canvas in the given rectangle to transparent black.
1349   */
1350 static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
1351 {
1352     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1353     CHECK_CONTEXT(r)
1354
1355
1356     if (args.Length() == 4) {
1357         r->context->buffer()->clearRect(args[0]->NumberValue(),
1358                                         args[1]->NumberValue(),
1359                                         args[2]->NumberValue(),
1360                                         args[3]->NumberValue());
1361     }
1362
1363     return args.This();
1364 }
1365 /*!
1366   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1367    Paint the specified rectangular area using the fillStyle.
1368
1369    \sa QtQuick2::Context2D::fillStyle
1370   */
1371 static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
1372 {
1373     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1374     CHECK_CONTEXT(r)
1375
1376
1377     if (args.Length() == 4) {
1378         r->context->buffer()->fillRect(args[0]->NumberValue(),
1379                              args[1]->NumberValue(),
1380                              args[2]->NumberValue(),
1381                              args[3]->NumberValue());
1382     }
1383
1384     return args.This();
1385 }
1386
1387 /*!
1388   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1389    Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin,
1390    and (if appropriate) miterLimit attributes.
1391
1392    \sa QtQuick2::Context2D::strokeStyle
1393    \sa QtQuick2::Context2D::lineWidth
1394    \sa QtQuick2::Context2D::lineJoin
1395    \sa QtQuick2::Context2D::miterLimit
1396   */
1397 static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
1398 {
1399     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1400     CHECK_CONTEXT(r)
1401
1402
1403     if (args.Length() == 4) {
1404         r->context->buffer()->strokeRect(args[0]->NumberValue(),
1405                                          args[1]->NumberValue(),
1406                                          args[2]->NumberValue(),
1407                                          args[3]->NumberValue());
1408     }
1409     
1410     return args.This();
1411 }
1412
1413 // Complex shapes (paths) API
1414 /*!
1415   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
1416    Adds points to the subpath such that the arc described by the circumference of
1417    the circle described by the arguments.
1418
1419    See http://www.w3.org/TR/2dcontext/#dom-context-2d-arc for details.
1420   */
1421 static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
1422 {
1423     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1424     CHECK_CONTEXT(r)
1425
1426     if (args.Length() >= 5) {
1427         bool antiClockwise = false;
1428
1429         if (args.Length() == 6)
1430             antiClockwise = args[5]->BooleanValue();
1431
1432         qreal radius = args[2]->NumberValue();
1433         //Throws an INDEX_SIZE_ERR exception if the given radius is negative.
1434         r->context->arc(args[0]->NumberValue(),
1435                         args[1]->NumberValue(),
1436                         radius,
1437                         args[3]->NumberValue(),
1438                         args[4]->NumberValue(),
1439                         antiClockwise);
1440     }
1441
1442     return args.This();
1443 }
1444
1445 /*!
1446   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius)
1447
1448    Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line.
1449    See http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto for details.
1450   */
1451 static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
1452 {
1453     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1454     CHECK_CONTEXT(r)
1455
1456
1457     if (args.Length() == 5) {
1458         r->context->arcTo(args[0]->NumberValue(),
1459                           args[1]->NumberValue(),
1460                           args[2]->NumberValue(),
1461                           args[3]->NumberValue(),
1462                           args[4]->NumberValue());
1463     }
1464
1465     return args.This();
1466 }
1467
1468 /*!
1469   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::beginPath()
1470
1471    Resets the current path.
1472   */
1473 static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
1474 {
1475     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1476     CHECK_CONTEXT(r)
1477
1478
1479     r->context->beginPath();
1480
1481     return args.This();
1482 }
1483
1484 /*!
1485   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
1486
1487    Adds the given point to the current subpath, connected to the previous one by a cubic Bézier curve with the given control points.
1488
1489    See http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto for details.
1490   */
1491 static v8::Handle<v8::Value> ctx2d_bezierCurveTo(const v8::Arguments &args)
1492 {
1493     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1494     CHECK_CONTEXT(r)
1495
1496
1497     if (args.Length() == 6) {
1498         r->context->bezierCurveTo(args[0]->NumberValue(),
1499                                   args[1]->NumberValue(),
1500                                   args[2]->NumberValue(),
1501                                   args[3]->NumberValue(),
1502                                   args[4]->NumberValue(),
1503                                   args[5]->NumberValue());
1504     }
1505
1506     return args.This();
1507 }
1508
1509 /*!
1510   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::clip()
1511
1512    constrains the clipping region to the given path.
1513
1514    See http://www.w3.org/TR/2dcontext/#dom-context-2d-clip for details.
1515   */
1516 static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
1517 {
1518     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1519     CHECK_CONTEXT(r)
1520
1521     r->context->state.clipPath = r->context->m_path;
1522     r->context->buffer()->clip(r->context->state.clipPath);
1523     
1524     return args.This();
1525 }
1526
1527 /*!
1528   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::closePath()
1529
1530    Marks the current subpath as closed, and starts a new subpath with a point the same as the start and end of the newly closed subpath.
1531
1532    See http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath for details.
1533   */
1534 static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
1535 {
1536     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1537     CHECK_CONTEXT(r)
1538
1539
1540     r->context->closePath();
1541
1542     return args.This();
1543 }
1544
1545 /*!
1546   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::fill()
1547
1548    Fills the subpaths with the current fill style.
1549
1550    See http://www.w3.org/TR/2dcontext/#dom-context-2d-fill for details.
1551
1552    \sa QtQuick2::Context2D::fillStyle
1553   */
1554 static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args)
1555 {
1556     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1557     CHECK_CONTEXT(r)
1558
1559     r->context->buffer()->fill(r->context->m_path);
1560
1561     return args.This();
1562 }
1563
1564 /*!
1565   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::lineTo(real x, real y)
1566
1567    Adds the given point to the current subpath, connected to the previous one by a straight line.
1568  */
1569 static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
1570 {
1571     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1572     CHECK_CONTEXT(r)
1573
1574
1575     if (args.Length() == 2) {
1576         r->context->lineTo(args[0]->NumberValue(),
1577                            args[1]->NumberValue());
1578     }
1579
1580     return args.This();
1581 }
1582
1583 /*!
1584   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::moveTo(real x, real y)
1585
1586    Creates a new subpath with the given point.
1587  */
1588 static v8::Handle<v8::Value> ctx2d_moveTo(const v8::Arguments &args)
1589 {
1590     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1591     CHECK_CONTEXT(r)
1592
1593
1594     if (args.Length() == 2) {
1595         r->context->moveTo(args[0]->NumberValue(),
1596                            args[1]->NumberValue());
1597     }
1598
1599     return args.This();
1600 }
1601
1602 /*!
1603   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
1604
1605    Adds the given point to the current subpath, connected to the previous one by a quadratic Bézier curve with the given control point.
1606
1607    See http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto for details.
1608  */
1609 static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
1610 {
1611     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1612     CHECK_CONTEXT(r)
1613
1614
1615     if (args.Length() == 4) {
1616         r->context->quadraticCurveTo(args[0]->NumberValue(),
1617                                      args[1]->NumberValue(),
1618                                      args[2]->NumberValue(),
1619                                      args[3]->NumberValue());
1620     }
1621
1622     return args.This();
1623 }
1624
1625 /*!
1626   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::rect(real x, real y, real w, real h)
1627
1628    Adds a new closed subpath to the path, representing the given rectangle.
1629
1630    See http://www.w3.org/TR/2dcontext/#dom-context-2d-rect for details.
1631  */
1632 static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
1633 {
1634     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1635     CHECK_CONTEXT(r)
1636
1637
1638     if (args.Length() == 4) {
1639         r->context->rect(args[0]->NumberValue(),
1640                          args[1]->NumberValue(),
1641                          args[2]->NumberValue(),
1642                          args[3]->NumberValue());
1643     }
1644
1645     return args.This();
1646 }
1647
1648 /*!
1649   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::roundedRect(real x, real y, real w, real h,  real xRadius, real yRadius)
1650
1651    Adds the given rectangle rect with rounded corners to the path. The xRadius and yRadius arguments specify the radii of the
1652    ellipses defining the corners of the rounded rectangle.
1653  */
1654 static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args)
1655 {
1656     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1657     CHECK_CONTEXT(r)
1658
1659
1660     if (args.Length() == 6) {
1661         r->context->roundedRect(args[0]->NumberValue(),
1662                                 args[1]->NumberValue(),
1663                                 args[2]->NumberValue(),
1664                                 args[3]->NumberValue(),
1665                                 args[4]->NumberValue(),
1666                                 args[5]->NumberValue());
1667     }
1668
1669     return args.This();
1670 }
1671
1672 /*!
1673   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::ellipse(real x, real y, real w, real h)
1674
1675   Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h,
1676   and adds it to the path as a closed subpath.
1677
1678   The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position).
1679  */
1680 static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args)
1681 {
1682     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1683     CHECK_CONTEXT(r)
1684
1685
1686     if (args.Length() == 6) {
1687         r->context->ellipse(args[0]->NumberValue(),
1688                             args[1]->NumberValue(),
1689                             args[2]->NumberValue(),
1690                             args[3]->NumberValue());
1691     }
1692
1693     return args.This();
1694 }
1695
1696 /*!
1697   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::text(string text, real x, real y)
1698
1699   Adds the given \a text to the path as a set of closed subpaths created from the current context font supplied.
1700   The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (x, y).
1701  */
1702 static v8::Handle<v8::Value> ctx2d_text(const v8::Arguments &args)
1703 {
1704     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1705     CHECK_CONTEXT(r)
1706
1707     QV8Engine *engine = V8ENGINE();
1708     if (args.Length() == 3) {
1709         r->context->text(engine->toString(args[0]),
1710                          args[1]->NumberValue(),
1711                          args[2]->NumberValue());
1712     }
1713
1714     return args.This();
1715 }
1716
1717 /*!
1718   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::stroke()
1719
1720    Strokes the subpaths with the current stroke style.
1721
1722    See http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke for details.
1723
1724    \sa QtQuick2::Context2D::strokeStyle
1725   */
1726 static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
1727 {
1728     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1729     CHECK_CONTEXT(r)
1730
1731
1732     r->context->buffer()->stroke(r->context->m_path);
1733
1734     return args.This();
1735 }
1736
1737 /*!
1738   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::isPointInPath(real x, real y)
1739
1740    Returns true if the given point is in the current path.
1741
1742    See http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath for details.
1743   */
1744 static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
1745 {
1746     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1747     CHECK_CONTEXT(r)
1748
1749
1750     bool pointInPath = false;
1751     if (args.Length() == 2) {
1752         pointInPath = r->context->isPointInPath(args[0]->NumberValue(),
1753                                                 args[1]->NumberValue());
1754     }
1755
1756     return v8::Boolean::New(pointInPath);
1757 }
1758
1759 static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
1760 {
1761     V8THROW_ERROR("Context2D::drawFocusRing is not supported")
1762     return args.This();
1763 }
1764
1765 static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
1766 {
1767     V8THROW_ERROR("Context2D::setCaretSelectionRect is not supported")
1768     return args.This();
1769 }
1770
1771 static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
1772 {
1773     V8THROW_ERROR("Context2D::caretBlinkRate is not supported")
1774
1775     return args.This();
1776 }
1777 // text
1778 /*!
1779   \qmlproperty string QtQuick2::Context2D::font
1780   Holds the current font settings, default value is "10px sans-serif".
1781
1782   See http://www.w3.org/TR/2dcontext/#dom-context-2d-font for details.
1783   */
1784 v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &info)
1785 {
1786     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1787     CHECK_CONTEXT(r)
1788
1789     QV8Engine *engine = V8ENGINE_ACCESSOR();
1790
1791     return engine->toString(r->context->m_fontString);
1792 }
1793
1794 static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1795 {
1796     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1797     CHECK_CONTEXT_SETTER(r)
1798
1799     QV8Engine *engine = V8ENGINE_ACCESSOR();
1800     QString fs = engine->toString(value);
1801     if (fs != r->context->m_fontString) {
1802         r->context->m_fontString = fs;
1803         QFont font = qt_font_from_string(fs);
1804         r->context->state.font = font;
1805     }
1806 }
1807
1808 /*!
1809   \qmlproperty string QtQuick2::Context2D::textAlign
1810
1811   Holds the current text alignment settings.
1812   The possible values are:
1813   \list
1814     \o start
1815     \o end
1816     \o left
1817     \o right
1818     \o center
1819   \endlist
1820   Other values are ignored. The default is start.
1821   */
1822 v8::Handle<v8::Value> ctx2d_textAlign(v8::Local<v8::String>, const v8::AccessorInfo &info)
1823 {
1824     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1825     CHECK_CONTEXT(r)
1826     QV8Engine *engine = V8ENGINE_ACCESSOR();
1827     switch (r->context->state.textAlign) {
1828     case QSGContext2D::Start:
1829         return engine->toString(QLatin1String("start"));
1830    case QSGContext2D::End:
1831         return engine->toString(QLatin1String("end"));
1832    case QSGContext2D::Left:
1833         return engine->toString(QLatin1String("left"));
1834    case QSGContext2D::Right:
1835         return engine->toString(QLatin1String("right"));
1836    case QSGContext2D::Center:
1837         return engine->toString(QLatin1String("center"));
1838     default:
1839         break;
1840     }
1841     return engine->toString(QLatin1String("start"));
1842 }
1843
1844 static void ctx2d_textAlign_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1845 {
1846     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1847     CHECK_CONTEXT_SETTER(r)
1848     QV8Engine *engine = V8ENGINE_ACCESSOR();
1849
1850     QString textAlign = engine->toString(value);
1851
1852     QSGContext2D::TextAlignType ta;
1853     if (textAlign == QLatin1String("start"))
1854         ta = QSGContext2D::Start;
1855     else if (textAlign == QLatin1String("end"))
1856         ta = QSGContext2D::End;
1857     else if (textAlign == QLatin1String("left"))
1858         ta = QSGContext2D::Left;
1859     else if (textAlign == QLatin1String("right"))
1860         ta = QSGContext2D::Right;
1861     else if (textAlign == QLatin1String("center"))
1862         ta = QSGContext2D::Center;
1863
1864     if (ta != r->context->state.textAlign) {
1865         r->context->state.textAlign = ta;
1866     }
1867 }
1868
1869 /*!
1870   \qmlproperty string QtQuick2::Context2D::textBaseline
1871
1872   Holds the current baseline alignment settings.
1873   The possible values are:
1874   \list
1875     \o top
1876     \o hanging
1877     \o middle
1878     \o alphabetic
1879     \o ideographic
1880     \o bottom
1881   \endlist
1882   Other values are ignored. The default value is "alphabetic".
1883   See http://www.w3.org/TR/2dcontext/#dom-context-2d-textbaseline for details.
1884   */
1885 v8::Handle<v8::Value> ctx2d_textBaseline(v8::Local<v8::String>, const v8::AccessorInfo &info)
1886 {
1887     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1888     CHECK_CONTEXT(r)
1889
1890     QV8Engine *engine = V8ENGINE_ACCESSOR();
1891     switch (r->context->state.textBaseline) {
1892     case QSGContext2D::Alphabetic:
1893         return engine->toString(QLatin1String("alphabetic"));
1894     case QSGContext2D::Hanging:
1895         return engine->toString(QLatin1String("hanging"));
1896     case QSGContext2D::Top:
1897         return engine->toString(QLatin1String("top"));
1898     case QSGContext2D::Bottom:
1899         return engine->toString(QLatin1String("bottom"));
1900     case QSGContext2D::Middle:
1901         return engine->toString(QLatin1String("middle"));
1902     default:
1903         break;
1904     }
1905     return engine->toString(QLatin1String("alphabetic"));
1906 }
1907
1908 static void ctx2d_textBaseline_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1909 {
1910     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1911     CHECK_CONTEXT_SETTER(r)
1912     QV8Engine *engine = V8ENGINE_ACCESSOR();
1913     QString textBaseline = engine->toString(value);
1914
1915     QSGContext2D::TextBaseLineType tb;
1916     if (textBaseline == QLatin1String("alphabetic"))
1917         tb = QSGContext2D::Alphabetic;
1918     else if (textBaseline == QLatin1String("hanging"))
1919         tb = QSGContext2D::Hanging;
1920     else if (textBaseline == QLatin1String("top"))
1921         tb = QSGContext2D::Top;
1922     else if (textBaseline == QLatin1String("bottom"))
1923         tb = QSGContext2D::Bottom;
1924     else if (textBaseline == QLatin1String("middle"))
1925         tb = QSGContext2D::Middle;
1926
1927     if (tb != r->context->state.textBaseline) {
1928         r->context->state.textBaseline = tb;
1929     }
1930 }
1931
1932 /*!
1933   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::fillText(text, x, y)
1934   Fills the given text at the given position.
1935   See http://www.w3.org/TR/2dcontext/#dom-context-2d-filltext for details.
1936   */
1937 static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
1938 {
1939     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1940     CHECK_CONTEXT(r)
1941
1942
1943     QV8Engine *engine = V8ENGINE();
1944
1945     if (args.Length() == 3) {
1946         QPainterPath textPath = r->context->createTextGlyphs(args[1]->NumberValue(),
1947                                                              args[2]->NumberValue(),
1948                                                              engine->toString(args[0]));
1949         r->context->buffer()->fill(textPath);
1950     }
1951
1952     return args.This();
1953 }
1954 /*!
1955   \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::strokeText(text, x, y)
1956   Strokes the given text at the given position.
1957   See http://www.w3.org/TR/2dcontext/#dom-context-2d-stroketext for details.
1958   */
1959 static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
1960 {
1961     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1962     CHECK_CONTEXT(r)
1963
1964
1965     QV8Engine *engine = V8ENGINE();
1966
1967     if (args.Length() == 3) {
1968         QPainterPath textPath = r->context->createTextGlyphs(args[1]->NumberValue(),
1969                                                              args[2]->NumberValue(),
1970                                                              engine->toString(args[0]));
1971         r->context->buffer()->stroke(textPath);
1972     }
1973
1974     return args.This();
1975 }
1976 /*!
1977   \qmlclass QtQuick2::TextMetrics
1978     \inqmlmodule QtQuick 2
1979     \since QtQuick 2.0
1980     \brief The Context2D TextMetrics interface.
1981     The TextMetrics object can be created by QtQuick2::Context2D::measureText method.
1982     See http://www.w3.org/TR/2dcontext/#textmetrics for more details.
1983
1984     \sa QtQuick2::Context2D::measureText
1985     \sa QtQuick2::TextMetrics::width
1986   */
1987
1988 /*!
1989   \qmlproperty int QtQuick2::TextMetrics::width
1990   Holds the advance width of the text that was passed to the QtQuick2::Context2D::measureText() method.
1991   This property is read only.
1992   See http://www.w3.org/TR/2dcontext/#dom-textmetrics-width for more details.
1993   */
1994
1995 /*!
1996   \qmlmethod variant QtQuick2::Context2D::measureText(text)
1997   Returns a TextMetrics object with the metrics of the given text in the current font.
1998   See http://www.w3.org/TR/2dcontext/#dom-context-2d-measuretext for details.
1999   */
2000 static v8::Handle<v8::Value> ctx2d_measureText(const v8::Arguments &args)
2001 {
2002     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2003     CHECK_CONTEXT(r)
2004
2005
2006     QV8Engine *engine = V8ENGINE();
2007
2008     if (args.Length() == 1) {
2009         QFontMetrics fm(r->context->state.font);
2010         uint width = fm.width(engine->toString(args[0]));
2011         v8::Local<v8::Object> tm = v8::Object::New();
2012         tm->Set(v8::String::New("width"), v8::Number::New(width));
2013         return tm;
2014     }
2015
2016     return v8::Undefined();
2017 }
2018
2019 // drawing images
2020 /*!
2021   \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy)
2022   Draws the given \a image on the canvas at position (\a dx, \a dy).
2023   Note:
2024      The \a image type can be an Image item or a image url. When given as Image
2025   type, if the image isn't fully loaded, will draw nothing. When given as url string,
2026   the context loads the image asynchorously and redraw the canvas when the image is loaded.
2027
2028   See http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage for more details.
2029   */
2030 /*!
2031   \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
2032   Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
2033   height \a dh.
2034   See http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage for more details.
2035   */
2036 /*!
2037   \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh)
2038   Draws the given item as \a image from source point (\a sx, \a sy) and source width \sw, source height \sh
2039   onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
2040   See http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage for more details.
2041   */
2042 static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
2043 {
2044     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2045     CHECK_CONTEXT(r)
2046
2047
2048     QV8Engine *engine = V8ENGINE();
2049
2050     //TODO: handle exceptions
2051
2052     qreal sx, sy, sw, sh, dx, dy, dw, dh;
2053
2054     if (args.Length() != 3 && args.Length() != 5 && args.Length() != 9) {
2055         //parameter error
2056         return args.This();
2057     }
2058
2059
2060     QImage image;
2061     if (args[0]->IsString()) {
2062         image = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
2063     } /*else if (args[0]->IsObject()) {
2064         QSGImage* imageItem = qobject_cast<QSGImage*>(engine->toQObject(args[0]->ToObject()));
2065         if (imageItem) {
2066             image = imageItem->pixmap().toImage();
2067         } else {
2068             //wrong image type
2069             return args.This();
2070         }
2071     }*/
2072     if (args.Length() == 3) {
2073         dx = args[1]->NumberValue();
2074         dy = args[2]->NumberValue();
2075         sx = 0;
2076         sy = 0;
2077         sw = image.isNull()? -1 : image.width();
2078         sh = image.isNull()? -1 : image.height();
2079         dw = sw;
2080         dh = sh;
2081     } else if (args.Length() == 5) {
2082         sx = 0;
2083         sy = 0;
2084         sw = image.isNull()? -1 : image.width();
2085         sh = image.isNull()? -1 : image.height();
2086         dx = args[1]->NumberValue();
2087         dy = args[2]->NumberValue();
2088         dw = args[3]->NumberValue();
2089         dh = args[4]->NumberValue();
2090     } else if (args.Length() == 9) {
2091         sx = args[1]->NumberValue();
2092         sy = args[2]->NumberValue();
2093         sw = args[3]->NumberValue();
2094         sh = args[4]->NumberValue();
2095         dx = args[5]->NumberValue();
2096         dy = args[6]->NumberValue();
2097         dw = args[7]->NumberValue();
2098         dh = args[8]->NumberValue();
2099     } else {
2100         //error
2101         return args.This();
2102     }
2103
2104     r->context->buffer()->drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
2105
2106     return args.This();
2107 }
2108
2109 // pixel manipulation
2110 /*!
2111   \qmlclass QtQuick2::CanvasImageData
2112
2113   */
2114 /*!
2115   \qmlproperty QtQuick2::CanvasImageData::width
2116   Holds the actual width dimension of the data in the ImageData object, in device pixels.
2117  */
2118 v8::Handle<v8::Value> ctx2d_imageData_width(v8::Local<v8::String>, const v8::AccessorInfo &args)
2119 {
2120     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2121     if (!r)
2122         return v8::Integer::New(0);
2123     return v8::Integer::New(r->image.width());
2124 }
2125
2126 /*!
2127   \qmlproperty QtQuick2::CanvasImageData::height
2128   Holds the actual height dimension of the data in the ImageData object, in device pixels.
2129   */
2130 v8::Handle<v8::Value> ctx2d_imageData_height(v8::Local<v8::String>, const v8::AccessorInfo &args)
2131 {
2132     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2133     if (!r)
2134         return v8::Integer::New(0);
2135
2136     return v8::Integer::New(r->image.height());
2137 }
2138
2139 /*!
2140   \qmlproperty QtQuick2::CanvasImageData::data
2141   Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
2142  */
2143 v8::Handle<v8::Value> ctx2d_imageData_data(v8::Local<v8::String>, const v8::AccessorInfo &args)
2144 {
2145     return args.This()->GetInternalField(0);
2146 }
2147
2148 static v8::Handle<v8::Value> ctx2d_imageData_mirror(const v8::Arguments &args)
2149 {
2150     bool horizontal = false, vertical = true;
2151     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2152
2153     if (!r) {
2154         //error
2155         return v8::Undefined();
2156     }
2157
2158     if (args.Length() > 2) {
2159       //error
2160       return v8::Undefined();
2161     }
2162
2163     if (args.Length() == 1) {
2164         horizontal = args[0]->BooleanValue();
2165     } else if (args.Length() == 2) {
2166         horizontal = args[0]->BooleanValue();
2167         vertical = args[1]->BooleanValue();
2168     }
2169
2170     r->image = r->image.mirrored(horizontal, vertical);
2171     return args.This();
2172 }
2173
2174
2175 static v8::Handle<v8::Value> ctx2d_imageData_filter(const v8::Arguments &args)
2176 {
2177     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2178
2179     if (!r) {
2180         //error
2181         return v8::Undefined();
2182     }
2183
2184     if (args.Length() >= 1) {
2185         int filterFlag = args[0]->IntegerValue();
2186         switch(filterFlag) {
2187         case QSGCanvasItem::GrayScale :
2188         {
2189             for (int y = 0; y < r->image.height(); ++y) {
2190               QRgb *row = (QRgb*)r->image.scanLine(y);
2191               for (int x = 0; x < r->image.width(); ++x) {
2192                   unsigned char* rgb = ((unsigned char*)&row[x]);
2193                   rgb[0] = rgb[1] = rgb[2] = qGray(rgb[0], rgb[1], rgb[2]);
2194               }
2195             }
2196         }
2197             break;
2198         case QSGCanvasItem::Threshold :
2199         {
2200             int threshold = 127;
2201             if (args.Length() > 1)
2202                 threshold = args[1]->IntegerValue();
2203
2204             for (int y = 0; y < r->image.height(); ++y) {
2205               QRgb *row = (QRgb*)r->image.scanLine(y);
2206               for (int x = 0; x < r->image.width(); ++x) {
2207                   unsigned char* rgb = ((unsigned char*)&row[x]);
2208                   unsigned char v = qGray(rgb[0], rgb[1], rgb[2]) >= threshold ? 255 : 0;
2209                   rgb[0] = rgb[1] = rgb[2] = v;
2210               }
2211             }
2212         }
2213             break;
2214         case QSGCanvasItem::Brightness :
2215         {
2216             int adjustment = 1;
2217             if (args.Length() > 1)
2218                 adjustment = args[1]->IntegerValue();
2219
2220             for (int y = 0; y < r->image.height(); ++y) {
2221               QRgb *row = (QRgb*)r->image.scanLine(y);
2222               for (int x = 0; x < r->image.width(); ++x) {
2223                 ((unsigned char*)&row[x])[0] += adjustment;
2224                 ((unsigned char*)&row[x])[1] += adjustment;
2225                 ((unsigned char*)&row[x])[2] += adjustment;
2226               }
2227             }
2228         }
2229             break;
2230         case QSGCanvasItem::Invert :
2231         {
2232             r->image.invertPixels();
2233         }
2234             break;
2235         case QSGCanvasItem::Blur :
2236         {
2237             QImage blurred(r->image.size(), QImage::Format_ARGB32);
2238             qreal blur = 10;
2239             if (args.Length() > 1)
2240                 blur = args[1]->NumberValue();
2241
2242             blurred.fill(Qt::transparent);
2243             QPainter blurPainter(&blurred);
2244             qt_blurImage(&blurPainter, r->image, blur, true, false);
2245             blurPainter.end();
2246             r->image = blurred;
2247         }
2248             break;
2249         case QSGCanvasItem::Opaque :
2250         {
2251             for (int y = 0; y < r->image.height(); ++y) {
2252               QRgb *row = (QRgb*)r->image.scanLine(y);
2253               for (int x = 0; x < r->image.width(); ++x) {
2254                 ((unsigned char*)&row[x])[3] = 255;
2255               }
2256             }
2257         }
2258             break;
2259         case QSGCanvasItem::Convolute :
2260         {
2261             if (args.Length() > 1 && args[1]->IsArray()) {
2262                 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(args[1]);
2263                 QVector<int> weights;
2264                 for (uint32_t i = 0; i < array->Length(); ++i)
2265                     weights.append(array->Get(i)->NumberValue());
2266                 int sides = qRound(qSqrt(weights.size()));
2267                 int half = qFloor(sides/2);
2268
2269                 QImage image = QImage(r->image.size(), QImage::Format_ARGB32);
2270                 int w = r->image.width();
2271                 int h = r->image.height();
2272                 for (int y = 0; y < image.height(); ++y) {
2273                   QRgb *dRow = (QRgb*)image.scanLine(y);
2274                   for (int x = 0; x < image.width(); ++x) {
2275                       unsigned char* dRgb = ((unsigned char*)&dRow[x]);
2276                       unsigned char red=0, green=0, blue=0, alpha=0;
2277                       int sy = y;
2278                       int sx = x;
2279
2280                       for (int cy=0; cy<sides; cy++) {
2281                          for (int cx=0; cx<sides; cx++) {
2282                            int scy = sy + cy - half;
2283                            int scx = sx + cx - half;
2284                            if (scy >= 0 && scy < w && scx >= 0 && scx < h) {
2285                               QRgb *sRow = (QRgb*)(r->image.scanLine(scy));
2286                               unsigned char* sRgb = ((unsigned char*)&sRow[scx]);
2287                               int wt = weights[cy*sides+cx];
2288                               red += sRgb[0] * wt;
2289                               green += sRgb[1] * wt;
2290                               blue += sRgb[2] * wt;
2291                               alpha += sRgb[3] * wt;
2292                            }
2293                          }
2294                       }
2295                       dRgb[0] = red;
2296                       dRgb[1] = green;
2297                       dRgb[2] = blue;
2298                       dRgb[3] = alpha;
2299                   }
2300                 }
2301                 r->image = image;
2302             } else {
2303                 //error
2304             }
2305         }
2306             break;
2307         default:
2308             break;
2309         }
2310     }
2311
2312     return args.This();
2313 }
2314 /*!
2315   \qmlclass QtQuick2::CanvasPixelArray
2316   The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
2317   See http://www.w3.org/TR/2dcontext/#canvaspixelarray for more details.
2318   */
2319
2320 /*!
2321   \qmlproperty QtQuick2::CanvasPixelArray::length
2322   The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
2323   The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
2324  */
2325 v8::Handle<v8::Value> ctx2d_pixelArray_length(v8::Local<v8::String>, const v8::AccessorInfo &args)
2326 {
2327     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
2328     if (!r || r->image.isNull()) return v8::Undefined();
2329
2330     return v8::Integer::New(r->image.width() * r->image.height() * 4);
2331 }
2332
2333 v8::Handle<v8::Value> ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args)
2334 {
2335     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
2336
2337     if (r && index && index < r->image.width() * r->image.height() * 4) {
2338         const int w = r->image.width();
2339         const int h = r->image.height();
2340         const int row = (index / 4) / w;
2341         const int col = (index / 4) % w;
2342         const QRgb* pixel = reinterpret_cast<const QRgb*>(r->image.constScanLine(row));
2343         pixel += col;
2344         switch (index % 4) {
2345         case 0:
2346             return v8::Integer::New(qRed(*pixel));
2347         case 1:
2348             return v8::Integer::New(qGreen(*pixel));
2349         case 2:
2350             return v8::Integer::New(qBlue(*pixel));
2351         case 3:
2352             return v8::Integer::New(qAlpha(*pixel));
2353         }
2354     }
2355     return v8::Undefined();
2356 }
2357
2358 v8::Handle<v8::Value> ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
2359 {
2360     QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(info.This());
2361
2362     const int v = value->Uint32Value();
2363     if (r && index > 0 && index < r->image.width() * r->image.height() * 4 && v > 0 && v <= 255) {
2364         const int w = r->image.width();
2365         const int h = r->image.height();
2366         const int row = (index / 4) / w;
2367         const int col = (index / 4) % w;
2368
2369         QRgb* pixel = reinterpret_cast<QRgb*>(r->image.scanLine(row));
2370         pixel += col;
2371         switch (index % 4) {
2372         case 0:
2373             *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel));
2374             break;
2375         case 1:
2376             *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel));
2377             break;
2378         case 2:
2379             *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel));
2380             break;
2381         case 3:
2382             *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v);
2383             break;
2384         }
2385     }
2386     return v8::Undefined();
2387 }
2388 /*!
2389   \qmlmethod QtQuick2::CanvasImageData createImageData(real sw, real sh)
2390    Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
2391   */
2392 /*!
2393   \qmlmethod QtQuick2::CanvasImageData createImageData(QtQuick2::CanvasImageData imageData)
2394    Creates a CanvasImageData object with the same dimensions as the argument.
2395   */
2396 /*!
2397   \qmlmethod QtQuick2::CanvasImageData createImageData(Url imageUrl)
2398    Creates a CanvasImageData object with the given image loaded from \a imageUrl.
2399    Note:The \a imageUrl must be already loaded before this function call, if not, an empty
2400    CanvasImageData obect will be returned.
2401
2402    \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::isImageLoaded
2403   */
2404 static v8::Handle<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
2405 {
2406     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2407     CHECK_CONTEXT(r)
2408
2409
2410     QV8Engine *engine = V8ENGINE();
2411
2412     if (args.Length() == 1) {
2413         if (args[0]->IsObject()) {
2414             v8::Local<v8::Object> imgData = args[0]->ToObject();
2415             QV8Context2DPixelArrayResource *pa = v8_resource_cast<QV8Context2DPixelArrayResource>(imgData->GetInternalField(0)->ToObject());
2416             if (pa) {
2417                 qreal w = imgData->Get(v8::String::New("width"))->NumberValue();
2418                 qreal h = imgData->Get(v8::String::New("height"))->NumberValue();
2419                 return qt_create_image_data(w, h, engine, QImage());
2420             }
2421         } else if (args[0]->IsString()) {
2422             QImage image = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
2423             return qt_create_image_data(image.width(), image.height(), engine, image);
2424         }
2425     } else if (args.Length() == 2) {
2426         qreal w = args[0]->NumberValue();
2427         qreal h = args[1]->NumberValue();
2428         if (w > 0 && h > 0)
2429             return qt_create_image_data(w, h, engine, QImage());
2430     }
2431     return v8::Undefined();
2432 }
2433
2434 /*!
2435   \qmlmethod QtQuick2::CanvasImageData getImageData(real sx, real sy, real sw, real sh)
2436   Returns an CanvasImageData object containing the image data for the given rectangle of the canvas.
2437   */
2438 static v8::Handle<v8::Value> ctx2d_getImageData(const v8::Arguments &args)
2439 {
2440     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2441     CHECK_CONTEXT(r)
2442
2443     QV8Engine *engine = V8ENGINE();
2444     if (args.Length() == 4) {
2445         qreal x = args[0]->NumberValue();
2446         qreal y = args[1]->NumberValue();
2447         qreal w = args[2]->NumberValue();
2448         qreal h = args[3]->NumberValue();
2449         QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h));
2450         if (image.format() != QImage::Format_ARGB32)
2451             image = image.convertToFormat(QImage::Format_ARGB32);
2452         v8::Local<v8::Object> imageData = qt_create_image_data(w, h, engine, image);
2453
2454         return imageData;
2455     }
2456     return v8::Null();
2457 }
2458
2459 /*!
2460   \qmlmethod QtQuick2::Context2D putImageData(QtQuick2::CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
2461   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.
2462   */
2463 static v8::Handle<v8::Value> ctx2d_putImageData(const v8::Arguments &args)
2464 {
2465     QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2466     CHECK_CONTEXT(r)
2467     if (args.Length() != 3 && args.Length() != 7)
2468         return v8::Undefined();
2469
2470     if (args[0]->IsNull() || !args[0]->IsObject()) {
2471         V8THROW_ERROR("Context2D::putImageData, the image data type mismatch");
2472     }
2473     qreal dx = args[1]->NumberValue();
2474     qreal dy = args[2]->NumberValue();
2475     qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
2476
2477     v8::Local<v8::Object> imageData = args[0]->ToObject();
2478     QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast<QV8Context2DPixelArrayResource>(imageData->Get(v8::String::New("data"))->ToObject());
2479     if (pixelArray) {
2480         w = imageData->Get(v8::String::New("width"))->NumberValue();
2481         h = imageData->Get(v8::String::New("height"))->NumberValue();
2482
2483         if (args.Length() == 7) {
2484             dirtyX = args[3]->NumberValue();
2485             dirtyY = args[4]->NumberValue();
2486             dirtyWidth = args[5]->NumberValue();
2487             dirtyHeight = args[6]->NumberValue();
2488             if (dirtyWidth < 0) {
2489                 dirtyX = dirtyX+dirtyWidth;
2490                 dirtyWidth = -dirtyWidth;
2491             }
2492
2493             if (dirtyHeight < 0) {
2494                 dirtyY = dirtyY+dirtyHeight;
2495                 dirtyHeight = -dirtyHeight;
2496             }
2497
2498             if (dirtyX < 0) {
2499                 dirtyWidth = dirtyWidth+dirtyX;
2500                 dirtyX = 0;
2501             }
2502
2503             if (dirtyY < 0) {
2504                 dirtyHeight = dirtyHeight+dirtyY;
2505                 dirtyY = 0;
2506             }
2507
2508             if (dirtyX+dirtyWidth > w) {
2509                 dirtyWidth = w - dirtyX;
2510             }
2511
2512             if (dirtyY+dirtyHeight > h) {
2513                 dirtyHeight = h - dirtyY;
2514             }
2515
2516             if (dirtyWidth <=0 || dirtyHeight <= 0)
2517                 return args.This();
2518         } else {
2519             dirtyX = 0;
2520             dirtyY = 0;
2521             dirtyWidth = w;
2522             dirtyHeight = h;
2523         }
2524
2525         QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2526         r->context->buffer()->drawImage(image, dirtyX, dirtyY, dirtyWidth, dirtyHeight, dx, dy, dirtyWidth, dirtyHeight);
2527     }
2528     return args.This();
2529 }
2530
2531 /*!
2532   \qmlclass QtQuick2::CanvasGradient
2533     \inqmlmodule QtQuick 2
2534     \since QtQuick 2.0
2535     \brief The Context2D opaque CanvasGradient interface.
2536   */
2537
2538 /*!
2539   \qmlmethod QtQuick2::CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color)
2540   Adds a color stop with the given color to the gradient at the given offset.
2541   0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
2542   */
2543 static v8::Handle<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &args)
2544 {
2545     QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(args.This());
2546     if (!style)
2547         V8THROW_ERROR("Not a CanvasGradient object");
2548
2549     QV8Engine *engine = V8ENGINE();
2550
2551     if (args.Length() == 2) {
2552
2553         if (!style->brush.gradient())
2554             V8THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
2555         QGradient gradient = *(style->brush.gradient());
2556         qreal pos = args[0]->NumberValue();
2557         QColor color = qt_color_from_string(engine->toString(args[1]));
2558         if (pos < 0.0 || pos > 1.0) {
2559             //Throws an INDEX_SIZE_ERR exception
2560             V8THROW_ERROR("CanvasGradient: parameter offset out of range");
2561         }
2562
2563         if (color.isValid()) {
2564             gradient.setColorAt(pos, color);
2565         } else {
2566             //Throws a SYNTAX_ERR exception
2567             V8THROW_ERROR("CanvasGradient: parameter color is not a valid color string");
2568         }
2569         style->brush = gradient;
2570     }
2571
2572     return args.This();
2573 }
2574
2575
2576 void QSGContext2D::beginPath()
2577 {
2578     m_path = QPainterPath();
2579     m_path.setFillRule(state.fillRule);
2580 }
2581
2582 void QSGContext2D::closePath()
2583 {
2584     if (m_path.isEmpty())
2585         return;
2586
2587     QRectF boundRect = m_path.boundingRect();
2588     if (boundRect.width() || boundRect.height())
2589         m_path.closeSubpath();
2590 }
2591
2592 void QSGContext2D::moveTo( qreal x, qreal y)
2593 {
2594     m_path.moveTo(state.matrix.map(QPointF(x, y)));
2595 }
2596
2597 void QSGContext2D::lineTo( qreal x, qreal y)
2598 {
2599     m_path.lineTo(state.matrix.map(QPointF(x, y)));
2600 }
2601
2602 void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
2603                                            qreal x, qreal y)
2604 {
2605     m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
2606                       state.matrix.map(QPointF(x, y)));
2607 }
2608
2609 void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
2610                                         qreal cp2x, qreal cp2y,
2611                                         qreal x, qreal y)
2612 {
2613     m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
2614                        state.matrix.map(QPointF(cp2x, cp2y)),
2615                        state.matrix.map(QPointF(x, y)));
2616 }
2617
2618 void QSGContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
2619 {
2620     QPointF p0(m_path.currentPosition());
2621
2622     QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
2623     QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
2624     float p1p0_length = qSqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y());
2625     float p1p2_length = qSqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y());
2626
2627     double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length);
2628
2629     // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
2630     // We could have used areCollinear() here, but since we're reusing
2631     // the variables computed above later on we keep this logic.
2632     if (qFuzzyCompare(qAbs(cos_phi), 1.0)) {
2633         m_path.lineTo(p1);
2634         return;
2635     }
2636
2637     float tangent = radius / tan(acos(cos_phi) / 2);
2638     float factor_p1p0 = tangent / p1p0_length;
2639     QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
2640
2641     QPointF orth_p1p0(p1p0.y(), -p1p0.x());
2642     float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y());
2643     float factor_ra = radius / orth_p1p0_length;
2644
2645     // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
2646     double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length);
2647     if (cos_alpha < 0.f)
2648         orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
2649
2650     QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
2651
2652     // calculate angles for addArc
2653     orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
2654     float sa = acos(orth_p1p0.x() / orth_p1p0_length);
2655     if (orth_p1p0.y() < 0.f)
2656         sa = 2 * Q_PI - sa;
2657
2658     // anticlockwise logic
2659     bool anticlockwise = false;
2660
2661     float factor_p1p2 = tangent / p1p2_length;
2662     QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
2663     QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
2664     float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y());
2665     float ea = acos(orth_p1p2.x() / orth_p1p2_length);
2666     if (orth_p1p2.y() < 0)
2667         ea = 2 * Q_PI - ea;
2668     if ((sa > ea) && ((sa - ea) < Q_PI))
2669         anticlockwise = true;
2670     if ((sa < ea) && ((ea - sa) > Q_PI))
2671         anticlockwise = true;
2672
2673     arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false);
2674 }
2675
2676 void QSGContext2D::arcTo(qreal x1, qreal y1,
2677                                 qreal x2, qreal y2,
2678                                 qreal radius)
2679 {
2680     QPointF st  = state.matrix.map(QPointF(x1, y1));
2681     QPointF end = state.matrix.map(QPointF(x2, y2));
2682
2683     if (!m_path.elementCount()) {
2684         m_path.moveTo(st);
2685     } else if (st == m_path.currentPosition() || st == end || !radius) {
2686         m_path.lineTo(st);
2687     } else {
2688         addArcTo(st, end, radius);
2689     }
2690 }
2691
2692 void QSGContext2D::rect(qreal x, qreal y,
2693                                qreal w, qreal h)
2694 {
2695     m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h)));
2696 }
2697
2698 void QSGContext2D::roundedRect(qreal x, qreal y,
2699                                qreal w, qreal h,
2700                                qreal xr, qreal yr)
2701 {
2702     QPainterPath path;
2703     path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
2704     m_path.addPath(state.matrix.map(path));
2705 }
2706
2707 void QSGContext2D::ellipse(qreal x, qreal y,
2708                            qreal w, qreal h)
2709 {
2710     QPainterPath path;
2711     path.addEllipse(x, y, w, h);
2712     m_path.addPath(state.matrix.map(path));
2713 }
2714
2715 void QSGContext2D::text(const QString& str, qreal x, qreal y)
2716 {
2717     QPainterPath path;
2718     path.addText(x, y, state.font, str);
2719     m_path.addPath(state.matrix.map(path));
2720 }
2721
2722 void QSGContext2D::arc(qreal xc,
2723                        qreal yc,
2724                        qreal radius,
2725                        qreal sar,
2726                        qreal ear,
2727                        bool antiClockWise,
2728                        bool transform)
2729 {
2730
2731     if (transform) {
2732         QPointF point = state.matrix.map(QPointF(xc, yc));
2733         xc = point.x();
2734         yc = point.y();
2735     }
2736     //### HACK
2737
2738     // In Qt we don't switch the coordinate system for degrees
2739     // and still use the 0,0 as bottom left for degrees so we need
2740     // to switch
2741     sar = -sar;
2742     ear = -ear;
2743     antiClockWise = !antiClockWise;
2744     //end hack
2745
2746     float sa = DEGREES(sar);
2747     float ea = DEGREES(ear);
2748
2749     double span = 0;
2750
2751     double xs     = xc - radius;
2752     double ys     = yc - radius;
2753     double width  = radius*2;
2754     double height = radius*2;
2755     if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360)))
2756         // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
2757         // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
2758         // circumference of this circle.
2759         span = 360;
2760     else {
2761         if (!antiClockWise && (ea < sa)) {
2762             span += 360;
2763         } else if (antiClockWise && (sa < ea)) {
2764             span -= 360;
2765         }
2766         //### this is also due to switched coordinate system
2767         // we would end up with a 0 span instead of 360
2768         if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
2769               qFuzzyCompare(qAbs(span), 360))) {
2770             span   += ea - sa;
2771         }
2772         if (!m_path.elementCount())
2773             m_path.moveTo(xs, ys);
2774     }
2775
2776
2777     if (transform) {
2778         QPointF currentPos = m_path.currentPosition();
2779         QPointF startPos = QPointF(xc + radius  * qCos(sar),
2780                                    yc - radius  * qSin(sar));
2781         if (currentPos != startPos)
2782             m_path.lineTo(startPos);
2783     }
2784
2785     m_path.arcTo(xs, ys, width, height, sa, span);
2786 }
2787
2788 int baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics)
2789 {
2790     int offset = 0;
2791     switch (value) {
2792     case QSGContext2D::QSGContext2D::Top:
2793         break;
2794     case QSGContext2D::QSGContext2D::Alphabetic:
2795     case QSGContext2D::QSGContext2D::Middle:
2796     case QSGContext2D::QSGContext2D::Hanging:
2797         offset = metrics.ascent();
2798         break;
2799     case QSGContext2D::QSGContext2D::Bottom:
2800         offset = metrics.height();
2801        break;
2802     }
2803     return offset;
2804 }
2805
2806 static int textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
2807 {
2808     int offset = 0;
2809     if (value == QSGContext2D::Start)
2810         value = QApplication::layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right;
2811     else if (value == QSGContext2D::End)
2812         value = QApplication::layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left;
2813     switch (value) {
2814     case QSGContext2D::QSGContext2D::Center:
2815         offset = metrics.width(text)/2;
2816         break;
2817     case QSGContext2D::QSGContext2D::Right:
2818         offset = metrics.width(text);
2819     case QSGContext2D::QSGContext2D::Left:
2820     default:
2821         break;
2822     }
2823     return offset;
2824 }
2825
2826
2827 QImage QSGContext2D::createImage(const QUrl& url)
2828 {
2829     return m_canvas->loadedImage(url);
2830 }
2831
2832 QPainterPath QSGContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
2833 {
2834     const QFontMetrics metrics(state.font);
2835     int yoffset = baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(state.textBaseline), metrics);
2836     int xoffset = textAlignOffset(static_cast<QSGContext2D::TextAlignType>(state.textAlign), metrics, text);
2837
2838     QPainterPath textPath;
2839
2840     textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
2841     textPath = state.matrix.map(textPath);
2842     return textPath;
2843 }
2844
2845
2846 bool QSGContext2D::isPointInPath(qreal x, qreal y) const
2847 {
2848     return m_path.contains(QPointF(x, y));
2849 }
2850
2851 QSGContext2D::QSGContext2D(QSGCanvasItem* item)
2852     : m_canvas(item)
2853     , m_buffer(new QSGContext2DCommandBuffer)
2854     , m_v8engine(0)
2855 {
2856     reset();
2857 }
2858
2859 QSGContext2D::~QSGContext2D()
2860 {
2861 }
2862
2863 v8::Handle<v8::Object> QSGContext2D::v8value() const
2864 {
2865     return m_v8value;
2866 }
2867
2868 QSGContext2DEngineData::QSGContext2DEngineData(QV8Engine *engine)
2869 {
2870     v8::HandleScope handle_scope;
2871     v8::Context::Scope scope(engine->context());
2872
2873     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
2874     ft->InstanceTemplate()->SetHasExternalResource(true);
2875     ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine));
2876     ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine));
2877     ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine));
2878     ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine));
2879     ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine));
2880     ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine));
2881     ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine));
2882     ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine));
2883     ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine));
2884     ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine));
2885     ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine));
2886     ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine));
2887     ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine));
2888     ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine));
2889     ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine));
2890     ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine));
2891     ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine));
2892     ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine));
2893     ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
2894     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine));
2895     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine));
2896     ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine));
2897     ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine));
2898     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine));
2899     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine));
2900     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine));
2901     ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine));
2902     ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine));
2903     ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine));
2904     ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine));
2905     ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine));
2906     ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine));
2907     ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine));
2908     ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine));
2909     ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine));
2910     ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine));
2911     ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine));
2912     ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine));
2913     ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine));
2914     ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine));
2915     ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine));
2916     ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine));
2917     ft->PrototypeTemplate()->Set(v8::String::New("roundedRect "), V8FUNCTION(ctx2d_roundedRect, engine));
2918     ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine));
2919     ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine));
2920     ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine));
2921     ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine));
2922     ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine));
2923     ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine));
2924     ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine));
2925     ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine));
2926     ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine));
2927     ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine));
2928     ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine));
2929     ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine));
2930     ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine));
2931     ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine));
2932     ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine));
2933     ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine));
2934     ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine));
2935
2936     constructorContext = qPersistentNew(ft->GetFunction());
2937
2938     v8::Local<v8::FunctionTemplate> ftGradient = v8::FunctionTemplate::New();
2939     ftGradient->InstanceTemplate()->SetHasExternalResource(true);
2940     ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine));
2941     constructorGradient = qPersistentNew(ftGradient->GetFunction());
2942
2943     v8::Local<v8::FunctionTemplate> ftPattern = v8::FunctionTemplate::New();
2944     ftPattern->InstanceTemplate()->SetHasExternalResource(true);
2945     constructorPattern = qPersistentNew(ftPattern->GetFunction());
2946
2947     v8::Local<v8::FunctionTemplate> ftPixelArray = v8::FunctionTemplate::New();
2948     ftPixelArray->InstanceTemplate()->SetHasExternalResource(true);
2949     ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine));
2950     ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine));
2951     constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction());
2952
2953     v8::Local<v8::FunctionTemplate> ftImageData = v8::FunctionTemplate::New();
2954     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine));
2955     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine));
2956     ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine));
2957     ftImageData->PrototypeTemplate()->Set(v8::String::New("mirror"), V8FUNCTION(ctx2d_imageData_mirror, engine));
2958     ftImageData->PrototypeTemplate()->Set(v8::String::New("filter"), V8FUNCTION(ctx2d_imageData_filter, engine));
2959     ftImageData->InstanceTemplate()->SetInternalFieldCount(1);
2960     constructorImageData = qPersistentNew(ftImageData->GetFunction());
2961 }
2962
2963 QSGContext2DEngineData::~QSGContext2DEngineData()
2964 {
2965     qPersistentDispose(constructorContext);
2966     qPersistentDispose(constructorGradient);
2967     qPersistentDispose(constructorPattern);
2968     qPersistentDispose(constructorImageData);
2969     qPersistentDispose(constructorPixelArray);
2970 }
2971
2972 void QSGContext2D::popState()
2973 {
2974     if (m_stateStack.isEmpty())
2975         return;
2976
2977     QSGContext2D::State newState = m_stateStack.pop();
2978
2979     if (state.matrix != newState.matrix)
2980         buffer()->updateMatrix(newState.matrix);
2981
2982     if (newState.globalAlpha != state.globalAlpha)
2983         buffer()->setGlobalAlpha(newState.globalAlpha);
2984
2985     if (newState.globalCompositeOperation != state.globalCompositeOperation)
2986         buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
2987
2988     if (newState.fillStyle != state.fillStyle)
2989         buffer()->setFillStyle(newState.fillStyle);
2990
2991     if (newState.strokeStyle != state.strokeStyle)
2992         buffer()->setStrokeStyle(newState.strokeStyle);
2993
2994     if (newState.lineWidth != state.lineWidth)
2995         buffer()->setLineWidth(newState.lineWidth);
2996
2997     if (newState.lineCap != state.lineCap)
2998         buffer()->setLineCap(newState.lineCap);
2999
3000     if (newState.lineJoin != state.lineJoin)
3001         buffer()->setLineJoin(newState.lineJoin);
3002
3003     if (newState.miterLimit != state.miterLimit)
3004         buffer()->setMiterLimit(newState.miterLimit);
3005
3006     if (newState.clipPath != state.clipPath)
3007         buffer()->clip(newState.clipPath);
3008
3009     if (newState.shadowBlur != state.shadowBlur)
3010         buffer()->setShadowBlur(newState.shadowBlur);
3011
3012     if (newState.shadowColor != state.shadowColor)
3013         buffer()->setShadowColor(newState.shadowColor);
3014
3015     if (newState.shadowOffsetX != state.shadowOffsetX)
3016         buffer()->setShadowOffsetX(newState.shadowOffsetX);
3017
3018     if (newState.shadowOffsetY != state.shadowOffsetY)
3019         buffer()->setShadowOffsetY(newState.shadowOffsetY);
3020
3021     state = newState;
3022 }
3023 void QSGContext2D::pushState()
3024 {
3025     m_stateStack.push(state);
3026 }
3027
3028 void QSGContext2D::reset()
3029 {
3030     QSGContext2D::State newState;
3031     newState.matrix = QTransform();
3032
3033     QPainterPath defaultClipPath;
3034     defaultClipPath.addRect(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
3035     newState.clipPath = defaultClipPath;
3036     newState.clipPath.setFillRule(Qt::WindingFill);
3037
3038     newState.strokeStyle = Qt::black;
3039     newState.fillStyle = Qt::black;
3040     newState.fillRule = Qt::WindingFill;
3041     newState.globalAlpha = 1.0;
3042     newState.lineWidth = 1;
3043     newState.lineCap = Qt::FlatCap;
3044     newState.lineJoin = Qt::MiterJoin;
3045     newState.miterLimit = 10;
3046     newState.shadowOffsetX = 0;
3047     newState.shadowOffsetY = 0;
3048     newState.shadowBlur = 0;
3049     newState.shadowColor = qRgba(0, 0, 0, 0);
3050     newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
3051     newState.font = QFont(QLatin1String("sans-serif"), 10);
3052     newState.textAlign = QSGContext2D::Start;
3053     newState.textBaseline = QSGContext2D::Alphabetic;
3054
3055     m_fontString = "";
3056     m_stateStack.clear();
3057     m_stateStack.push(newState);
3058     popState();
3059 }
3060
3061 void QSGContext2D::setV8Engine(QV8Engine *engine)
3062 {
3063     v8::HandleScope handle_scope;
3064     v8::Context::Scope scope(engine->context());
3065
3066     if (m_v8engine != engine) {
3067         m_v8engine = engine;
3068
3069         qPersistentDispose(m_v8value);
3070
3071         if (m_v8engine == 0)
3072             return;
3073
3074         QSGContext2DEngineData *ed = engineData(engine);
3075         m_v8value = qPersistentNew(ed->constructorContext->NewInstance());
3076         QV8Context2DResource *r = new QV8Context2DResource(engine);
3077         r->context = this;
3078         m_v8value->SetExternalResource(r);
3079     }
3080 }
3081
3082 QT_END_NAMESPACE