1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickcontext2d_p.h"
43 #include "qquickcontext2dcommandbuffer_p.h"
44 #include "qquickcanvasitem_p.h"
45 #include <private/qquickcontext2dtexture_p.h>
46 #include <private/qquickitem_p.h>
47 #include <QtQuick/private/qquickshadereffectsource_p.h>
48 #include <QtGui/qopenglframebufferobject.h>
50 #include <QtQuick/private/qsgcontext_p.h>
51 #include <private/qquicksvgparser_p.h>
52 #include <private/qquickpath_p.h>
54 #include <private/qquickimage_p_p.h>
56 #include <QtGui/qguiapplication.h>
58 #include <QtCore/qmath.h>
59 #include <private/qv8engine_p.h>
61 #include <qqmlengine.h>
62 #include <private/qv8domerrors_p.h>
63 #include <QtCore/qnumeric.h>
64 #include <private/qquickwindow_p.h>
65 #include <private/qquickwindowmanager_p.h>
74 \instantiates QQuickContext2D
75 \inqmlmodule QtQuick 2
76 \ingroup qtquick-canvas
78 \brief Provides 2D context for shapes on a Canvas item
80 The Context2D object can be created by \c Canvas item's \c getContext()
86 var ctx = canvas.getContext('2d');
91 The Context2D API implements the same \l
92 {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard} with
93 some enhanced features.
95 The Context2D API provides the rendering \b{context} which defines the
96 methods and attributes needed to draw on the \c Canvas item. The following
97 assigns the canvas rendering context to a \c{context} variable:
99 var context = mycanvas.getContext("2d")
102 The Context2D API renders the canvas as a coordinate system whose origin
103 (0,0) is at the top left corner, as shown in the figure below. Coordinates
104 increase along the \c{x} axis from left to right and along the \c{y} axis
105 from top to bottom of the canvas.
106 \image qml-item-canvas-context.gif
111 Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
113 static const double Q_PI = 3.14159265358979323846; // pi
115 #define DEGREES(t) ((t) * 180.0 / Q_PI)
117 #define CHECK_CONTEXT(r) if (!r || !r->context || !r->context->bufferValid()) \
118 V8THROW_ERROR("Not a Context2D object");
120 #define CHECK_CONTEXT_SETTER(r) if (!r || !r->context || !r->context->bufferValid()) \
121 V8THROW_ERROR_SETTER("Not a Context2D object");
122 #define qClamp(val, min, max) qMin(qMax(val, min), max)
123 #define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
124 QColor qt_color_from_string(v8::Local<v8::Value> name)
126 v8::String::AsciiValue str(name);
129 int len = str.length();
130 //rgb/hsl color string has at least 7 characters
131 if (!p || len > 255 || len <= 7)
134 bool isRgb(false), isHsl(false), hasAlpha(false);
137 while (isspace(*p)) p++;
138 if (strncmp(p, "rgb", 3) == 0)
140 else if (strncmp(p, "hsl", 3) == 0)
145 p+=3; //skip "rgb" or "hsl"
146 hasAlpha = (*p == 'a') ? true : false;
150 if (hasAlpha) ++p; //skip "a"
152 int rh, gs, bl, alpha = 255;
155 while (isspace(*p)) p++;
156 rh = strtol(p, &p, 10);
158 rh = qRound(rh/100.0 * 255);
161 if (*p++ != ',') return QColor();
164 while (isspace(*p)) p++;
165 gs = strtol(p, &p, 10);
167 gs = qRound(gs/100.0 * 255);
170 if (*p++ != ',') return QColor();
173 while (isspace(*p)) p++;
174 bl = strtol(p, &p, 10);
176 bl = qRound(bl/100.0 * 255);
181 if (*p++!= ',') return QColor();
182 while (isspace(*p)) p++;
184 alpha = qRound(qstrtod(p, const_cast<const char **>(&p), &ok) * 255);
187 if (*p != ')') return QColor();
189 return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
191 return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
196 QFont qt_font_from_string(const QString& fontString) {
198 // ### this is simplified and incomplete
199 // ### TODO:get code from Qt webkit
200 const QStringList tokens = fontString.split(QLatin1Char(' '));
201 foreach (const QString &token, tokens) {
202 if (token == QLatin1String("italic"))
203 font.setItalic(true);
204 else if (token == QLatin1String("bold"))
206 else if (token.endsWith(QLatin1String("px"))) {
207 QString number = token;
208 number.remove(QLatin1String("px"));
209 //font.setPointSizeF(number.trimmed().toFloat());
210 font.setPixelSize(number.trimmed().toInt());
212 font.setFamily(token);
220 class QQuickContext2DEngineData : public QV8Engine::Deletable
223 QQuickContext2DEngineData(QV8Engine *engine);
224 ~QQuickContext2DEngineData();
226 v8::Persistent<v8::Function> constructorContext;
227 v8::Persistent<v8::Function> constructorGradient;
228 v8::Persistent<v8::Function> constructorPattern;
229 v8::Persistent<v8::Function> constructorPixelArray;
230 v8::Persistent<v8::Function> constructorImageData;
233 V8_DEFINE_EXTENSION(QQuickContext2DEngineData, engineData)
235 class QV8Context2DResource : public QV8ObjectResource
237 V8_RESOURCE_TYPE(Context2DType)
239 QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e), context(0) {}
240 QQuickContext2D* context;
243 class QV8Context2DStyleResource : public QV8ObjectResource
245 V8_RESOURCE_TYPE(Context2DStyleType)
247 QV8Context2DStyleResource(QV8Engine *e)
248 : QV8ObjectResource(e)
249 , patternRepeatX(false)
250 , patternRepeatY(false)
253 bool patternRepeatX:1;
254 bool patternRepeatY:1;
257 class QV8Context2DPixelArrayResource : public QV8ObjectResource
259 V8_RESOURCE_TYPE(Context2DPixelArrayType)
261 QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {}
266 QImage qt_image_convolute_filter(const QImage& src, const QVector<qreal>& weights, int radius = 0)
268 int sides = radius ? radius : qRound(qSqrt(weights.size()));
269 int half = qFloor(sides/2);
271 QImage dst = QImage(src.size(), src.format());
273 int h = src.height();
274 for (int y = 0; y < dst.height(); ++y) {
275 QRgb *dr = (QRgb*)dst.scanLine(y);
276 for (int x = 0; x < dst.width(); ++x) {
277 unsigned char* dRgb = ((unsigned char*)&dr[x]);
278 unsigned char red=0, green=0, blue=0, alpha=0;
282 for (int cy=0; cy<sides; cy++) {
283 for (int cx=0; cx<sides; cx++) {
284 int scy = sy + cy - half;
285 int scx = sx + cx - half;
286 if (scy >= 0 && scy < w && scx >= 0 && scx < h) {
287 const QRgb *sr = (const QRgb*)(src.constScanLine(scy));
288 const unsigned char* sRgb = ((const unsigned char*)&sr[scx]);
289 qreal wt = radius ? weights[0] : weights[cy*sides+cx];
291 green += sRgb[1] * wt;
292 blue += sRgb[2] * wt;
293 alpha += sRgb[3] * wt;
306 void qt_image_boxblur(QImage& image, int radius, bool quality)
308 int passes = quality? 3: 1;
309 for (int i=0; i < passes; i++) {
310 image = qt_image_convolute_filter(image, QVector<qreal>() << 1.0/(radius * radius * 1.0), radius);
314 static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
316 if (compositeOperator == QLatin1String("source-over")) {
317 return QPainter::CompositionMode_SourceOver;
318 } else if (compositeOperator == QLatin1String("source-out")) {
319 return QPainter::CompositionMode_SourceOut;
320 } else if (compositeOperator == QLatin1String("source-in")) {
321 return QPainter::CompositionMode_SourceIn;
322 } else if (compositeOperator == QLatin1String("source-atop")) {
323 return QPainter::CompositionMode_SourceAtop;
324 } else if (compositeOperator == QLatin1String("destination-atop")) {
325 return QPainter::CompositionMode_DestinationAtop;
326 } else if (compositeOperator == QLatin1String("destination-in")) {
327 return QPainter::CompositionMode_DestinationIn;
328 } else if (compositeOperator == QLatin1String("destination-out")) {
329 return QPainter::CompositionMode_DestinationOut;
330 } else if (compositeOperator == QLatin1String("destination-over")) {
331 return QPainter::CompositionMode_DestinationOver;
332 } else if (compositeOperator == QLatin1String("lighter")) {
333 return QPainter::CompositionMode_Lighten;
334 } else if (compositeOperator == QLatin1String("copy")) {
335 return QPainter::CompositionMode_Source;
336 } else if (compositeOperator == QLatin1String("xor")) {
337 return QPainter::CompositionMode_Xor;
338 } else if (compositeOperator == QLatin1String("qt-clear")) {
339 return QPainter::CompositionMode_Clear;
340 } else if (compositeOperator == QLatin1String("qt-destination")) {
341 return QPainter::CompositionMode_Destination;
342 } else if (compositeOperator == QLatin1String("qt-multiply")) {
343 return QPainter::CompositionMode_Multiply;
344 } else if (compositeOperator == QLatin1String("qt-screen")) {
345 return QPainter::CompositionMode_Screen;
346 } else if (compositeOperator == QLatin1String("qt-overlay")) {
347 return QPainter::CompositionMode_Overlay;
348 } else if (compositeOperator == QLatin1String("qt-darken")) {
349 return QPainter::CompositionMode_Darken;
350 } else if (compositeOperator == QLatin1String("qt-lighten")) {
351 return QPainter::CompositionMode_Lighten;
352 } else if (compositeOperator == QLatin1String("qt-color-dodge")) {
353 return QPainter::CompositionMode_ColorDodge;
354 } else if (compositeOperator == QLatin1String("qt-color-burn")) {
355 return QPainter::CompositionMode_ColorBurn;
356 } else if (compositeOperator == QLatin1String("qt-hard-light")) {
357 return QPainter::CompositionMode_HardLight;
358 } else if (compositeOperator == QLatin1String("qt-soft-light")) {
359 return QPainter::CompositionMode_SoftLight;
360 } else if (compositeOperator == QLatin1String("qt-difference")) {
361 return QPainter::CompositionMode_Difference;
362 } else if (compositeOperator == QLatin1String("qt-exclusion")) {
363 return QPainter::CompositionMode_Exclusion;
365 return QPainter::CompositionMode_SourceOver;
368 static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
371 case QPainter::CompositionMode_SourceOver:
372 return QLatin1String("source-over");
373 case QPainter::CompositionMode_DestinationOver:
374 return QLatin1String("destination-over");
375 case QPainter::CompositionMode_Clear:
376 return QLatin1String("qt-clear");
377 case QPainter::CompositionMode_Source:
378 return QLatin1String("copy");
379 case QPainter::CompositionMode_Destination:
380 return QLatin1String("qt-destination");
381 case QPainter::CompositionMode_SourceIn:
382 return QLatin1String("source-in");
383 case QPainter::CompositionMode_DestinationIn:
384 return QLatin1String("destination-in");
385 case QPainter::CompositionMode_SourceOut:
386 return QLatin1String("source-out");
387 case QPainter::CompositionMode_DestinationOut:
388 return QLatin1String("destination-out");
389 case QPainter::CompositionMode_SourceAtop:
390 return QLatin1String("source-atop");
391 case QPainter::CompositionMode_DestinationAtop:
392 return QLatin1String("destination-atop");
393 case QPainter::CompositionMode_Xor:
394 return QLatin1String("xor");
395 case QPainter::CompositionMode_Plus:
396 return QLatin1String("plus");
397 case QPainter::CompositionMode_Multiply:
398 return QLatin1String("qt-multiply");
399 case QPainter::CompositionMode_Screen:
400 return QLatin1String("qt-screen");
401 case QPainter::CompositionMode_Overlay:
402 return QLatin1String("qt-overlay");
403 case QPainter::CompositionMode_Darken:
404 return QLatin1String("qt-darken");
405 case QPainter::CompositionMode_Lighten:
406 return QLatin1String("lighter");
407 case QPainter::CompositionMode_ColorDodge:
408 return QLatin1String("qt-color-dodge");
409 case QPainter::CompositionMode_ColorBurn:
410 return QLatin1String("qt-color-burn");
411 case QPainter::CompositionMode_HardLight:
412 return QLatin1String("qt-hard-light");
413 case QPainter::CompositionMode_SoftLight:
414 return QLatin1String("qt-soft-light");
415 case QPainter::CompositionMode_Difference:
416 return QLatin1String("qt-difference");
417 case QPainter::CompositionMode_Exclusion:
418 return QLatin1String("qt-exclusion");
426 static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image)
428 QQuickContext2DEngineData *ed = engineData(engine);
429 v8::Local<v8::Object> imageData = ed->constructorImageData->NewInstance();
430 QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine);
431 if (image.isNull()) {
432 r->image = QImage(w, h, QImage::Format_ARGB32);
433 r->image.fill(0x00000000);
435 Q_ASSERT(image.width() == w && image.height() == h);
436 r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
438 v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
439 pixelData->SetExternalResource(r);
441 imageData->SetInternalField(0, pixelData);
445 //static script functions
448 \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas
449 Holds the canvas item that the context paints on.
451 This property is read only.
453 static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::AccessorInfo &info)
455 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
459 QV8Engine *engine = V8ENGINE_ACCESSOR();
461 return engine->newQObject(r->context->canvas());
465 \qmlmethod object QtQuick2::Context2D::restore()
466 Pops the top state on the stack, restoring the context to that state.
470 static v8::Handle<v8::Value> ctx2d_restore(const v8::Arguments &args)
472 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
475 r->context->popState();
480 \qmlmethod object QtQuick2::Context2D::reset()
481 Resets the context state and properties to the default values.
483 static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args)
485 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
494 \qmlmethod object QtQuick2::Context2D::save()
495 Pushes the current state onto the state stack.
497 Before changing any state attributes, you should save the current state
498 for future reference. The context maintains a stack of drawing states.
499 Each state consists of the current transformation matrix, clipping region,
500 and values of the following attributes:
514 \li globalCompositeOperation
520 The current path is NOT part of the drawing state. The path can be reset by
521 invoking the beginPath() method.
523 static v8::Handle<v8::Value> ctx2d_save(const v8::Arguments &args)
525 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
528 r->context->pushState();
535 \qmlmethod object QtQuick2::Context2D::rotate(real angle)
536 Rotate the canvas around the current origin by \c angle in radians and clockwise direction.
538 ctx.rotate(Math.PI/2);
540 \image qml-item-canvas-rotate.png
542 The rotation transformation matrix is as follows:
544 \image qml-item-canvas-math-rotate.png
546 where the \c angle of rotation is in radians.
549 static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args)
551 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
554 if (args.Length() == 1)
555 r->context->rotate(args[0]->NumberValue());
560 \qmlmethod object QtQuick2::Context2D::scale(real x, real y)
561 Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors
562 to the current tranform matrix.
563 Where \c x is the scale factor in the horizontal direction and \c y is the scale factor in the
565 The following code doubles the horizontal size of an object drawn on the canvas and half its
570 \image qml-item-canvas-scale.png
573 static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args)
575 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
579 if (args.Length() == 2)
580 r->context->scale(args[0]->NumberValue(), args[1]->NumberValue());
585 \qmlmethod object QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f)
586 Changes the transformation matrix to the matrix given by the arguments as described below.
588 Modifying the transformation matrix directly enables you to perform scaling,
589 rotating, and translating transformations in a single step.
591 Each point on the canvas is multiplied by the matrix before anything is
592 drawn. The \l{HTML5 Canvas API} defines the transformation matrix as:
594 \image qml-item-canvas-math.png
597 \li \c{a} is the scale factor in the horizontal (x) direction
598 \image qml-item-canvas-scalex.png
599 \li \c{c} is the skew factor in the x direction
600 \image qml-item-canvas-canvas-skewx.png
601 \li \c{e} is the translation in the x direction
602 \image qml-item-canvas-canvas-translate.png
603 \li \c{b} is the skew factor in the y (vertical) direction
604 \image qml-item-canvas-canvas-skewy.png
605 \li \c{d} is the scale factor in the y direction
606 \image qml-item-canvas-canvas-scaley.png
607 \li \c{f} is the translation in the y direction
608 \image qml-item-canvas-canvas-translatey.png
609 \li the last row remains constant
611 The scale factors and skew factors are multiples; \c{e} and \c{f} are
612 coordinate space units, just like the units in the translate(x,y)
617 static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
619 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
623 if (args.Length() == 6)
624 r->context->setTransform( args[0]->NumberValue()
625 , args[1]->NumberValue()
626 , args[2]->NumberValue()
627 , args[3]->NumberValue()
628 , args[4]->NumberValue()
629 , args[5]->NumberValue());
635 \qmlmethod object QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f)
636 This method is very similar to setTransform(), but instead of replacing the old
637 tranform matrix, this method applies the given tranform matrix to the current matrix by mulitplying to it.
639 The setTransform(a, b, c, d, e, f) method actually resets the current transform to the identity matrix,
640 and then invokes the transform(a, b, c, d, e, f) method with the same arguments.
644 static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
646 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
650 if (args.Length() == 6)
651 r->context->transform( args[0]->NumberValue()
652 , args[1]->NumberValue()
653 , args[2]->NumberValue()
654 , args[3]->NumberValue()
655 , args[4]->NumberValue()
656 , args[5]->NumberValue());
662 \qmlmethod object QtQuick2::Context2D::translate(real x, real y)
663 Translates the origin of the canvas to point (\c x, \c y).
665 \c x is the horizontal distance that the origin is translated, in coordinate space units,
666 \c y is the vertical distance that the origin is translated, in coordinate space units.
667 Translating the origin enables you to draw patterns of different objects on the canvas
668 without having to measure the coordinates manually for each shape.
670 static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
672 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
676 if (args.Length() == 2)
677 r->context->translate(args[0]->NumberValue(), args[1]->NumberValue());
683 \qmlmethod object QtQuick2::Context2D::resetTransform()
684 Reset the transformation matrix to default value.
686 \sa transform(), setTransform(), reset()
688 static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
690 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
693 r->context->setTransform(1, 0, 0, 1, 0, 0);
700 \qmlmethod object QtQuick2::Context2D::shear(real sh, real sv )
701 Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction.
703 static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args)
705 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
708 if (args.Length() == 2)
709 r->context->shear(args[0]->NumberValue(), args[1]->NumberValue());
716 \qmlproperty real QtQuick2::Context2D::globalAlpha
717 Holds the the current alpha value applied to rendering operations.
718 The value must be in the range from 0.0 (fully transparent) to 1.0 (fully opque).
719 The default value is 1.0.
721 static v8::Handle<v8::Value> ctx2d_globalAlpha(v8::Local<v8::String>, const v8::AccessorInfo &info)
723 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
726 return v8::Number::New(r->context->state.globalAlpha);
729 static void ctx2d_globalAlpha_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
731 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
732 CHECK_CONTEXT_SETTER(r)
734 qreal globalAlpha = value->NumberValue();
736 if (!qIsFinite(globalAlpha))
739 if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) {
740 r->context->state.globalAlpha = globalAlpha;
741 r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha);
746 \qmlproperty string QtQuick2::Context2D::globalCompositeOperation
747 Holds the the current the current composition operation, from the list below:
749 \li source-atop - A atop B. Display the source image wherever both images are opaque.
750 Display the destination image wherever the destination image is opaque but the source image is transparent.
751 Display transparency elsewhere.
752 \li source-in - A in B. Display the source image wherever both the source image and destination image are opaque.
753 Display transparency elsewhere.
754 \li source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
755 Display transparency elsewhere.
756 \li source-over - (default) A over B. Display the source image wherever the source image is opaque.
757 Display the destination image elsewhere.
758 \li destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
759 \li destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
760 \li destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
761 \li destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
762 \li lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
763 \li copy - A (B is ignored). Display the source image instead of the destination image.
764 \li xor - A xor B. Exclusive OR of the source image and destination image.
767 Additionally, this property also accepts the compositon modes listed in QPainter::CompositionMode. According to the W3C standard, these
768 extension composition modes are provided as "vendorName-operationName" syntax, for example: QPainter::CompositionMode_Exclusion is provided as
771 static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
773 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
777 QV8Engine *engine = V8ENGINE_ACCESSOR();
779 return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation));
782 static void ctx2d_globalCompositeOperation_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
784 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
785 CHECK_CONTEXT_SETTER(r)
787 QV8Engine *engine = V8ENGINE_ACCESSOR();
790 QString mode = engine->toString(value);
791 QPainter::CompositionMode cm = qt_composite_mode_from_string(mode);
792 if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over"))
795 if (cm != r->context->state.globalCompositeOperation) {
796 r->context->state.globalCompositeOperation = cm;
797 r->context->buffer()->setGlobalCompositeOperation(cm);
803 \qmlproperty variant QtQuick2::Context2D::fillStyle
804 Holds the current style used for filling shapes.
805 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored.
806 This property accepts several color syntaxes:
808 \li 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)'
809 \li 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)'
810 \li 'hsl(hue, saturation, lightness)'
811 \li 'hsla(hue, saturation, lightness, alpha)'
812 \li '#RRGGBB' - for example: '#00FFCC'
813 \li Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0)
815 If the \c fillStyle or \l strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the
816 best performance, because it's already a valid QColor value, does not need to be parsed everytime.
818 The default value is '#000000'.
819 \sa createLinearGradient()
820 \sa createRadialGradient()
824 static v8::Handle<v8::Value> ctx2d_fillStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
826 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
829 QV8Engine *engine = V8ENGINE_ACCESSOR();
831 QColor color = r->context->state.fillStyle.color();
832 if (color.isValid()) {
833 if (color.alpha() == 255)
834 return engine->toString(color.name());
835 QString alphaString = QString::number(color.alphaF(), 'f');
836 while (alphaString.endsWith(QLatin1Char('0')))
838 if (alphaString.endsWith(QLatin1Char('.')))
839 alphaString += QLatin1Char('0');
840 return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
842 return r->context->m_fillStyle;
845 static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
847 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
848 CHECK_CONTEXT_SETTER(r)
850 QV8Engine *engine = V8ENGINE_ACCESSOR();
852 if (value->IsObject()) {
853 QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
854 if (color.isValid()) {
855 r->context->state.fillStyle = color;
856 r->context->buffer()->setFillStyle(color);
857 r->context->m_fillStyle = value;
859 QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
860 if (style && style->brush != r->context->state.fillStyle) {
861 r->context->state.fillStyle = style->brush;
862 r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
863 r->context->m_fillStyle = value;
864 r->context->state.fillPatternRepeatX = style->patternRepeatX;
865 r->context->state.fillPatternRepeatY = style->patternRepeatY;
868 } else if (value->IsString()) {
869 QColor color = qt_color_from_string(value);
870 if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
871 r->context->state.fillStyle = QBrush(color);
872 r->context->buffer()->setFillStyle(r->context->state.fillStyle);
873 r->context->m_fillStyle = value;
878 \qmlproperty enumeration QtQuick2::Context2D::fillRule
879 Holds the current fill rule used for filling shapes. The following fill rules supported:
884 Note: Unlike the QPainterPath, the Canvas API uses the winding fill as the default fill rule.
885 The fillRule property is part of the context rendering state.
889 static v8::Handle<v8::Value> ctx2d_fillRule(v8::Local<v8::String>, const v8::AccessorInfo &info)
891 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
893 QV8Engine *engine = V8ENGINE_ACCESSOR();
895 return engine->fromVariant(r->context->state.fillRule);
898 static void ctx2d_fillRule_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
900 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
901 CHECK_CONTEXT_SETTER(r)
903 QV8Engine *engine = V8ENGINE_ACCESSOR();
905 if ((value->IsString() && engine->toString(value) == QStringLiteral("WindingFill"))
906 ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) {
907 r->context->state.fillRule = Qt::WindingFill;
908 } else if ((value->IsString() && engine->toString(value) == QStringLiteral("OddEvenFill"))
909 ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) {
910 r->context->state.fillRule = Qt::OddEvenFill;
914 r->context->m_path.setFillRule(r->context->state.fillRule);
917 \qmlproperty variant QtQuick2::Context2D::strokeStyle
918 Holds the current color or style to use for the lines around shapes,
919 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object.
920 Invalid values are ignored.
922 The default value is '#000000'.
924 \sa createLinearGradient()
925 \sa createRadialGradient()
929 v8::Handle<v8::Value> ctx2d_strokeStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
931 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
934 QV8Engine *engine = V8ENGINE_ACCESSOR();
936 QColor color = r->context->state.strokeStyle.color();
937 if (color.isValid()) {
938 if (color.alpha() == 255)
939 return engine->toString(color.name());
940 QString alphaString = QString::number(color.alphaF(), 'f');
941 while (alphaString.endsWith(QLatin1Char('0')))
943 if (alphaString.endsWith(QLatin1Char('.')))
944 alphaString += QLatin1Char('0');
945 return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
947 return r->context->m_strokeStyle;
950 static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
952 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
953 CHECK_CONTEXT_SETTER(r)
955 QV8Engine *engine = V8ENGINE_ACCESSOR();
957 if (value->IsObject()) {
958 QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
959 if (color.isValid()) {
960 r->context->state.fillStyle = color;
961 r->context->buffer()->setStrokeStyle(color);
962 r->context->m_strokeStyle = value;
964 QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
965 if (style && style->brush != r->context->state.strokeStyle) {
966 r->context->state.strokeStyle = style->brush;
967 r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
968 r->context->m_strokeStyle = value;
969 r->context->state.strokePatternRepeatX = style->patternRepeatX;
970 r->context->state.strokePatternRepeatY = style->patternRepeatY;
974 } else if (value->IsString()) {
975 QColor color = qt_color_from_string(value);
976 if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
977 r->context->state.strokeStyle = QBrush(color);
978 r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
979 r->context->m_strokeStyle = value;
985 \qmlmethod object QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
986 Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between
987 the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
989 A gradient is a smooth transition between colors. There are two types of gradients: linear and radial.
990 Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between
991 to the gradient's starting and end points or circles.
993 \sa CanvasGradient::addColorStop()
994 \sa createRadialGradient()
995 \sa createConicalGradient()
1001 static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &args)
1003 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1007 QV8Engine *engine = V8ENGINE();
1009 if (args.Length() == 4) {
1010 QQuickContext2DEngineData *ed = engineData(engine);
1011 v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1012 QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1013 qreal x0 = args[0]->NumberValue();
1014 qreal y0 = args[1]->NumberValue();
1015 qreal x1 = args[2]->NumberValue();
1016 qreal y1 = args[3]->NumberValue();
1021 || !qIsFinite(y1)) {
1023 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
1026 r->brush = QLinearGradient(x0, y0, x1, y1);
1027 gradient->SetExternalResource(r);
1035 \qmlmethod object QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
1036 Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with
1037 origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1.
1039 \sa CanvasGradient::addColorStop()
1040 \sa createLinearGradient()
1041 \sa createConicalGradient()
1047 static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &args)
1049 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1053 QV8Engine *engine = V8ENGINE();
1055 if (args.Length() == 6) {
1056 QQuickContext2DEngineData *ed = engineData(engine);
1057 v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1058 QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1060 qreal x0 = args[0]->NumberValue();
1061 qreal y0 = args[1]->NumberValue();
1062 qreal r0 = args[2]->NumberValue();
1063 qreal x1 = args[3]->NumberValue();
1064 qreal y1 = args[4]->NumberValue();
1065 qreal r1 = args[5]->NumberValue();
1072 || !qIsFinite(y1)) {
1074 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
1077 if (r0 < 0 || r1 < 0)
1078 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
1081 r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
1082 gradient->SetExternalResource(r);
1090 \qmlmethod object QtQuick2::Context2D::createConicalGradient(real x, real y, real angle)
1091 Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y)
1092 with start angle \c angle in units of radians.
1094 \sa CanvasGradient::addColorStop()
1095 \sa createLinearGradient()
1096 \sa createRadialGradient()
1102 static v8::Handle<v8::Value> ctx2d_createConicalGradient(const v8::Arguments &args)
1104 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1108 QV8Engine *engine = V8ENGINE();
1110 if (args.Length() == 6) {
1111 QQuickContext2DEngineData *ed = engineData(engine);
1112 v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1113 QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1115 qreal x = args[0]->NumberValue();
1116 qreal y = args[1]->NumberValue();
1117 qreal angle = DEGREES(args[2]->NumberValue());
1118 if (!qIsFinite(x) || !qIsFinite(y)) {
1120 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
1123 if (!qIsFinite(angle)) {
1125 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
1128 r->brush = QConicalGradient(x, y, angle);
1129 gradient->SetExternalResource(r);
1136 \qmlmethod variant QtQuick2::Context2D::createPattern(color color, enumeration patternMode)
1137 This is a overload function.
1138 Returns a CanvasPattern object that uses the given \a color and \a patternMode.
1139 The valid pattern modes are:
1142 \li Qt.Dense1Pattern
1143 \li Qt.Dense2Pattern
1144 \li Qt.Dense3Pattern
1145 \li Qt.Dense4Pattern
1146 \li Qt.Dense5Pattern
1147 \li Qt.Dense6Pattern
1148 \li Qt.Dense7Pattern
1154 \li Qt.DiagCrossPattern
1159 \qmlmethod variant QtQuick2::Context2D::createPattern(Image image, string repetition)
1160 Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
1162 The \a image parameter must be a valid Image item, a valid CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception.
1164 The allowed values for \a repetition are:
1167 \li "repeat" - both directions
1168 \li "repeat-x - horizontal only
1169 \li "repeat-y" - vertical only
1170 \li "no-repeat" - neither
1173 If the repetition argument is empty or null, the value "repeat" is used.
1178 static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
1180 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1184 QV8Engine *engine = V8ENGINE();
1186 if (args.Length() == 2) {
1187 QQuickContext2DEngineData *ed = engineData(engine);
1188 QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine);
1190 QColor color = engine->toVariant(args[0], qMetaTypeId<QColor>()).value<QColor>();
1191 if (color.isValid()) {
1192 int patternMode = args[1]->IntegerValue();
1193 Qt::BrushStyle style = Qt::SolidPattern;
1194 if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
1195 style = static_cast<Qt::BrushStyle>(patternMode);
1197 styleResouce->brush = QBrush(color, style);
1199 QImage patternTexture;
1201 if (args[0]->IsObject()) {
1202 QV8Context2DPixelArrayResource *pixelData = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject());
1204 patternTexture = pixelData->image;
1207 patternTexture = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
1210 if (!patternTexture.isNull()) {
1211 styleResouce->brush.setTextureImage(patternTexture);
1213 QString repetition = engine->toString(args[1]);
1214 if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) {
1215 styleResouce->patternRepeatX = true;
1216 styleResouce->patternRepeatY = true;
1217 } else if (repetition == QStringLiteral("repeat-x")) {
1218 styleResouce->patternRepeatX = true;
1219 } else if (repetition == QStringLiteral("repeat-y")) {
1220 styleResouce->patternRepeatY = true;
1221 } else if (repetition == QStringLiteral("no-repeat")) {
1222 styleResouce->patternRepeatY = false;
1223 styleResouce->patternRepeatY = false;
1225 //TODO: exception: SYNTAX_ERR
1231 v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
1232 pattern->SetExternalResource(styleResouce);
1236 return v8::Undefined();
1241 \qmlproperty string QtQuick2::Context2D::lineCap
1242 Holds the the current line cap style.
1243 The possible line cap styles are:
1245 \li butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value.
1246 \li round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line.
1247 \li square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line.
1249 Other values are ignored.
1251 v8::Handle<v8::Value> ctx2d_lineCap(v8::Local<v8::String>, const v8::AccessorInfo &info)
1253 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1257 QV8Engine *engine = V8ENGINE_ACCESSOR();
1258 switch (r->context->state.lineCap) {
1260 return engine->toString(QLatin1String("round"));
1262 return engine->toString(QLatin1String("butt"));
1264 return engine->toString(QLatin1String("square"));
1268 return engine->toString(QLatin1String("butt"));;
1271 static void ctx2d_lineCap_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1273 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1274 CHECK_CONTEXT_SETTER(r)
1276 QV8Engine *engine = V8ENGINE_ACCESSOR();
1278 QString lineCap = engine->toString(value);
1279 Qt::PenCapStyle cap;
1280 if (lineCap == QLatin1String("round"))
1282 else if (lineCap == QLatin1String("butt"))
1284 else if (lineCap == QLatin1String("square"))
1285 cap = Qt::SquareCap;
1289 if (cap != r->context->state.lineCap) {
1290 r->context->state.lineCap = cap;
1291 r->context->buffer()->setLineCap(cap);
1296 \qmlproperty string QtQuick2::Context2D::lineJoin
1297 Holds the the current line join style. A join exists at any point in a subpath
1298 shared by two consecutive lines. When a subpath is closed, then a join also exists
1299 at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
1301 The possible line join styles are:
1303 \li bevel - this is all that is rendered at joins.
1304 \li round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins.
1305 \li miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style.
1307 Other values are ignored.
1309 v8::Handle<v8::Value> ctx2d_lineJoin(v8::Local<v8::String>, const v8::AccessorInfo &info)
1311 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1315 QV8Engine *engine = V8ENGINE_ACCESSOR();
1316 switch (r->context->state.lineJoin) {
1318 return engine->toString(QLatin1String("round"));
1320 return engine->toString(QLatin1String("bevel"));
1322 return engine->toString(QLatin1String("miter"));
1326 return engine->toString(QLatin1String("miter"));
1329 static void ctx2d_lineJoin_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1331 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1332 CHECK_CONTEXT_SETTER(r)
1334 QV8Engine *engine = V8ENGINE_ACCESSOR();
1336 QString lineJoin = engine->toString(value);
1337 Qt::PenJoinStyle join;
1338 if (lineJoin == QLatin1String("round"))
1339 join = Qt::RoundJoin;
1340 else if (lineJoin == QLatin1String("bevel"))
1341 join = Qt::BevelJoin;
1342 else if (lineJoin == QLatin1String("miter"))
1343 join = Qt::SvgMiterJoin;
1347 if (join != r->context->state.lineJoin) {
1348 r->context->state.lineJoin = join;
1349 r->context->buffer()->setLineJoin(join);
1354 \qmlproperty real QtQuick2::Context2D::lineWidth
1355 Holds the the current line width. Values that are not finite values greater than zero are ignored.
1357 v8::Handle<v8::Value> ctx2d_lineWidth(v8::Local<v8::String>, const v8::AccessorInfo &info)
1359 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1363 return v8::Number::New(r->context->state.lineWidth);
1366 static void ctx2d_lineWidth_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1368 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1369 CHECK_CONTEXT_SETTER(r)
1371 qreal w = value->NumberValue();
1373 if (w > 0 && qIsFinite(w) && w != r->context->state.lineWidth) {
1374 r->context->state.lineWidth = w;
1375 r->context->buffer()->setLineWidth(w);
1380 \qmlproperty real QtQuick2::Context2D::miterLimit
1381 Holds the current miter limit ratio.
1382 The default miter limit value is 10.0.
1384 v8::Handle<v8::Value> ctx2d_miterLimit(v8::Local<v8::String>, const v8::AccessorInfo &info)
1386 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1390 return v8::Number::New(r->context->state.miterLimit);
1393 static void ctx2d_miterLimit_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1395 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1396 CHECK_CONTEXT_SETTER(r)
1398 qreal ml = value->NumberValue();
1400 if (ml > 0 && qIsFinite(ml) && ml != r->context->state.miterLimit) {
1401 r->context->state.miterLimit = ml;
1402 r->context->buffer()->setMiterLimit(ml);
1408 \qmlproperty real QtQuick2::Context2D::shadowBlur
1409 Holds the current level of blur applied to shadows
1411 v8::Handle<v8::Value> ctx2d_shadowBlur(v8::Local<v8::String>, const v8::AccessorInfo &info)
1413 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1417 return v8::Number::New(r->context->state.shadowBlur);
1420 static void ctx2d_shadowBlur_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1422 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1423 CHECK_CONTEXT_SETTER(r)
1424 qreal blur = value->NumberValue();
1426 if (blur > 0 && qIsFinite(blur) && blur != r->context->state.shadowBlur) {
1427 r->context->state.shadowBlur = blur;
1428 r->context->buffer()->setShadowBlur(blur);
1433 \qmlproperty string QtQuick2::Context2D::shadowColor
1434 Holds the current shadow color.
1436 v8::Handle<v8::Value> ctx2d_shadowColor(v8::Local<v8::String>, const v8::AccessorInfo &info)
1438 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1442 QV8Engine *engine = V8ENGINE_ACCESSOR();
1444 return engine->toString(r->context->state.shadowColor.name());
1447 static void ctx2d_shadowColor_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1449 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1450 CHECK_CONTEXT_SETTER(r)
1452 QColor color = qt_color_from_string(value);
1454 if (color.isValid() && color != r->context->state.shadowColor) {
1455 r->context->state.shadowColor = color;
1456 r->context->buffer()->setShadowColor(color);
1462 \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX
1463 Holds the current shadow offset in the positive horizontal distance.
1467 v8::Handle<v8::Value> ctx2d_shadowOffsetX(v8::Local<v8::String>, const v8::AccessorInfo &info)
1469 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1473 return v8::Number::New(r->context->state.shadowOffsetX);
1476 static void ctx2d_shadowOffsetX_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1478 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1479 CHECK_CONTEXT_SETTER(r)
1481 qreal offsetX = value->NumberValue();
1482 if (qIsFinite(offsetX) && offsetX != r->context->state.shadowOffsetX) {
1483 r->context->state.shadowOffsetX = offsetX;
1484 r->context->buffer()->setShadowOffsetX(offsetX);
1488 \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY
1489 Holds the current shadow offset in the positive vertical distance.
1493 v8::Handle<v8::Value> ctx2d_shadowOffsetY(v8::Local<v8::String>, const v8::AccessorInfo &info)
1495 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1499 return v8::Number::New(r->context->state.shadowOffsetY);
1502 static void ctx2d_shadowOffsetY_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1504 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1505 CHECK_CONTEXT_SETTER(r)
1507 qreal offsetY = value->NumberValue();
1508 if (qIsFinite(offsetY) && offsetY != r->context->state.shadowOffsetY) {
1509 r->context->state.shadowOffsetY = offsetY;
1510 r->context->buffer()->setShadowOffsetY(offsetY);
1514 v8::Handle<v8::Value> ctx2d_path(v8::Local<v8::String>, const v8::AccessorInfo &info)
1516 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1518 return r->context->m_v8path;
1521 static void ctx2d_path_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1523 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1524 CHECK_CONTEXT_SETTER(r)
1525 QV8Engine *engine = V8ENGINE_ACCESSOR();
1527 r->context->beginPath();
1528 if (value->IsObject()) {
1529 QQuickPath* path = qobject_cast<QQuickPath*>(engine->toQObject(value));
1531 r->context->m_path = path->path();
1533 QString path = engine->toString(value->ToString());
1534 QQuickSvgParser::parsePathDataFast(path, r->context->m_path);
1536 r->context->m_v8path = value;
1541 \qmlmethod object QtQuick2::Context2D::clearRect(real x, real y, real w, real h)
1542 Clears all pixels on the canvas in the given rectangle to transparent black.
1544 static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
1546 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1550 if (args.Length() == 4)
1551 r->context->clearRect(args[0]->NumberValue(),
1552 args[1]->NumberValue(),
1553 args[2]->NumberValue(),
1554 args[3]->NumberValue());
1559 \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1560 Paint the specified rectangular area using the fillStyle.
1564 static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
1566 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1569 if (args.Length() == 4)
1570 r->context->fillRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1575 \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1576 Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin,
1577 and (if appropriate) miterLimit attributes.
1584 static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
1586 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1589 if (args.Length() == 4)
1590 r->context->strokeRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1595 // Complex shapes (paths) API
1597 \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
1598 Adds an arc to the current subpath that lies on the circumference of the circle whose center is at the point (\c x,\cy) and whose radius is \c radius.
1599 \image qml-item-canvas-arcTo2.png
1601 {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc}
1603 static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
1605 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1608 if (args.Length() >= 5) {
1609 bool antiClockwise = false;
1611 if (args.Length() == 6)
1612 antiClockwise = args[5]->BooleanValue();
1614 qreal radius = args[2]->NumberValue();
1616 if (qIsFinite(radius) && radius < 0)
1617 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1619 r->context->arc(args[0]->NumberValue(),
1620 args[1]->NumberValue(),
1622 args[3]->NumberValue(),
1623 args[4]->NumberValue(),
1631 \qmlmethod object QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius)
1633 Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line.
1634 To draw an arc, you begin with the same steps your followed to create a line:
1636 \li Call the context.beginPath() method to set a new path.
1637 \li Call the context.moveTo(\c x, \c y) method to set your starting position on the canvas at the point (\c x,\c y).
1638 \li To draw an arc or circle, call the context.arcTo(\c x1, \c y1, \c x2, \c y2,\c radius) method.
1639 This adds an arc with starting point (\c x1,\c y1), ending point (\c x2, \c y2), and radius \c radius to the current subpath and connects
1640 it to the previous subpath by a straight line.
1642 \image qml-item-canvas-arcTo.png
1643 Both startAngle and endAngle are measured from the x axis in units of radians.
1645 \image qml-item-canvas-startAngle.png
1646 The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction.
1647 \sa arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d
1648 context standard for arcTo}
1650 static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
1652 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1655 if (args.Length() == 5) {
1656 qreal radius = args[4]->NumberValue();
1658 if (qIsFinite(radius) && radius < 0)
1659 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1661 r->context->arcTo(args[0]->NumberValue(),
1662 args[1]->NumberValue(),
1663 args[2]->NumberValue(),
1664 args[3]->NumberValue(),
1672 \qmlmethod object QtQuick2::Context2D::beginPath()
1674 Resets the current path to a new path.
1676 static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
1678 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1682 r->context->beginPath();
1688 \qmlmethod object QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
1690 Adds a cubic bezier curve between the current position and the given endPoint using the control points specified by (\c cp1x, cp1y),
1691 and (\c cp2x, \c cp2y).
1692 After the curve is added, the current position is updated to be at the end point (\c x, \c y) of the curve.
1693 The following code produces the path shown below:
1695 ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
1698 ctx.moveTo(20, 0);//start point
1699 ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
1702 \image qml-item-canvas-bezierCurveTo.png
1703 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
1704 \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
1706 static v8::Handle<v8::Value> ctx2d_bezierCurveTo(const v8::Arguments &args)
1708 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1712 if (args.Length() == 6) {
1713 qreal cp1x = args[0]->NumberValue();
1714 qreal cp1y = args[1]->NumberValue();
1715 qreal cp2x = args[2]->NumberValue();
1716 qreal cp2y = args[3]->NumberValue();
1717 qreal x = args[4]->NumberValue();
1718 qreal y = args[5]->NumberValue();
1720 if (!qIsFinite(cp1x) || !qIsFinite(cp1y) || !qIsFinite(cp2x) || !qIsFinite(cp2y) || !qIsFinite(x) || !qIsFinite(y))
1723 r->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
1730 \qmlmethod object QtQuick2::Context2D::clip()
1732 Creates the clipping region from the current path.
1733 Any parts of the shape outside the clipping path are not displayed.
1734 To create a complex shape using the \c clip() method:
1737 \li Call the \c{context.beginPath()} method to set the clipping path.
1738 \li Define the clipping path by calling any combination of the \c{lineTo},
1739 \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
1740 \li Call the \c{context.clip()} method.
1743 The new shape displays. The following shows how a clipping path can
1744 modify how an image displays:
1746 \image qml-canvas-clip-complex.png
1751 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
1753 static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
1755 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1763 \qmlmethod object QtQuick2::Context2D::closePath()
1764 Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
1765 The current point of the new path is the previous subpath's first point.
1767 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
1769 static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
1771 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1775 r->context->closePath();
1781 \qmlmethod object QtQuick2::Context2D::fill()
1783 Fills the subpaths with the current fill style.
1785 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
1789 static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args)
1791 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1798 \qmlmethod object QtQuick2::Context2D::lineTo(real x, real y)
1800 Draws a line from the current position to the point (x, y).
1802 static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
1804 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1808 if (args.Length() == 2) {
1809 qreal x = args[0]->NumberValue();
1810 qreal y = args[1]->NumberValue();
1812 if (!qIsFinite(x) || !qIsFinite(y))
1815 r->context->lineTo(x, y);
1822 \qmlmethod object QtQuick2::Context2D::moveTo(real x, real y)
1824 Creates a new subpath with the given point.
1826 static v8::Handle<v8::Value> ctx2d_moveTo(const v8::Arguments &args)
1828 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1831 if (args.Length() == 2) {
1832 qreal x = args[0]->NumberValue();
1833 qreal y = args[1]->NumberValue();
1835 if (!qIsFinite(x) || !qIsFinite(y))
1837 r->context->moveTo(x, y);
1843 \qmlmethod object QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
1845 Adds a quadratic bezier curve between the current point and the endpoint (\c x, \c y) with the control point specified by (\c cpx, \c cpy).
1847 See \l{http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for for quadraticCurveTo}
1849 static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
1851 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1854 if (args.Length() == 4) {
1855 qreal cpx = args[0]->NumberValue();
1856 qreal cpy = args[1]->NumberValue();
1857 qreal x = args[2]->NumberValue();
1858 qreal y = args[3]->NumberValue();
1860 if (!qIsFinite(cpx) || !qIsFinite(cpy) || !qIsFinite(x) || !qIsFinite(y))
1863 r->context->quadraticCurveTo(cpx, cpy, x, y);
1870 \qmlmethod object QtQuick2::Context2D::rect(real x, real y, real w, real h)
1872 Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath.
1874 static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
1876 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1879 if (args.Length() == 4)
1880 r->context->rect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1885 \qmlmethod object QtQuick2::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius)
1887 Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the
1888 ellipses defining the corners of the rounded rectangle.
1890 static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args)
1892 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1895 if (args.Length() == 6)
1896 r->context->roundedRect(args[0]->NumberValue()
1897 , args[1]->NumberValue()
1898 , args[2]->NumberValue()
1899 , args[3]->NumberValue()
1900 , args[4]->NumberValue()
1901 , args[5]->NumberValue());
1906 \qmlmethod object QtQuick2::Context2D::ellipse(real x, real y, real w, real h)
1908 Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h,
1909 and adds it to the path as a closed subpath.
1911 The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position).
1913 static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args)
1915 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1919 if (args.Length() == 4)
1920 r->context->ellipse(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1926 \qmlmethod object QtQuick2::Context2D::text(string text, real x, real y)
1928 Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied.
1929 The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y).
1931 static v8::Handle<v8::Value> ctx2d_text(const v8::Arguments &args)
1933 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1936 QV8Engine *engine = V8ENGINE();
1937 if (args.Length() == 3) {
1938 qreal x = args[1]->NumberValue();
1939 qreal y = args[2]->NumberValue();
1941 if (!qIsFinite(x) || !qIsFinite(y))
1943 r->context->text(engine->toString(args[0]), x, y);
1949 \qmlmethod object QtQuick2::Context2D::stroke()
1951 Strokes the subpaths with the current stroke style.
1953 See \l{http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
1957 static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
1959 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1962 r->context->stroke();
1967 \qmlmethod object QtQuick2::Context2D::isPointInPath(real x, real y)
1969 Returns true if the given point is in the current path.
1971 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
1973 static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
1975 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1978 bool pointInPath = false;
1979 if (args.Length() == 2)
1980 pointInPath = r->context->isPointInPath(args[0]->NumberValue(), args[1]->NumberValue());
1981 return v8::Boolean::New(pointInPath);
1984 static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
1988 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
1991 static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
1995 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
1998 static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
2002 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
2006 \qmlproperty string QtQuick2::Context2D::font
2007 Holds the current font settings.
2009 The default font value is "10px sans-serif".
2010 See \l {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
2012 v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &info)
2014 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2017 QV8Engine *engine = V8ENGINE_ACCESSOR();
2019 return engine->toString(r->context->state.font.toString());
2022 static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2024 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2025 CHECK_CONTEXT_SETTER(r)
2027 QV8Engine *engine = V8ENGINE_ACCESSOR();
2028 QString fs = engine->toString(value);
2029 QFont font = qt_font_from_string(fs);
2030 if (font != r->context->state.font) {
2031 r->context->state.font = font;
2036 \qmlproperty string QtQuick2::Context2D::textAlign
2038 Holds the current text alignment settings.
2039 The possible values are:
2047 Other values are ignored. The default value is "start".
2049 v8::Handle<v8::Value> ctx2d_textAlign(v8::Local<v8::String>, const v8::AccessorInfo &info)
2051 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2053 QV8Engine *engine = V8ENGINE_ACCESSOR();
2054 switch (r->context->state.textAlign) {
2055 case QQuickContext2D::Start:
2056 return engine->toString(QLatin1String("start"));
2057 case QQuickContext2D::End:
2058 return engine->toString(QLatin1String("end"));
2059 case QQuickContext2D::Left:
2060 return engine->toString(QLatin1String("left"));
2061 case QQuickContext2D::Right:
2062 return engine->toString(QLatin1String("right"));
2063 case QQuickContext2D::Center:
2064 return engine->toString(QLatin1String("center"));
2068 return engine->toString(QLatin1String("start"));
2071 static void ctx2d_textAlign_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2073 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2074 CHECK_CONTEXT_SETTER(r)
2075 QV8Engine *engine = V8ENGINE_ACCESSOR();
2077 QString textAlign = engine->toString(value);
2079 QQuickContext2D::TextAlignType ta;
2080 if (textAlign == QLatin1String("start"))
2081 ta = QQuickContext2D::Start;
2082 else if (textAlign == QLatin1String("end"))
2083 ta = QQuickContext2D::End;
2084 else if (textAlign == QLatin1String("left"))
2085 ta = QQuickContext2D::Left;
2086 else if (textAlign == QLatin1String("right"))
2087 ta = QQuickContext2D::Right;
2088 else if (textAlign == QLatin1String("center"))
2089 ta = QQuickContext2D::Center;
2093 if (ta != r->context->state.textAlign) {
2094 r->context->state.textAlign = ta;
2099 \qmlproperty string QtQuick2::Context2D::textBaseline
2101 Holds the current baseline alignment settings.
2102 The possible values are:
2111 Other values are ignored. The default value is "alphabetic".
2113 v8::Handle<v8::Value> ctx2d_textBaseline(v8::Local<v8::String>, const v8::AccessorInfo &info)
2115 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2118 QV8Engine *engine = V8ENGINE_ACCESSOR();
2119 switch (r->context->state.textBaseline) {
2120 case QQuickContext2D::Alphabetic:
2121 return engine->toString(QLatin1String("alphabetic"));
2122 case QQuickContext2D::Hanging:
2123 return engine->toString(QLatin1String("hanging"));
2124 case QQuickContext2D::Top:
2125 return engine->toString(QLatin1String("top"));
2126 case QQuickContext2D::Bottom:
2127 return engine->toString(QLatin1String("bottom"));
2128 case QQuickContext2D::Middle:
2129 return engine->toString(QLatin1String("middle"));
2133 return engine->toString(QLatin1String("alphabetic"));
2136 static void ctx2d_textBaseline_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2138 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2139 CHECK_CONTEXT_SETTER(r)
2140 QV8Engine *engine = V8ENGINE_ACCESSOR();
2141 QString textBaseline = engine->toString(value);
2143 QQuickContext2D::TextBaseLineType tb;
2144 if (textBaseline == QLatin1String("alphabetic"))
2145 tb = QQuickContext2D::Alphabetic;
2146 else if (textBaseline == QLatin1String("hanging"))
2147 tb = QQuickContext2D::Hanging;
2148 else if (textBaseline == QLatin1String("top"))
2149 tb = QQuickContext2D::Top;
2150 else if (textBaseline == QLatin1String("bottom"))
2151 tb = QQuickContext2D::Bottom;
2152 else if (textBaseline == QLatin1String("middle"))
2153 tb = QQuickContext2D::Middle;
2157 if (tb != r->context->state.textBaseline) {
2158 r->context->state.textBaseline = tb;
2163 \qmlmethod object QtQuick2::Context2D::fillText(text, x, y)
2164 Fills the given text at the given position.
2170 static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
2172 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2175 QV8Engine *engine = V8ENGINE();
2176 if (args.Length() == 3) {
2177 qreal x = args[1]->NumberValue();
2178 qreal y = args[2]->NumberValue();
2179 if (!qIsFinite(x) || !qIsFinite(y))
2181 QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0]));
2182 r->context->buffer()->fill(textPath);
2187 \qmlmethod object QtQuick2::Context2D::strokeText(text, x, y)
2188 Strokes the given text at the given position.
2194 static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
2196 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2199 QV8Engine *engine = V8ENGINE();
2200 if (args.Length() == 3)
2201 r->context->drawText(engine->toString(args[0]), args[1]->NumberValue(), args[2]->NumberValue(), false);
2206 \qmltype TextMetrics
2207 \inqmlmodule QtQuick 2
2209 \ingroup qtquick-canvas
2210 \brief Provides a Context2D TextMetrics interface
2212 The TextMetrics object can be created by QtQuick2::Context2D::measureText method.
2213 See \l{http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details.
2215 \sa Context2D::measureText
2220 \qmlproperty int QtQuick2::TextMetrics::width
2221 Holds the advance width of the text that was passed to the QtQuick2::Context2D::measureText() method.
2222 This property is read only.
2226 \qmlmethod variant QtQuick2::Context2D::measureText(text)
2227 Returns a TextMetrics object with the metrics of the given text in the current font.
2229 static v8::Handle<v8::Value> ctx2d_measureText(const v8::Arguments &args)
2231 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2234 QV8Engine *engine = V8ENGINE();
2236 if (args.Length() == 1) {
2237 QFontMetrics fm(r->context->state.font);
2238 uint width = fm.width(engine->toString(args[0]));
2239 v8::Local<v8::Object> tm = v8::Object::New();
2240 tm->Set(v8::String::New("width"), v8::Number::New(width));
2243 return v8::Undefined();
2248 \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy)
2249 Draws the given \a image on the canvas at position (\a dx, \a dy).
2251 The \a image type can be an Image item, an image url or a CanvasImageData object.
2252 When given as Image item, if the image isn't fully loaded, this method draws nothing.
2253 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
2254 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
2258 \sa Canvas::loadImage
2259 \sa Canvas::isImageLoaded
2260 \sa Canvas::onImageLoaded
2262 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
2265 \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
2266 This is an overloaded function.
2267 Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
2271 The \a image type can be an Image item, an image url or a CanvasImageData object.
2272 When given as Image item, if the image isn't fully loaded, this method draws nothing.
2273 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
2274 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
2278 \sa Canvas::loadImage()
2279 \sa Canvas::isImageLoaded
2280 \sa Canvas::onImageLoaded
2282 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
2285 \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh)
2286 This is an overloaded function.
2287 Draws the given item as \a image from source point (\a sx, \a sy) and source width \a sw, source height \a sh
2288 onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
2292 The \a image type can be an Image or Canvas item, an image url or a CanvasImageData object.
2293 When given as Image item, if the image isn't fully loaded, this method draws nothing.
2294 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
2295 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
2299 \sa Canvas::loadImage()
2300 \sa Canvas::isImageLoaded
2301 \sa Canvas::onImageLoaded
2303 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
2305 static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
2307 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2310 QV8Engine *engine = V8ENGINE();
2311 qreal sx, sy, sw, sh, dx, dy, dw, dh;
2316 //FIXME:This function should be moved to QQuickContext2D::drawImage(...)
2317 if (!r->context->state.invertibleCTM)
2320 QQmlRefPointer<QQuickCanvasPixmap> pixmap;
2322 if (args[0]->IsString()) {
2323 QUrl url(engine->toString(args[0]->ToString()));
2325 V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
2327 pixmap = r->context->createPixmap(url);
2328 } else if (args[0]->IsObject()) {
2329 QQuickImage *imageItem = qobject_cast<QQuickImage*>(engine->toQObject(args[0]->ToObject()));
2330 QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(engine->toQObject(args[0]->ToObject()));
2332 QV8Context2DPixelArrayResource *pix = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->GetInternalField(0)->ToObject());
2333 if (pix && !pix->image.isNull()) {
2334 pixmap.take(new QQuickCanvasPixmap(pix->image, r->context->canvas()->window()));
2335 } else if (imageItem) {
2336 pixmap.take(r->context->createPixmap(imageItem->source()));
2337 } else if (canvas) {
2338 QImage img = canvas->toImage();
2340 pixmap.take(new QQuickCanvasPixmap(img, canvas->window()));
2342 V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
2345 V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
2348 if (pixmap.isNull() || !pixmap->isValid())
2351 if (args.Length() == 3) {
2352 dx = args[1]->NumberValue();
2353 dy = args[2]->NumberValue();
2356 sw = pixmap->width();
2357 sh = pixmap->height();
2360 } else if (args.Length() == 5) {
2363 sw = pixmap->width();
2364 sh = pixmap->height();
2365 dx = args[1]->NumberValue();
2366 dy = args[2]->NumberValue();
2367 dw = args[3]->NumberValue();
2368 dh = args[4]->NumberValue();
2369 } else if (args.Length() == 9) {
2370 sx = args[1]->NumberValue();
2371 sy = args[2]->NumberValue();
2372 sw = args[3]->NumberValue();
2373 sh = args[4]->NumberValue();
2374 dx = args[5]->NumberValue();
2375 dy = args[6]->NumberValue();
2376 dw = args[7]->NumberValue();
2377 dh = args[8]->NumberValue();
2396 || sx + sw > pixmap->width()
2397 || sy + sh > pixmap->height()
2398 || sx + sw < 0 || sy + sh < 0) {
2399 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error");
2402 r->context->buffer()->drawPixmap(pixmap, QRectF(sx, sy, sw, sh), QRectF(dx, dy, dw, dh));
2407 // pixel manipulation
2409 \qmltype CanvasImageData
2410 \inqmlmodule QtQuick 2
2411 \ingroup qtquick-canvas
2412 \brief Contains image pixel data in RGBA order
2414 The CanvasImageData object holds the image pixel data.
2416 The CanvasImageData object has the actual dimensions of the data stored in
2417 this object and holds the one-dimensional array containing the data in RGBA order,
2418 as integers in the range 0 to 255.
2423 \sa Context2D::createImageData()
2424 \sa Context2D::getImageData()
2425 \sa Context2D::putImageData()
2428 \qmlproperty int QtQuick2::CanvasImageData::width
2429 Holds the actual width dimension of the data in the ImageData object, in device pixels.
2431 v8::Handle<v8::Value> ctx2d_imageData_width(v8::Local<v8::String>, const v8::AccessorInfo &args)
2433 QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2435 return v8::Integer::New(0);
2436 return v8::Integer::New(r->image.width());
2440 \qmlproperty int QtQuick2::CanvasImageData::height
2441 Holds the actual height dimension of the data in the ImageData object, in device pixels.
2443 v8::Handle<v8::Value> ctx2d_imageData_height(v8::Local<v8::String>, const v8::AccessorInfo &args)
2445 QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
2447 return v8::Integer::New(0);
2449 return v8::Integer::New(r->image.height());
2453 \qmlproperty object QtQuick2::CanvasImageData::data
2454 Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
2456 v8::Handle<v8::Value> ctx2d_imageData_data(v8::Local<v8::String>, const v8::AccessorInfo &args)
2458 return args.This()->GetInternalField(0);
2462 \qmltype CanvasPixelArray
2463 \inqmlmodule QtQuick 2
2464 \ingroup qtquick-canvas
2465 \brief Provides ordered and indexed access to the components of each pixel in image data
2467 The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
2468 The CanvasPixelArray can be accessed as normal Javascript array.
2470 \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
2474 \qmlproperty int QtQuick2::CanvasPixelArray::length
2475 The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
2476 The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
2477 This property is read only.
2479 v8::Handle<v8::Value> ctx2d_pixelArray_length(v8::Local<v8::String>, const v8::AccessorInfo &args)
2481 QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
2482 if (!r || r->image.isNull()) return v8::Undefined();
2484 return v8::Integer::New(r->image.width() * r->image.height() * 4);
2487 v8::Handle<v8::Value> ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args)
2489 QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
2491 if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4)) {
2492 const quint32 w = r->image.width();
2493 const quint32 row = (index / 4) / w;
2494 const quint32 col = (index / 4) % w;
2495 const QRgb* pixel = reinterpret_cast<const QRgb*>(r->image.constScanLine(row));
2497 switch (index % 4) {
2499 return v8::Integer::New(qRed(*pixel));
2501 return v8::Integer::New(qGreen(*pixel));
2503 return v8::Integer::New(qBlue(*pixel));
2505 return v8::Integer::New(qAlpha(*pixel));
2508 return v8::Undefined();
2511 v8::Handle<v8::Value> ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
2513 QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(info.This());
2515 const int v = value->Uint32Value();
2516 if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4) && v > 0 && v <= 255) {
2517 const quint32 w = r->image.width();
2518 const quint32 row = (index / 4) / w;
2519 const quint32 col = (index / 4) % w;
2521 QRgb* pixel = reinterpret_cast<QRgb*>(r->image.scanLine(row));
2523 switch (index % 4) {
2525 *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel));
2528 *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel));
2531 *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel));
2534 *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v);
2538 return v8::Undefined();
2541 \qmlmethod CanvasImageData QtQuick2::Context2D::createImageData(real sw, real sh)
2542 Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
2545 \qmlmethod CanvasImageData QtQuick2::Context2D::createImageData(CanvasImageData imageData)
2546 Creates a CanvasImageData object with the same dimensions as the argument.
2549 \qmlmethod CanvasImageData QtQuick2::Context2D::createImageData(Url imageUrl)
2550 Creates a CanvasImageData object with the given image loaded from \a imageUrl.
2551 Note:The \a imageUrl must be already loaded before this function call, if not, an empty
2552 CanvasImageData obect will be returned.
2554 \sa Canvas::loadImage(), QtQuick2::Canvas::unloadImage(), QtQuick2::Canvas::isImageLoaded
2556 static v8::Handle<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
2558 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2561 QV8Engine *engine = V8ENGINE();
2563 if (args.Length() == 1) {
2564 if (args[0]->IsObject()) {
2565 v8::Local<v8::Object> imgData = args[0]->ToObject();
2566 QV8Context2DPixelArrayResource *pa = v8_resource_cast<QV8Context2DPixelArrayResource>(imgData->GetInternalField(0)->ToObject());
2568 qreal w = imgData->Get(v8::String::New("width"))->NumberValue();
2569 qreal h = imgData->Get(v8::String::New("height"))->NumberValue();
2570 return qt_create_image_data(w, h, engine, QImage());
2572 } else if (args[0]->IsString()) {
2573 QImage image = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
2574 return qt_create_image_data(image.width(), image.height(), engine, image);
2576 } else if (args.Length() == 2) {
2577 qreal w = args[0]->NumberValue();
2578 qreal h = args[1]->NumberValue();
2580 if (!qIsFinite(w) || !qIsFinite(h))
2581 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments");
2584 return qt_create_image_data(w, h, engine, QImage());
2586 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments");
2588 return v8::Undefined();
2592 \qmlmethod CanvasImageData QtQuick2::Canvas::getImageData(real sx, real sy, real sw, real sh)
2593 Returns an CanvasImageData object containing the image data for the given rectangle of the canvas.
2595 static v8::Handle<v8::Value> ctx2d_getImageData(const v8::Arguments &args)
2597 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2600 QV8Engine *engine = V8ENGINE();
2601 if (args.Length() == 4) {
2602 qreal x = args[0]->NumberValue();
2603 qreal y = args[1]->NumberValue();
2604 qreal w = args[2]->NumberValue();
2605 qreal h = args[3]->NumberValue();
2606 if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(w))
2607 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments");
2609 if (w <= 0 || h <= 0)
2610 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
2612 QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h));
2613 v8::Local<v8::Object> imageData = qt_create_image_data(w, h, engine, image);
2621 \qmlmethod object QtQuick2::Context2D::putImageData(CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
2622 Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted.
2624 static v8::Handle<v8::Value> ctx2d_putImageData(const v8::Arguments &args)
2626 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2628 if (args.Length() != 3 && args.Length() != 7)
2629 return v8::Undefined();
2631 if (args[0]->IsNull() || !args[0]->IsObject()) {
2632 V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch");
2634 qreal dx = args[1]->NumberValue();
2635 qreal dy = args[2]->NumberValue();
2636 qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
2638 if (!qIsFinite(dx) || !qIsFinite(dy))
2639 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
2641 v8::Local<v8::Object> imageData = args[0]->ToObject();
2642 QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast<QV8Context2DPixelArrayResource>(imageData->Get(v8::String::New("data"))->ToObject());
2644 w = imageData->Get(v8::String::New("width"))->NumberValue();
2645 h = imageData->Get(v8::String::New("height"))->NumberValue();
2647 if (args.Length() == 7) {
2648 dirtyX = args[3]->NumberValue();
2649 dirtyY = args[4]->NumberValue();
2650 dirtyWidth = args[5]->NumberValue();
2651 dirtyHeight = args[6]->NumberValue();
2653 if (!qIsFinite(dirtyX) || !qIsFinite(dirtyY) || !qIsFinite(dirtyWidth) || !qIsFinite(dirtyHeight))
2654 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
2657 if (dirtyWidth < 0) {
2658 dirtyX = dirtyX+dirtyWidth;
2659 dirtyWidth = -dirtyWidth;
2662 if (dirtyHeight < 0) {
2663 dirtyY = dirtyY+dirtyHeight;
2664 dirtyHeight = -dirtyHeight;
2668 dirtyWidth = dirtyWidth+dirtyX;
2673 dirtyHeight = dirtyHeight+dirtyY;
2677 if (dirtyX+dirtyWidth > w) {
2678 dirtyWidth = w - dirtyX;
2681 if (dirtyY+dirtyHeight > h) {
2682 dirtyHeight = h - dirtyY;
2685 if (dirtyWidth <=0 || dirtyHeight <= 0)
2694 QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2695 r->context->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
2701 \qmltype CanvasGradient
2702 \inqmlmodule QtQuick 2
2704 \ingroup qtquick-canvas
2705 \brief Provides an opaque CanvasGradient interface
2709 \qmlmethod CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color)
2710 Adds a color stop with the given color to the gradient at the given offset.
2711 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
2715 var gradient = ctx.createLinearGradient(0, 0, 100, 100);
2716 gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
2717 gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1');
2720 static v8::Handle<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &args)
2722 QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(args.This());
2724 V8THROW_ERROR("Not a CanvasGradient object");
2726 QV8Engine *engine = V8ENGINE();
2728 if (args.Length() == 2) {
2730 if (!style->brush.gradient())
2731 V8THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
2732 QGradient gradient = *(style->brush.gradient());
2733 qreal pos = args[0]->NumberValue();
2736 if (args[1]->IsObject()) {
2737 color = engine->toVariant(args[1], qMetaTypeId<QColor>()).value<QColor>();
2739 color = qt_color_from_string(args[1]);
2741 if (pos < 0.0 || pos > 1.0 || !qIsFinite(pos)) {
2742 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range");
2745 if (color.isValid()) {
2746 gradient.setColorAt(pos, color);
2748 V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
2750 style->brush = gradient;
2756 void QQuickContext2D::scale(qreal x, qreal y)
2758 if (!state.invertibleCTM)
2761 if (!qIsFinite(x) || !qIsFinite(y))
2764 QTransform newTransform = state.matrix;
2765 newTransform.scale(x, y);
2767 if (!newTransform.isInvertible()) {
2768 state.invertibleCTM = false;
2772 state.matrix = newTransform;
2773 buffer()->updateMatrix(state.matrix);
2774 m_path = QTransform().scale(1.0 / x, 1.0 / y).map(m_path);
2777 void QQuickContext2D::rotate(qreal angle)
2779 if (!state.invertibleCTM)
2782 if (!qIsFinite(angle))
2785 QTransform newTransform =state.matrix;
2786 newTransform.rotate(DEGREES(angle));
2788 if (!newTransform.isInvertible()) {
2789 state.invertibleCTM = false;
2793 state.matrix = newTransform;
2794 buffer()->updateMatrix(state.matrix);
2795 m_path = QTransform().rotate(-DEGREES(angle)).map(m_path);
2798 void QQuickContext2D::shear(qreal h, qreal v)
2800 if (!state.invertibleCTM)
2803 if (!qIsFinite(h) || !qIsFinite(v))
2806 QTransform newTransform = state.matrix;
2807 newTransform.shear(h, v);
2809 if (!newTransform.isInvertible()) {
2810 state.invertibleCTM = false;
2814 state.matrix = newTransform;
2815 buffer()->updateMatrix(state.matrix);
2816 m_path = QTransform().shear(-h, -v).map(m_path);
2819 void QQuickContext2D::translate(qreal x, qreal y)
2821 if (!state.invertibleCTM)
2824 if (!qIsFinite(x) || !qIsFinite(y))
2827 QTransform newTransform = state.matrix;
2828 newTransform.translate(x, y);
2830 if (!newTransform.isInvertible()) {
2831 state.invertibleCTM = false;
2835 state.matrix = newTransform;
2836 buffer()->updateMatrix(state.matrix);
2837 m_path = QTransform().translate(-x, -y).map(m_path);
2840 void QQuickContext2D::transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
2842 if (!state.invertibleCTM)
2845 if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f))
2848 QTransform transform(a, b, c, d, e, f);
2849 QTransform newTransform = state.matrix * transform;
2851 if (!newTransform.isInvertible()) {
2852 state.invertibleCTM = false;
2855 state.matrix = newTransform;
2856 buffer()->updateMatrix(state.matrix);
2857 m_path = transform.inverted().map(m_path);
2860 void QQuickContext2D::setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
2862 if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f))
2865 QTransform ctm = state.matrix;
2866 if (!ctm.isInvertible())
2869 state.matrix = ctm.inverted() * state.matrix;
2870 m_path = ctm.map(m_path);
2871 state.invertibleCTM = true;
2872 transform(a, b, c, d, e, f);
2875 void QQuickContext2D::fill()
2877 if (!state.invertibleCTM)
2880 if (!m_path.elementCount())
2883 m_path.setFillRule(state.fillRule);
2884 buffer()->fill(m_path);
2887 void QQuickContext2D::clip()
2889 if (!state.invertibleCTM)
2892 QPainterPath clipPath = m_path;
2893 clipPath.closeSubpath();
2894 if (!state.clipPath.isEmpty())
2895 state.clipPath = clipPath.intersected(state.clipPath);
2897 state.clipPath = clipPath;
2898 buffer()->clip(state.clipPath);
2901 void QQuickContext2D::stroke()
2903 if (!state.invertibleCTM)
2906 if (!m_path.elementCount())
2909 buffer()->stroke(m_path);
2912 void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
2914 if (!state.invertibleCTM)
2917 if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
2920 buffer()->fillRect(QRectF(x, y, w, h));
2923 void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
2925 if (!state.invertibleCTM)
2928 if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
2931 buffer()->strokeRect(QRectF(x, y, w, h));
2934 void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
2936 if (!state.invertibleCTM)
2939 if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
2942 buffer()->clearRect(QRectF(x, y, w, h));
2945 void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill)
2947 if (!state.invertibleCTM)
2950 if (!qIsFinite(x) || !qIsFinite(y))
2953 QPainterPath textPath = createTextGlyphs(x, y, text);
2955 buffer()->fill(textPath);
2957 buffer()->stroke(textPath);
2961 void QQuickContext2D::beginPath()
2963 if (!m_path.elementCount())
2965 m_path = QPainterPath();
2968 void QQuickContext2D::closePath()
2970 if (!m_path.elementCount())
2973 QRectF boundRect = m_path.boundingRect();
2974 if (boundRect.width() || boundRect.height())
2975 m_path.closeSubpath();
2976 //FIXME:QPainterPath set the current point to (0,0) after close subpath
2977 //should be the first point of the previous subpath
2980 void QQuickContext2D::moveTo( qreal x, qreal y)
2982 if (!state.invertibleCTM)
2985 //FIXME: moveTo should not close the previous subpath
2986 m_path.moveTo(QPointF(x, y));
2989 void QQuickContext2D::lineTo( qreal x, qreal y)
2991 if (!state.invertibleCTM)
2996 if (!m_path.elementCount())
2998 else if (m_path.currentPosition() != pt)
3002 void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
3005 if (!state.invertibleCTM)
3008 if (!m_path.elementCount())
3009 m_path.moveTo(QPointF(cpx, cpy));
3012 if (m_path.currentPosition() != pt)
3013 m_path.quadTo(QPointF(cpx, cpy), pt);
3016 void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
3017 qreal cp2x, qreal cp2y,
3020 if (!state.invertibleCTM)
3023 if (!m_path.elementCount())
3024 m_path.moveTo(QPointF(cp1x, cp1y));
3027 if (m_path.currentPosition() != pt)
3028 m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y), pt);
3031 void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
3033 QPointF p0(m_path.currentPosition());
3035 QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
3036 QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
3037 float p1p0_length = qSqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y());
3038 float p1p2_length = qSqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y());
3040 double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length);
3042 // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
3043 // We could have used areCollinear() here, but since we're reusing
3044 // the variables computed above later on we keep this logic.
3045 if (qFuzzyCompare(qAbs(cos_phi), 1.0)) {
3050 float tangent = radius / tan(acos(cos_phi) / 2);
3051 float factor_p1p0 = tangent / p1p0_length;
3052 QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
3054 QPointF orth_p1p0(p1p0.y(), -p1p0.x());
3055 float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y());
3056 float factor_ra = radius / orth_p1p0_length;
3058 // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
3059 double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length);
3060 if (cos_alpha < 0.f)
3061 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3063 QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
3065 // calculate angles for addArc
3066 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3067 float sa = acos(orth_p1p0.x() / orth_p1p0_length);
3068 if (orth_p1p0.y() < 0.f)
3071 // anticlockwise logic
3072 bool anticlockwise = false;
3074 float factor_p1p2 = tangent / p1p2_length;
3075 QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
3076 QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
3077 float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y());
3078 float ea = acos(orth_p1p2.x() / orth_p1p2_length);
3079 if (orth_p1p2.y() < 0)
3081 if ((sa > ea) && ((sa - ea) < Q_PI))
3082 anticlockwise = true;
3083 if ((sa < ea) && ((ea - sa) > Q_PI))
3084 anticlockwise = true;
3086 arc(p.x(), p.y(), radius, sa, ea, anticlockwise);
3089 void QQuickContext2D::arcTo(qreal x1, qreal y1,
3093 if (!state.invertibleCTM)
3096 if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2) || !qIsFinite(radius))
3100 QPointF end(x2, y2);
3102 if (!m_path.elementCount())
3104 else if (st == m_path.currentPosition() || st == end || !radius)
3107 addArcTo(st, end, radius);
3110 void QQuickContext2D::rect(qreal x, qreal y, qreal w, qreal h)
3112 if (!state.invertibleCTM)
3114 if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
3118 m_path.moveTo(x, y);
3121 m_path.addRect(x, y, w, h);
3124 void QQuickContext2D::roundedRect(qreal x, qreal y,
3128 if (!state.invertibleCTM)
3131 if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h) || !qIsFinite(xr) || !qIsFinite(yr))
3135 m_path.moveTo(x, y);
3138 m_path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
3141 void QQuickContext2D::ellipse(qreal x, qreal y,
3144 if (!state.invertibleCTM)
3147 if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
3151 m_path.moveTo(x, y);
3155 m_path.addEllipse(x, y, w, h);
3158 void QQuickContext2D::text(const QString& str, qreal x, qreal y)
3160 if (!state.invertibleCTM)
3164 path.addText(x, y, state.font, str);
3165 m_path.addPath(path);
3168 void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise)
3170 if (!state.invertibleCTM)
3173 if (!qIsFinite(xc) || !qIsFinite(yc) || !qIsFinite(sar) || !qIsFinite(ear) || !qIsFinite(radius))
3182 // In Qt we don't switch the coordinate system for degrees
3183 // and still use the 0,0 as bottom left for degrees so we need
3187 antiClockWise = !antiClockWise;
3190 float sa = DEGREES(sar);
3191 float ea = DEGREES(ear);
3195 double xs = xc - radius;
3196 double ys = yc - radius;
3197 double width = radius*2;
3198 double height = radius*2;
3199 if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360)))
3200 // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
3201 // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
3202 // circumference of this circle.
3205 if (!antiClockWise && (ea < sa)) {
3207 } else if (antiClockWise && (sa < ea)) {
3210 //### this is also due to switched coordinate system
3211 // we would end up with a 0 span instead of 360
3212 if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
3213 qFuzzyCompare(qAbs(span), 360))) {
3218 // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
3219 if (!m_path.elementCount())
3220 m_path.arcMoveTo(xs, ys, width, height, sa);
3222 m_path.lineTo(xc, yc);
3226 m_path.arcTo(xs, ys, width, height, sa, span);
3229 int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
3233 case QQuickContext2D::Top:
3235 case QQuickContext2D::Alphabetic:
3236 case QQuickContext2D::Middle:
3237 case QQuickContext2D::Hanging:
3238 offset = metrics.ascent();
3240 case QQuickContext2D::Bottom:
3241 offset = metrics.height();
3247 static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
3250 if (value == QQuickContext2D::Start)
3251 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Left : QQuickContext2D::Right;
3252 else if (value == QQuickContext2D::End)
3253 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Right: QQuickContext2D::Left;
3255 case QQuickContext2D::Center:
3256 offset = metrics.width(text)/2;
3258 case QQuickContext2D::Right:
3259 offset = metrics.width(text);
3260 case QQuickContext2D::Left:
3267 void QQuickContext2D::setGrabbedImage(const QImage& grab)
3269 m_grabbedImage = grab;
3273 QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
3275 return m_canvas->loadedPixmap(url);
3278 QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
3280 const QFontMetrics metrics(state.font);
3281 int yoffset = baseLineOffset(static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
3282 int xoffset = textAlignOffset(static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
3284 QPainterPath textPath;
3286 textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
3287 textPath = state.matrix.map(textPath);
3292 static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
3294 // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
3295 return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x()));
3298 static inline bool withinRange(qreal p, qreal a, qreal b)
3300 return (p >= a && p <= b) || (p >= b && p <= a);
3303 bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
3305 if (!state.invertibleCTM)
3308 if (!m_path.elementCount())
3311 if (!qIsFinite(x) || !qIsFinite(y))
3314 QPointF point(x, y);
3315 QTransform ctm = state.matrix;
3316 QPointF p = ctm.inverted().map(point);
3317 if (!qIsFinite(p.x()) || !qIsFinite(p.y()))
3320 const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
3322 bool contains = m_path.contains(p);
3325 // check whether the point is on the border
3326 QPolygonF border = m_path.toFillPolygon();
3328 QPointF p1 = border.at(0);
3331 for (int i = 1; i < border.size(); ++i) {
3333 if (areCollinear(p, p1, p2)
3334 // Once we know that the points are collinear we
3335 // only need to check one of the coordinates
3336 && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ?
3337 withinRange(p.x(), p1.x(), p2.x()) :
3338 withinRange(p.y(), p1.y(), p2.y()))) {
3347 QQuickContext2D::QQuickContext2D(QObject *parent)
3348 : QQuickCanvasContext(parent)
3349 , m_buffer(new QQuickContext2DCommandBuffer)
3351 , m_windowManager(0)
3359 QQuickContext2D::~QQuickContext2D()
3362 m_texture->deleteLater();
3365 v8::Handle<v8::Object> QQuickContext2D::v8value() const
3370 QStringList QQuickContext2D::contextNames() const
3372 return QStringList() << QLatin1String("2d");
3375 void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args)
3379 m_canvas = canvasItem;
3380 m_renderTarget = canvasItem->renderTarget();
3382 QQuickWindow *window = canvasItem->window();
3383 m_windowManager = QQuickWindowPrivate::get(window)->windowManager;
3384 m_renderStrategy = canvasItem->renderStrategy();
3386 switch (m_renderTarget) {
3387 case QQuickCanvasItem::Image:
3388 m_texture = new QQuickContext2DImageTexture;
3390 case QQuickCanvasItem::FramebufferObject:
3391 m_texture = new QQuickContext2DFBOTexture;
3395 m_texture->setItem(canvasItem);
3396 m_texture->setCanvasWindow(canvasItem->canvasWindow().toRect());
3397 m_texture->setTileSize(canvasItem->tileSize());
3398 m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
3399 m_texture->setSmooth(canvasItem->smooth());
3400 m_texture->setAntialiasing(canvasItem->antialiasing());
3401 m_thread = QThread::currentThread();
3403 QThread *renderThread = m_thread;
3404 QThread *sceneGraphThread = window->openglContext() ? window->openglContext()->thread() : 0;
3406 if (m_renderStrategy == QQuickCanvasItem::Threaded)
3407 renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
3408 else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
3409 renderThread = sceneGraphThread;
3411 if (renderThread && renderThread != QThread::currentThread())
3412 m_texture->moveToThread(renderThread);
3414 if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
3415 QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->glContext();
3417 m_glContext = new QOpenGLContext;
3418 m_glContext->setFormat(cc->format());
3419 m_glContext->setShareContext(cc);
3420 if (renderThread != QThread::currentThread())
3421 m_glContext->moveToThread(renderThread);
3424 connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
3429 void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing)
3431 QMetaObject::invokeMethod(m_texture
3433 , Qt::AutoConnection
3434 , Q_ARG(QSize, canvasSize)
3435 , Q_ARG(QSize, tileSize)
3436 , Q_ARG(QRect, canvasWindow)
3437 , Q_ARG(QRect, dirtyRect)
3438 , Q_ARG(bool, smooth)
3439 , Q_ARG(bool, antialiasing));
3442 void QQuickContext2D::flush()
3445 QMetaObject::invokeMethod(m_texture,
3448 Q_ARG(QQuickContext2DCommandBuffer*, m_buffer));
3449 m_buffer = new QQuickContext2DCommandBuffer();
3452 QSGDynamicTexture *QQuickContext2D::texture() const
3457 QImage QQuickContext2D::toImage(const QRectF& bounds)
3460 if (m_texture->thread() == QThread::currentThread())
3461 m_texture->grabImage(bounds);
3462 else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
3463 qWarning() << "Pixel read back is not support in Cooperative mode, please try Theaded or Immediate mode";
3466 QMetaObject::invokeMethod(m_texture,
3468 Qt::BlockingQueuedConnection,
3469 Q_ARG(QRectF, bounds));
3471 QImage img = m_grabbedImage;
3472 m_grabbedImage = QImage();
3478 QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine)
3480 v8::HandleScope handle_scope;
3481 v8::Context::Scope scope(engine->context());
3483 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
3484 ft->InstanceTemplate()->SetHasExternalResource(true);
3485 ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine));
3486 ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine));
3487 ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine));
3488 ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine));
3489 ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine));
3490 ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine));
3491 ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine));
3492 ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine));
3493 ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine));
3494 ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine));
3495 ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine));
3496 ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine));
3497 ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine));
3498 ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine));
3499 ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine));
3500 ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine));
3501 ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine));
3502 ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine));
3503 ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine));
3504 ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
3505 ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine));
3506 ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine));
3507 ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine));
3508 ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine));
3509 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine));
3510 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine));
3511 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine));
3512 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine));
3513 ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine));
3514 ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine));
3515 ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine));
3516 ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine));
3517 ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine));
3518 ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine));
3519 ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine));
3520 ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine));
3521 ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine));
3522 ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine));
3523 ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine));
3524 ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine));
3525 ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine));
3526 ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine));
3527 ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine));
3528 ft->PrototypeTemplate()->Set(v8::String::New("roundedRect"), V8FUNCTION(ctx2d_roundedRect, engine));
3529 ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine));
3530 ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine));
3531 ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine));
3532 ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine));
3533 ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine));
3534 ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine));
3535 ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine));
3536 ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine));
3537 ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine));
3538 ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine));
3539 ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine));
3540 ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine));
3541 ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine));
3542 ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine));
3543 ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine));
3544 ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine));
3545 ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine));
3547 constructorContext = qPersistentNew(ft->GetFunction());
3549 v8::Local<v8::FunctionTemplate> ftGradient = v8::FunctionTemplate::New();
3550 ftGradient->InstanceTemplate()->SetHasExternalResource(true);
3551 ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine));
3552 constructorGradient = qPersistentNew(ftGradient->GetFunction());
3554 v8::Local<v8::FunctionTemplate> ftPattern = v8::FunctionTemplate::New();
3555 ftPattern->InstanceTemplate()->SetHasExternalResource(true);
3556 constructorPattern = qPersistentNew(ftPattern->GetFunction());
3558 v8::Local<v8::FunctionTemplate> ftPixelArray = v8::FunctionTemplate::New();
3559 ftPixelArray->InstanceTemplate()->SetHasExternalResource(true);
3560 ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine));
3561 ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine));
3562 constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction());
3564 v8::Local<v8::FunctionTemplate> ftImageData = v8::FunctionTemplate::New();
3565 ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine));
3566 ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine));
3567 ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine));
3568 ftImageData->InstanceTemplate()->SetInternalFieldCount(1);
3569 constructorImageData = qPersistentNew(ftImageData->GetFunction());
3572 QQuickContext2DEngineData::~QQuickContext2DEngineData()
3574 qPersistentDispose(constructorContext);
3575 qPersistentDispose(constructorGradient);
3576 qPersistentDispose(constructorPattern);
3577 qPersistentDispose(constructorImageData);
3578 qPersistentDispose(constructorPixelArray);
3581 void QQuickContext2D::popState()
3583 if (m_stateStack.isEmpty())
3586 QQuickContext2D::State newState = m_stateStack.pop();
3588 if (state.matrix != newState.matrix)
3589 buffer()->updateMatrix(newState.matrix);
3591 if (newState.globalAlpha != state.globalAlpha)
3592 buffer()->setGlobalAlpha(newState.globalAlpha);
3594 if (newState.globalCompositeOperation != state.globalCompositeOperation)
3595 buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
3597 if (newState.fillStyle != state.fillStyle)
3598 buffer()->setFillStyle(newState.fillStyle);
3600 if (newState.strokeStyle != state.strokeStyle)
3601 buffer()->setStrokeStyle(newState.strokeStyle);
3603 if (newState.lineWidth != state.lineWidth)
3604 buffer()->setLineWidth(newState.lineWidth);
3606 if (newState.lineCap != state.lineCap)
3607 buffer()->setLineCap(newState.lineCap);
3609 if (newState.lineJoin != state.lineJoin)
3610 buffer()->setLineJoin(newState.lineJoin);
3612 if (newState.miterLimit != state.miterLimit)
3613 buffer()->setMiterLimit(newState.miterLimit);
3615 if (newState.clipPath != state.clipPath) {
3616 buffer()->clip(newState.clipPath);
3619 if (newState.shadowBlur != state.shadowBlur)
3620 buffer()->setShadowBlur(newState.shadowBlur);
3622 if (newState.shadowColor != state.shadowColor)
3623 buffer()->setShadowColor(newState.shadowColor);
3625 if (newState.shadowOffsetX != state.shadowOffsetX)
3626 buffer()->setShadowOffsetX(newState.shadowOffsetX);
3628 if (newState.shadowOffsetY != state.shadowOffsetY)
3629 buffer()->setShadowOffsetY(newState.shadowOffsetY);
3630 m_path = state.matrix.map(m_path);
3632 m_path = state.matrix.inverted().map(m_path);
3634 void QQuickContext2D::pushState()
3636 m_stateStack.push(state);
3639 void QQuickContext2D::reset()
3641 QQuickContext2D::State newState;
3642 newState.matrix = QTransform();
3644 m_path = QPainterPath();
3646 QPainterPath defaultClipPath;
3648 QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
3649 r = r.united(m_canvas->canvasWindow().toRect());
3650 defaultClipPath.addRect(r);
3651 newState.clipPath = defaultClipPath;
3652 newState.clipPath.setFillRule(Qt::WindingFill);
3654 newState.strokeStyle = QColor("#000000");
3655 newState.fillStyle = QColor("#000000");
3656 newState.fillPatternRepeatX = false;
3657 newState.fillPatternRepeatY = false;
3658 newState.strokePatternRepeatX = false;
3659 newState.strokePatternRepeatY = false;
3660 newState.invertibleCTM = true;
3661 newState.fillRule = Qt::WindingFill;
3662 newState.globalAlpha = 1.0;
3663 newState.lineWidth = 1;
3664 newState.lineCap = Qt::FlatCap;
3665 newState.lineJoin = Qt::MiterJoin;
3666 newState.miterLimit = 10;
3667 newState.shadowOffsetX = 0;
3668 newState.shadowOffsetY = 0;
3669 newState.shadowBlur = 0;
3670 newState.shadowColor = qRgba(0, 0, 0, 0);
3671 newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
3672 newState.font = QFont(QLatin1String("sans-serif"), 10);
3673 newState.textAlign = QQuickContext2D::Start;
3674 newState.textBaseline = QQuickContext2D::Alphabetic;
3676 m_stateStack.clear();
3677 m_stateStack.push(newState);
3679 m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
3682 void QQuickContext2D::setV8Engine(QV8Engine *engine)
3684 v8::HandleScope handle_scope;
3685 v8::Context::Scope scope(engine->context());
3687 if (m_v8engine != engine) {
3688 m_v8engine = engine;
3690 qPersistentDispose(m_v8value);
3692 if (m_v8engine == 0)
3695 QQuickContext2DEngineData *ed = engineData(engine);
3696 m_v8value = qPersistentNew(ed->constructorContext->NewInstance());
3697 QV8Context2DResource *r = new QV8Context2DResource(engine);
3699 m_v8value->SetExternalResource(r);
3703 QQuickContext2DCommandBuffer* QQuickContext2D::nextBuffer()
3705 QMutexLocker lock(&m_mutex);
3706 return m_bufferQueue.isEmpty() ? 0 : m_bufferQueue.dequeue();