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/qquickcanvas_p.h>
65 #include <private/qquickwindowmanager_p.h>
66 #include <QtGui/private/qguiapplication_p.h>
67 #include <qpa/qplatformintegration.h>
75 \qmlclass Context2D QQuickContext2D
76 \inqmlmodule QtQuick 2
77 \ingroup qtquick-canvas
79 \brief Provides 2D context for shapes on a Canvas item
81 The Context2D object can be created by \c Canvas item's \c getContext()
87 var ctx = canvas.getContext('2d');
92 The Context2D API implements the same \l
93 {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard} with
94 some enhanced features.
96 The Context2D API provides the rendering \b{context} which defines the
97 methods and attributes needed to draw on the \c Canvas item. The following
98 assigns the canvas rendering context to a \c{context} variable:
100 var context = mycanvas.getContext("2d")
103 The Context2D API renders the canvas as a coordinate system whose origin
104 (0,0) is at the top left corner, as shown in the figure below. Coordinates
105 increase along the \c{x} axis from left to right and along the \c{y} axis
106 from top to bottom of the canvas.
107 \image qml-item-canvas-context.gif
112 Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
114 static const double Q_PI = 3.14159265358979323846; // pi
116 #define DEGREES(t) ((t) * 180.0 / Q_PI)
118 #define CHECK_CONTEXT(r) if (!r || !r->context || !r->context->bufferValid()) \
119 V8THROW_ERROR("Not a Context2D object");
121 #define CHECK_CONTEXT_SETTER(r) if (!r || !r->context || !r->context->bufferValid()) \
122 V8THROW_ERROR_SETTER("Not a Context2D object");
123 #define qClamp(val, min, max) qMin(qMax(val, min), max)
124 #define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
125 QColor qt_color_from_string(v8::Local<v8::Value> name)
127 v8::String::AsciiValue str(name);
130 int len = str.length();
131 //rgb/hsl color string has at least 7 characters
132 if (!p || len > 255 || len <= 7)
135 bool isRgb(false), isHsl(false), hasAlpha(false);
138 while (isspace(*p)) p++;
139 if (strncmp(p, "rgb", 3) == 0)
141 else if (strncmp(p, "hsl", 3) == 0)
146 p+=3; //skip "rgb" or "hsl"
147 hasAlpha = (*p == 'a') ? true : false;
151 if (hasAlpha) ++p; //skip "a"
153 int rh, gs, bl, alpha = 255;
156 while (isspace(*p)) p++;
157 rh = strtol(p, &p, 10);
159 rh = qRound(rh/100.0 * 255);
162 if (*p++ != ',') return QColor();
165 while (isspace(*p)) p++;
166 gs = strtol(p, &p, 10);
168 gs = qRound(gs/100.0 * 255);
171 if (*p++ != ',') return QColor();
174 while (isspace(*p)) p++;
175 bl = strtol(p, &p, 10);
177 bl = qRound(bl/100.0 * 255);
182 if (*p++!= ',') return QColor();
183 while (isspace(*p)) p++;
185 alpha = qRound(qstrtod(p, const_cast<const char **>(&p), &ok) * 255);
188 if (*p != ')') return QColor();
190 return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
192 return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
197 QFont qt_font_from_string(const QString& fontString) {
199 // ### this is simplified and incomplete
200 // ### TODO:get code from Qt webkit
201 const QStringList tokens = fontString.split(QLatin1Char(' '));
202 foreach (const QString &token, tokens) {
203 if (token == QLatin1String("italic"))
204 font.setItalic(true);
205 else if (token == QLatin1String("bold"))
207 else if (token.endsWith(QLatin1String("px"))) {
208 QString number = token;
209 number.remove(QLatin1String("px"));
210 //font.setPointSizeF(number.trimmed().toFloat());
211 font.setPixelSize(number.trimmed().toInt());
213 font.setFamily(token);
221 class QQuickContext2DEngineData : public QV8Engine::Deletable
224 QQuickContext2DEngineData(QV8Engine *engine);
225 ~QQuickContext2DEngineData();
227 v8::Persistent<v8::Function> constructorContext;
228 v8::Persistent<v8::Function> constructorGradient;
229 v8::Persistent<v8::Function> constructorPattern;
230 v8::Persistent<v8::Function> constructorPixelArray;
231 v8::Persistent<v8::Function> constructorImageData;
234 V8_DEFINE_EXTENSION(QQuickContext2DEngineData, engineData)
236 class QV8Context2DResource : public QV8ObjectResource
238 V8_RESOURCE_TYPE(Context2DType)
240 QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e), context(0) {}
241 QQuickContext2D* context;
244 class QV8Context2DStyleResource : public QV8ObjectResource
246 V8_RESOURCE_TYPE(Context2DStyleType)
248 QV8Context2DStyleResource(QV8Engine *e)
249 : QV8ObjectResource(e)
250 , patternRepeatX(false)
251 , patternRepeatY(false)
254 bool patternRepeatX:1;
255 bool patternRepeatY:1;
258 class QV8Context2DPixelArrayResource : public QV8ObjectResource
260 V8_RESOURCE_TYPE(Context2DPixelArrayType)
262 QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {}
267 QImage qt_image_convolute_filter(const QImage& src, const QVector<qreal>& weights, int radius = 0)
269 int sides = radius ? radius : qRound(qSqrt(weights.size()));
270 int half = qFloor(sides/2);
272 QImage dst = QImage(src.size(), src.format());
274 int h = src.height();
275 for (int y = 0; y < dst.height(); ++y) {
276 QRgb *dr = (QRgb*)dst.scanLine(y);
277 for (int x = 0; x < dst.width(); ++x) {
278 unsigned char* dRgb = ((unsigned char*)&dr[x]);
279 unsigned char red=0, green=0, blue=0, alpha=0;
283 for (int cy=0; cy<sides; cy++) {
284 for (int cx=0; cx<sides; cx++) {
285 int scy = sy + cy - half;
286 int scx = sx + cx - half;
287 if (scy >= 0 && scy < w && scx >= 0 && scx < h) {
288 const QRgb *sr = (const QRgb*)(src.constScanLine(scy));
289 const unsigned char* sRgb = ((const unsigned char*)&sr[scx]);
290 qreal wt = radius ? weights[0] : weights[cy*sides+cx];
292 green += sRgb[1] * wt;
293 blue += sRgb[2] * wt;
294 alpha += sRgb[3] * wt;
307 void qt_image_boxblur(QImage& image, int radius, bool quality)
309 int passes = quality? 3: 1;
310 for (int i=0; i < passes; i++) {
311 image = qt_image_convolute_filter(image, QVector<qreal>() << 1.0/(radius * radius * 1.0), radius);
315 static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
317 if (compositeOperator == QLatin1String("source-over")) {
318 return QPainter::CompositionMode_SourceOver;
319 } else if (compositeOperator == QLatin1String("source-out")) {
320 return QPainter::CompositionMode_SourceOut;
321 } else if (compositeOperator == QLatin1String("source-in")) {
322 return QPainter::CompositionMode_SourceIn;
323 } else if (compositeOperator == QLatin1String("source-atop")) {
324 return QPainter::CompositionMode_SourceAtop;
325 } else if (compositeOperator == QLatin1String("destination-atop")) {
326 return QPainter::CompositionMode_DestinationAtop;
327 } else if (compositeOperator == QLatin1String("destination-in")) {
328 return QPainter::CompositionMode_DestinationIn;
329 } else if (compositeOperator == QLatin1String("destination-out")) {
330 return QPainter::CompositionMode_DestinationOut;
331 } else if (compositeOperator == QLatin1String("destination-over")) {
332 return QPainter::CompositionMode_DestinationOver;
333 } else if (compositeOperator == QLatin1String("lighter")) {
334 return QPainter::CompositionMode_Lighten;
335 } else if (compositeOperator == QLatin1String("copy")) {
336 return QPainter::CompositionMode_Source;
337 } else if (compositeOperator == QLatin1String("xor")) {
338 return QPainter::CompositionMode_Xor;
339 } else if (compositeOperator == QLatin1String("qt-clear")) {
340 return QPainter::CompositionMode_Clear;
341 } else if (compositeOperator == QLatin1String("qt-destination")) {
342 return QPainter::CompositionMode_Destination;
343 } else if (compositeOperator == QLatin1String("qt-multiply")) {
344 return QPainter::CompositionMode_Multiply;
345 } else if (compositeOperator == QLatin1String("qt-screen")) {
346 return QPainter::CompositionMode_Screen;
347 } else if (compositeOperator == QLatin1String("qt-overlay")) {
348 return QPainter::CompositionMode_Overlay;
349 } else if (compositeOperator == QLatin1String("qt-darken")) {
350 return QPainter::CompositionMode_Darken;
351 } else if (compositeOperator == QLatin1String("qt-lighten")) {
352 return QPainter::CompositionMode_Lighten;
353 } else if (compositeOperator == QLatin1String("qt-color-dodge")) {
354 return QPainter::CompositionMode_ColorDodge;
355 } else if (compositeOperator == QLatin1String("qt-color-burn")) {
356 return QPainter::CompositionMode_ColorBurn;
357 } else if (compositeOperator == QLatin1String("qt-hard-light")) {
358 return QPainter::CompositionMode_HardLight;
359 } else if (compositeOperator == QLatin1String("qt-soft-light")) {
360 return QPainter::CompositionMode_SoftLight;
361 } else if (compositeOperator == QLatin1String("qt-difference")) {
362 return QPainter::CompositionMode_Difference;
363 } else if (compositeOperator == QLatin1String("qt-exclusion")) {
364 return QPainter::CompositionMode_Exclusion;
366 return QPainter::CompositionMode_SourceOver;
369 static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
372 case QPainter::CompositionMode_SourceOver:
373 return QLatin1String("source-over");
374 case QPainter::CompositionMode_DestinationOver:
375 return QLatin1String("destination-over");
376 case QPainter::CompositionMode_Clear:
377 return QLatin1String("qt-clear");
378 case QPainter::CompositionMode_Source:
379 return QLatin1String("copy");
380 case QPainter::CompositionMode_Destination:
381 return QLatin1String("qt-destination");
382 case QPainter::CompositionMode_SourceIn:
383 return QLatin1String("source-in");
384 case QPainter::CompositionMode_DestinationIn:
385 return QLatin1String("destination-in");
386 case QPainter::CompositionMode_SourceOut:
387 return QLatin1String("source-out");
388 case QPainter::CompositionMode_DestinationOut:
389 return QLatin1String("destination-out");
390 case QPainter::CompositionMode_SourceAtop:
391 return QLatin1String("source-atop");
392 case QPainter::CompositionMode_DestinationAtop:
393 return QLatin1String("destination-atop");
394 case QPainter::CompositionMode_Xor:
395 return QLatin1String("xor");
396 case QPainter::CompositionMode_Plus:
397 return QLatin1String("plus");
398 case QPainter::CompositionMode_Multiply:
399 return QLatin1String("qt-multiply");
400 case QPainter::CompositionMode_Screen:
401 return QLatin1String("qt-screen");
402 case QPainter::CompositionMode_Overlay:
403 return QLatin1String("qt-overlay");
404 case QPainter::CompositionMode_Darken:
405 return QLatin1String("qt-darken");
406 case QPainter::CompositionMode_Lighten:
407 return QLatin1String("lighter");
408 case QPainter::CompositionMode_ColorDodge:
409 return QLatin1String("qt-color-dodge");
410 case QPainter::CompositionMode_ColorBurn:
411 return QLatin1String("qt-color-burn");
412 case QPainter::CompositionMode_HardLight:
413 return QLatin1String("qt-hard-light");
414 case QPainter::CompositionMode_SoftLight:
415 return QLatin1String("qt-soft-light");
416 case QPainter::CompositionMode_Difference:
417 return QLatin1String("qt-difference");
418 case QPainter::CompositionMode_Exclusion:
419 return QLatin1String("qt-exclusion");
427 static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image)
429 QQuickContext2DEngineData *ed = engineData(engine);
430 v8::Local<v8::Object> imageData = ed->constructorImageData->NewInstance();
431 QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine);
432 if (image.isNull()) {
433 r->image = QImage(w, h, QImage::Format_ARGB32);
434 r->image.fill(0x00000000);
436 Q_ASSERT(image.width() == w && image.height() == h);
437 r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
439 v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
440 pixelData->SetExternalResource(r);
442 imageData->SetInternalField(0, pixelData);
446 //static script functions
449 \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas
450 Holds the canvas item that the context paints on.
452 This property is read only.
454 static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::AccessorInfo &info)
456 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
460 QV8Engine *engine = V8ENGINE_ACCESSOR();
462 return engine->newQObject(r->context->canvas());
466 \qmlmethod object QtQuick2::Context2D::restore()
467 Pops the top state on the stack, restoring the context to that state.
469 \sa QtQuick2::Context2D::save()
471 static v8::Handle<v8::Value> ctx2d_restore(const v8::Arguments &args)
473 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
476 r->context->popState();
481 \qmlmethod object QtQuick2::Context2D::reset()
482 Resets the context state and properties to the default values.
484 static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args)
486 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
495 \qmlmethod object QtQuick2::Context2D::save()
496 Pushes the current state onto the state stack.
498 Before changing any state attributes, you should save the current state
499 for future reference. The context maintains a stack of drawing states.
500 Each state consists of the current transformation matrix, clipping region,
501 and values of the following attributes:
503 \li\a QtQuick2::Context2D::strokeStyle
504 \li\a QtQuick2::Context2D::fillStyle
505 \li\a QtQuick2::Context2D::fillRule
506 \li\a QtQuick2::Context2D::globalAlpha
507 \li\a QtQuick2::Context2D::lineWidth
508 \li\a QtQuick2::Context2D::lineCap
509 \li\a QtQuick2::Context2D::lineJoin
510 \li\a QtQuick2::Context2D::miterLimit
511 \li\a QtQuick2::Context2D::shadowOffsetX
512 \li\a QtQuick2::Context2D::shadowOffsetY
513 \li\a QtQuick2::Context2D::shadowBlur
514 \li\a QtQuick2::Context2D::shadowColor
515 \li\a QtQuick2::Context2D::globalCompositeOperation
516 \li\a QtQuick2::Context2D::font
517 \li\a QtQuick2::Context2D::textAlign
518 \li\a QtQuick2::Context2D::textBaseline
521 The current path is NOT part of the drawing state. The path can be reset by
522 invoking the \a QtQuick2::Context2D::beginPath() method.
524 static v8::Handle<v8::Value> ctx2d_save(const v8::Arguments &args)
526 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
529 r->context->pushState();
536 \qmlmethod object QtQuick2::Context2D::rotate(real angle)
537 Rotate the canvas around the current origin by \c angle in radians and clockwise direction.
539 ctx.rotate(Math.PI/2);
541 \image qml-item-canvas-rotate.png
543 The rotation transformation matrix is as follows:
545 \image qml-item-canvas-math-rotate.png
547 where the \c angle of rotation is in radians.
550 static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args)
552 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
555 if (args.Length() == 1)
556 r->context->rotate(args[0]->NumberValue());
561 \qmlmethod object QtQuick2::Context2D::scale(real x, real y)
562 Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors
563 to the current tranform matrix.
564 Where \c x is the scale factor in the horizontal direction and \c y is the scale factor in the
566 The following code doubles the horizontal size of an object drawn on the canvas and half its
571 \image qml-item-canvas-scale.png
574 static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args)
576 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
580 if (args.Length() == 2)
581 r->context->scale(args[0]->NumberValue(), args[1]->NumberValue());
586 \qmlmethod object QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f)
587 Changes the transformation matrix to the matrix given by the arguments as described below.
589 Modifying the transformation matrix directly enables you to perform scaling,
590 rotating, and translating transformations in a single step.
592 Each point on the canvas is multiplied by the matrix before anything is
593 drawn. The \l{HTML5 Canvas API} defines the transformation matrix as:
595 \image qml-item-canvas-math.png
598 \li \c{a} is the scale factor in the horizontal (x) direction
599 \image qml-item-canvas-scalex.png
600 \li \c{c} is the skew factor in the x direction
601 \image qml-item-canvas-canvas-skewx.png
602 \li \c{e} is the translation in the x direction
603 \image qml-item-canvas-canvas-translate.png
604 \li \c{b} is the skew factor in the y (vertical) direction
605 \image qml-item-canvas-canvas-skewy.png
606 \li \c{d} is the scale factor in the y direction
607 \image qml-item-canvas-canvas-scaley.png
608 \li \c{f} is the translation in the y direction
609 \image qml-item-canvas-canvas-translatey.png
610 \li the last row remains constant
612 The scale factors and skew factors are multiples; \c{e} and \c{f} are
613 coordinate space units, just like the units in the \a QtQuick2::Context2D::translate(x,y)
616 \sa QtQuick2::Context2D::transform()
618 static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
620 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
624 if (args.Length() == 6)
625 r->context->setTransform( args[0]->NumberValue()
626 , args[1]->NumberValue()
627 , args[2]->NumberValue()
628 , args[3]->NumberValue()
629 , args[4]->NumberValue()
630 , args[5]->NumberValue());
636 \qmlmethod object QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f)
637 This method is very similar to \a QtQuick2::Context2D::setTransform(), but instead of replacing the old
638 tranform matrix, this method applies the given tranform matrix to the current matrix by mulitplying to it.
640 The \a setTransform(a, b, c, d, e, f) method actually resets the current transform to the identity matrix,
641 and then invokes the transform(a, b, c, d, e, f) method with the same arguments.
643 \sa QtQuick2::Context2D::setTransform()
645 static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
647 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
651 if (args.Length() == 6)
652 r->context->transform( args[0]->NumberValue()
653 , args[1]->NumberValue()
654 , args[2]->NumberValue()
655 , args[3]->NumberValue()
656 , args[4]->NumberValue()
657 , args[5]->NumberValue());
663 \qmlmethod object QtQuick2::Context2D::translate(real x, real y)
664 Translates the origin of the canvas to point (\c x, \c y).
666 \c x is the horizontal distance that the origin is translated, in coordinate space units,
667 \c y is the vertical distance that the origin is translated, in coordinate space units.
668 Translating the origin enables you to draw patterns of different objects on the canvas
669 without having to measure the coordinates manually for each shape.
671 static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
673 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
677 if (args.Length() == 2)
678 r->context->translate(args[0]->NumberValue(), args[1]->NumberValue());
684 \qmlmethod object QtQuick2::Context2D::resetTransform()
685 Reset the transformation matrix to default value.
687 \sa QtQuick2::Context2D::transform(), QtQuick2::Context2D::setTransform(), QtQuick2::Context2D::reset()
689 static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
691 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
694 r->context->setTransform(1, 0, 0, 1, 0, 0);
701 \qmlmethod object QtQuick2::Context2D::shear(real sh, real sv )
702 Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction.
704 static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args)
706 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
709 if (args.Length() == 2)
710 r->context->shear(args[0]->NumberValue(), args[1]->NumberValue());
717 \qmlproperty real QtQuick2::Context2D::globalAlpha
718 Holds the the current alpha value applied to rendering operations.
719 The value must be in the range from 0.0 (fully transparent) to 1.0 (fully opque).
720 The default value is 1.0.
722 static v8::Handle<v8::Value> ctx2d_globalAlpha(v8::Local<v8::String>, const v8::AccessorInfo &info)
724 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
727 return v8::Number::New(r->context->state.globalAlpha);
730 static void ctx2d_globalAlpha_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
732 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
733 CHECK_CONTEXT_SETTER(r)
735 qreal globalAlpha = value->NumberValue();
737 if (!qIsFinite(globalAlpha))
740 if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) {
741 r->context->state.globalAlpha = globalAlpha;
742 r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha);
747 \qmlproperty string QtQuick2::Context2D::globalCompositeOperation
748 Holds the the current the current composition operation, from the list below:
750 \li source-atop - A atop B. Display the source image wherever both images are opaque.
751 Display the destination image wherever the destination image is opaque but the source image is transparent.
752 Display transparency elsewhere.
753 \li source-in - A in B. Display the source image wherever both the source image and destination image are opaque.
754 Display transparency elsewhere.
755 \li source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
756 Display transparency elsewhere.
757 \li source-over - (default) A over B. Display the source image wherever the source image is opaque.
758 Display the destination image elsewhere.
759 \li destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
760 \li destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
761 \li destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
762 \li destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
763 \li lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
764 \li copy - A (B is ignored). Display the source image instead of the destination image.
765 \li xor - A xor B. Exclusive OR of the source image and destination image.
768 Additionally, this property also accepts the compositon modes listed in \a {QPainter::CompositionMode}. According to the W3C standard, these
769 extension composition modes are provided as "vendorName-operationName" syntax, for example: \c {QPainter::CompositionMode_Exclusion} is porvided as
772 static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
774 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
778 QV8Engine *engine = V8ENGINE_ACCESSOR();
780 return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation));
783 static void ctx2d_globalCompositeOperation_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
785 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
786 CHECK_CONTEXT_SETTER(r)
788 QV8Engine *engine = V8ENGINE_ACCESSOR();
791 QString mode = engine->toString(value);
792 QPainter::CompositionMode cm = qt_composite_mode_from_string(mode);
793 if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over"))
796 if (cm != r->context->state.globalCompositeOperation) {
797 r->context->state.globalCompositeOperation = cm;
798 r->context->buffer()->setGlobalCompositeOperation(cm);
804 \qmlproperty variant QtQuick2::Context2D::fillStyle
805 Holds the current style used for filling shapes.
806 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored.
807 This property accepts several color syntaxes:
809 \li 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)'
810 \li 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)'
811 \li 'hsl(hue, saturation, lightness)'
812 \li 'hsla(hue, saturation, lightness, alpha)'
813 \li '#RRGGBB' - for example: '#00FFCC'
814 \li Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0)
816 If the \a fillStyle or \a strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the
817 best performance, because it's already a valid QColor value, does not need to be parsed everytime.
819 The default value is '#000000'.
820 \sa QtQuick2::Context2D::createLinearGradient
821 \sa QtQuick2::Context2D::createRadialGradient
822 \sa QtQuick2::Context2D::createPattern
823 \sa QtQuick2::Context2D::strokeStyle
825 static v8::Handle<v8::Value> ctx2d_fillStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
827 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
830 QV8Engine *engine = V8ENGINE_ACCESSOR();
832 QColor color = r->context->state.fillStyle.color();
833 if (color.isValid()) {
834 if (color.alpha() == 255)
835 return engine->toString(color.name());
836 QString alphaString = QString::number(color.alphaF(), 'f');
837 while (alphaString.endsWith(QLatin1Char('0')))
839 if (alphaString.endsWith(QLatin1Char('.')))
840 alphaString += QLatin1Char('0');
841 return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
843 return r->context->m_fillStyle;
846 static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
848 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
849 CHECK_CONTEXT_SETTER(r)
851 QV8Engine *engine = V8ENGINE_ACCESSOR();
853 if (value->IsObject()) {
854 QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
855 if (color.isValid()) {
856 r->context->state.fillStyle = color;
857 r->context->buffer()->setFillStyle(color);
858 r->context->m_fillStyle = value;
860 QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
861 if (style && style->brush != r->context->state.fillStyle) {
862 r->context->state.fillStyle = style->brush;
863 r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
864 r->context->m_fillStyle = value;
865 r->context->state.fillPatternRepeatX = style->patternRepeatX;
866 r->context->state.fillPatternRepeatY = style->patternRepeatY;
869 } else if (value->IsString()) {
870 QColor color = qt_color_from_string(value);
871 if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
872 r->context->state.fillStyle = QBrush(color);
873 r->context->buffer()->setFillStyle(r->context->state.fillStyle);
874 r->context->m_fillStyle = value;
879 \qmlproperty enumeration QtQuick2::Context2D::fillRule
880 Holds the current fill rule used for filling shapes. The following fill rules supported:
885 Note: Unlike the \a QPainterPath, the Canvas API uses the winding fill as the default fill rule.
886 The fillRule property is part of the context rendering state.
888 \sa QtQuick2::Context2D::fillStyle
890 static v8::Handle<v8::Value> ctx2d_fillRule(v8::Local<v8::String>, const v8::AccessorInfo &info)
892 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
894 QV8Engine *engine = V8ENGINE_ACCESSOR();
896 return engine->fromVariant(r->context->state.fillRule);
899 static void ctx2d_fillRule_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
901 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
902 CHECK_CONTEXT_SETTER(r)
904 QV8Engine *engine = V8ENGINE_ACCESSOR();
906 if ((value->IsString() && engine->toString(value) == QStringLiteral("WindingFill"))
907 ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) {
908 r->context->state.fillRule = Qt::WindingFill;
909 } else if ((value->IsString() && engine->toString(value) == QStringLiteral("OddEvenFill"))
910 ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) {
911 r->context->state.fillRule = Qt::OddEvenFill;
915 r->context->m_path.setFillRule(r->context->state.fillRule);
918 \qmlproperty variant QtQuick2::Context2D::strokeStyle
919 Holds the current color or style to use for the lines around shapes,
920 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object.
921 Invalid values are ignored.
923 The default value is '#000000'.
925 \sa QtQuick2::Context2D::createLinearGradient
926 \sa QtQuick2::Context2D::createRadialGradient
927 \sa QtQuick2::Context2D::createPattern
928 \sa QtQuick2::Context2D::fillStyle
930 v8::Handle<v8::Value> ctx2d_strokeStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
932 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
935 QV8Engine *engine = V8ENGINE_ACCESSOR();
937 QColor color = r->context->state.strokeStyle.color();
938 if (color.isValid()) {
939 if (color.alpha() == 255)
940 return engine->toString(color.name());
941 QString alphaString = QString::number(color.alphaF(), 'f');
942 while (alphaString.endsWith(QLatin1Char('0')))
944 if (alphaString.endsWith(QLatin1Char('.')))
945 alphaString += QLatin1Char('0');
946 return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
948 return r->context->m_strokeStyle;
951 static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
953 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
954 CHECK_CONTEXT_SETTER(r)
956 QV8Engine *engine = V8ENGINE_ACCESSOR();
958 if (value->IsObject()) {
959 QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
960 if (color.isValid()) {
961 r->context->state.fillStyle = color;
962 r->context->buffer()->setStrokeStyle(color);
963 r->context->m_strokeStyle = value;
965 QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
966 if (style && style->brush != r->context->state.strokeStyle) {
967 r->context->state.strokeStyle = style->brush;
968 r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
969 r->context->m_strokeStyle = value;
970 r->context->state.strokePatternRepeatX = style->patternRepeatX;
971 r->context->state.strokePatternRepeatY = style->patternRepeatY;
975 } else if (value->IsString()) {
976 QColor color = qt_color_from_string(value);
977 if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
978 r->context->state.strokeStyle = QBrush(color);
979 r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
980 r->context->m_strokeStyle = value;
986 \qmlmethod object QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
987 Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between
988 the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
990 A gradient is a smooth transition between colors. There are two types of gradients: linear and radial.
991 Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between
992 to the gradient's starting and end points or circles.
994 \sa QtQuick2::Context2D::CanvasGradient::addColorStop
995 \sa QtQuick2::Context2D::createRadialGradient
996 \sa QtQuick2::Context2D::ctx2d_createConicalGradient
997 \sa QtQuick2::Context2D::createPattern
998 \sa QtQuick2::Context2D::fillStyle
999 \sa QtQuick2::Context2D::strokeStyle
1002 static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &args)
1004 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1008 QV8Engine *engine = V8ENGINE();
1010 if (args.Length() == 4) {
1011 QQuickContext2DEngineData *ed = engineData(engine);
1012 v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1013 QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1014 qreal x0 = args[0]->NumberValue();
1015 qreal y0 = args[1]->NumberValue();
1016 qreal x1 = args[2]->NumberValue();
1017 qreal y1 = args[3]->NumberValue();
1022 || !qIsFinite(y1)) {
1024 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
1027 r->brush = QLinearGradient(x0, y0, x1, y1);
1028 gradient->SetExternalResource(r);
1036 \qmlmethod object QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
1037 Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with
1038 origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1.
1040 \sa QtQuick2::Context2D::CanvasGradient::addColorStop
1041 \sa QtQuick2::Context2D::createLinearGradient
1042 \sa QtQuick2::Context2D::ctx2d_createConicalGradient
1043 \sa QtQuick2::Context2D::createPattern
1044 \sa QtQuick2::Context2D::fillStyle
1045 \sa QtQuick2::Context2D::strokeStyle
1048 static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &args)
1050 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1054 QV8Engine *engine = V8ENGINE();
1056 if (args.Length() == 6) {
1057 QQuickContext2DEngineData *ed = engineData(engine);
1058 v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1059 QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1061 qreal x0 = args[0]->NumberValue();
1062 qreal y0 = args[1]->NumberValue();
1063 qreal r0 = args[2]->NumberValue();
1064 qreal x1 = args[3]->NumberValue();
1065 qreal y1 = args[4]->NumberValue();
1066 qreal r1 = args[5]->NumberValue();
1073 || !qIsFinite(y1)) {
1075 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
1078 if (r0 < 0 || r1 < 0)
1079 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
1082 r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
1083 gradient->SetExternalResource(r);
1091 \qmlmethod object QtQuick2::Context2D::createConicalGradient(real x, real y, real angle)
1092 Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y)
1093 with start angle \c angle in units of radians.
1095 \sa QtQuick2::Context2D::CanvasGradient::addColorStop
1096 \sa QtQuick2::Context2D::createLinearGradient
1097 \sa QtQuick2::Context2D::ctx2d_createRadialGradient
1098 \sa QtQuick2::Context2D::createPattern
1099 \sa QtQuick2::Context2D::fillStyle
1100 \sa QtQuick2::Context2D::strokeStyle
1103 static v8::Handle<v8::Value> ctx2d_createConicalGradient(const v8::Arguments &args)
1105 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1109 QV8Engine *engine = V8ENGINE();
1111 if (args.Length() == 6) {
1112 QQuickContext2DEngineData *ed = engineData(engine);
1113 v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
1114 QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
1116 qreal x = args[0]->NumberValue();
1117 qreal y = args[1]->NumberValue();
1118 qreal angle = DEGREES(args[2]->NumberValue());
1119 if (!qIsFinite(x) || !qIsFinite(y)) {
1121 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
1124 if (!qIsFinite(angle)) {
1126 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
1129 r->brush = QConicalGradient(x, y, angle);
1130 gradient->SetExternalResource(r);
1137 \qmlmethod variant createPattern(Color color, enumeration patternMode)
1138 This is a overload function.
1139 Returns a CanvasPattern object that uses the given \c color and \c patternMode.
1140 The valid pattern modes are:
1143 \li Qt.Dense1Pattern
1144 \li Qt.Dense2Pattern
1145 \li Qt.Dense3Pattern
1146 \li Qt.Dense4Pattern
1147 \li Qt.Dense5Pattern
1148 \li Qt.Dense6Pattern
1149 \li Qt.Dense7Pattern
1155 \li Qt.DiagCrossPattern
1160 \qmlmethod variant createPattern(Image image, string repetition)
1161 Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
1163 The \a image parameter must be a valid Image item, a valid \a QtQuick2::CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception.
1165 The allowed values for \a repetition are:
1168 \li "repeat" - both directions
1169 \li "repeat-x - horizontal only
1170 \li "repeat-y" - vertical only
1171 \li "no-repeat" - neither
1174 If the repetition argument is empty or null, the value "repeat" is used.
1176 \sa QtQuick2::Context2D::strokeStyle
1177 \sa QtQuick2::Context2D::fillStyle
1179 static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
1181 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1185 QV8Engine *engine = V8ENGINE();
1187 if (args.Length() == 2) {
1188 QQuickContext2DEngineData *ed = engineData(engine);
1189 QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine);
1191 QColor color = engine->toVariant(args[0], qMetaTypeId<QColor>()).value<QColor>();
1192 if (color.isValid()) {
1193 int patternMode = args[1]->IntegerValue();
1194 Qt::BrushStyle style = Qt::SolidPattern;
1195 if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
1196 style = static_cast<Qt::BrushStyle>(patternMode);
1198 styleResouce->brush = QBrush(color, style);
1200 QImage patternTexture;
1202 if (args[0]->IsObject()) {
1203 QV8Context2DPixelArrayResource *pixelData = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject());
1205 patternTexture = pixelData->image;
1208 patternTexture = r->context->createPixmap(QUrl(engine->toString(args[0]->ToString())))->image();
1211 if (!patternTexture.isNull()) {
1212 styleResouce->brush.setTextureImage(patternTexture);
1214 QString repetition = engine->toString(args[1]);
1215 if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) {
1216 styleResouce->patternRepeatX = true;
1217 styleResouce->patternRepeatY = true;
1218 } else if (repetition == QStringLiteral("repeat-x")) {
1219 styleResouce->patternRepeatX = true;
1220 } else if (repetition == QStringLiteral("repeat-y")) {
1221 styleResouce->patternRepeatY = true;
1222 } else if (repetition == QStringLiteral("no-repeat")) {
1223 styleResouce->patternRepeatY = false;
1224 styleResouce->patternRepeatY = false;
1226 //TODO: exception: SYNTAX_ERR
1232 v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
1233 pattern->SetExternalResource(styleResouce);
1237 return v8::Undefined();
1242 \qmlproperty string QtQuick2::Context2D::lineCap
1243 Holds the the current line cap style.
1244 The possible line cap styles are:
1246 \li butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value.
1247 \li round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line.
1248 \li square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line.
1250 Other values are ignored.
1252 v8::Handle<v8::Value> ctx2d_lineCap(v8::Local<v8::String>, const v8::AccessorInfo &info)
1254 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1258 QV8Engine *engine = V8ENGINE_ACCESSOR();
1259 switch (r->context->state.lineCap) {
1261 return engine->toString(QLatin1String("round"));
1263 return engine->toString(QLatin1String("butt"));
1265 return engine->toString(QLatin1String("square"));
1269 return engine->toString(QLatin1String("butt"));;
1272 static void ctx2d_lineCap_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1274 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1275 CHECK_CONTEXT_SETTER(r)
1277 QV8Engine *engine = V8ENGINE_ACCESSOR();
1279 QString lineCap = engine->toString(value);
1280 Qt::PenCapStyle cap;
1281 if (lineCap == QLatin1String("round"))
1283 else if (lineCap == QLatin1String("butt"))
1285 else if (lineCap == QLatin1String("square"))
1286 cap = Qt::SquareCap;
1290 if (cap != r->context->state.lineCap) {
1291 r->context->state.lineCap = cap;
1292 r->context->buffer()->setLineCap(cap);
1297 \qmlproperty string QtQuick2::Context2D::lineJoin
1298 Holds the the current line join style. A join exists at any point in a subpath
1299 shared by two consecutive lines. When a subpath is closed, then a join also exists
1300 at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
1302 The possible line join styles are:
1304 \li bevel - this is all that is rendered at joins.
1305 \li round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins.
1306 \li miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style.
1308 Other values are ignored.
1310 v8::Handle<v8::Value> ctx2d_lineJoin(v8::Local<v8::String>, const v8::AccessorInfo &info)
1312 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1316 QV8Engine *engine = V8ENGINE_ACCESSOR();
1317 switch (r->context->state.lineJoin) {
1319 return engine->toString(QLatin1String("round"));
1321 return engine->toString(QLatin1String("bevel"));
1323 return engine->toString(QLatin1String("miter"));
1327 return engine->toString(QLatin1String("miter"));
1330 static void ctx2d_lineJoin_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1332 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1333 CHECK_CONTEXT_SETTER(r)
1335 QV8Engine *engine = V8ENGINE_ACCESSOR();
1337 QString lineJoin = engine->toString(value);
1338 Qt::PenJoinStyle join;
1339 if (lineJoin == QLatin1String("round"))
1340 join = Qt::RoundJoin;
1341 else if (lineJoin == QLatin1String("bevel"))
1342 join = Qt::BevelJoin;
1343 else if (lineJoin == QLatin1String("miter"))
1344 join = Qt::SvgMiterJoin;
1348 if (join != r->context->state.lineJoin) {
1349 r->context->state.lineJoin = join;
1350 r->context->buffer()->setLineJoin(join);
1355 \qmlproperty real QtQuick2::Context2D::lineWidth
1356 Holds the the current line width. Values that are not finite values greater than zero are ignored.
1358 v8::Handle<v8::Value> ctx2d_lineWidth(v8::Local<v8::String>, const v8::AccessorInfo &info)
1360 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1364 return v8::Number::New(r->context->state.lineWidth);
1367 static void ctx2d_lineWidth_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1369 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1370 CHECK_CONTEXT_SETTER(r)
1372 qreal w = value->NumberValue();
1374 if (w > 0 && qIsFinite(w) && w != r->context->state.lineWidth) {
1375 r->context->state.lineWidth = w;
1376 r->context->buffer()->setLineWidth(w);
1381 \qmlproperty real QtQuick2::Context2D::miterLimit
1382 Holds the current miter limit ratio.
1383 The default miter limit value is 10.0.
1385 v8::Handle<v8::Value> ctx2d_miterLimit(v8::Local<v8::String>, const v8::AccessorInfo &info)
1387 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1391 return v8::Number::New(r->context->state.miterLimit);
1394 static void ctx2d_miterLimit_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1396 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1397 CHECK_CONTEXT_SETTER(r)
1399 qreal ml = value->NumberValue();
1401 if (ml > 0 && qIsFinite(ml) && ml != r->context->state.miterLimit) {
1402 r->context->state.miterLimit = ml;
1403 r->context->buffer()->setMiterLimit(ml);
1409 \qmlproperty real QtQuick2::Context2D::shadowBlur
1410 Holds the current level of blur applied to shadows
1412 v8::Handle<v8::Value> ctx2d_shadowBlur(v8::Local<v8::String>, const v8::AccessorInfo &info)
1414 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1418 return v8::Number::New(r->context->state.shadowBlur);
1421 static void ctx2d_shadowBlur_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1423 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1424 CHECK_CONTEXT_SETTER(r)
1425 qreal blur = value->NumberValue();
1427 if (blur > 0 && qIsFinite(blur) && blur != r->context->state.shadowBlur) {
1428 r->context->state.shadowBlur = blur;
1429 r->context->buffer()->setShadowBlur(blur);
1434 \qmlproperty string QtQuick2::Context2D::shadowColor
1435 Holds the current shadow color.
1437 v8::Handle<v8::Value> ctx2d_shadowColor(v8::Local<v8::String>, const v8::AccessorInfo &info)
1439 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1443 QV8Engine *engine = V8ENGINE_ACCESSOR();
1445 return engine->toString(r->context->state.shadowColor.name());
1448 static void ctx2d_shadowColor_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1450 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1451 CHECK_CONTEXT_SETTER(r)
1453 QColor color = qt_color_from_string(value);
1455 if (color.isValid() && color != r->context->state.shadowColor) {
1456 r->context->state.shadowColor = color;
1457 r->context->buffer()->setShadowColor(color);
1463 \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX
1464 Holds the current shadow offset in the positive horizontal distance.
1466 \sa QtQuick2::Context2D::shadowOffsetY
1468 v8::Handle<v8::Value> ctx2d_shadowOffsetX(v8::Local<v8::String>, const v8::AccessorInfo &info)
1470 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1474 return v8::Number::New(r->context->state.shadowOffsetX);
1477 static void ctx2d_shadowOffsetX_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1479 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1480 CHECK_CONTEXT_SETTER(r)
1482 qreal offsetX = value->NumberValue();
1483 if (qIsFinite(offsetX) && offsetX != r->context->state.shadowOffsetX) {
1484 r->context->state.shadowOffsetX = offsetX;
1485 r->context->buffer()->setShadowOffsetX(offsetX);
1489 \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY
1490 Holds the current shadow offset in the positive vertical distance.
1492 \sa QtQuick2::Context2D::shadowOffsetX
1494 v8::Handle<v8::Value> ctx2d_shadowOffsetY(v8::Local<v8::String>, const v8::AccessorInfo &info)
1496 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1500 return v8::Number::New(r->context->state.shadowOffsetY);
1503 static void ctx2d_shadowOffsetY_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1505 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1506 CHECK_CONTEXT_SETTER(r)
1508 qreal offsetY = value->NumberValue();
1509 if (qIsFinite(offsetY) && offsetY != r->context->state.shadowOffsetY) {
1510 r->context->state.shadowOffsetY = offsetY;
1511 r->context->buffer()->setShadowOffsetY(offsetY);
1515 v8::Handle<v8::Value> ctx2d_path(v8::Local<v8::String>, const v8::AccessorInfo &info)
1517 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1519 return r->context->m_v8path;
1522 static void ctx2d_path_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
1524 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
1525 CHECK_CONTEXT_SETTER(r)
1526 QV8Engine *engine = V8ENGINE_ACCESSOR();
1528 r->context->beginPath();
1529 if (value->IsObject()) {
1530 QQuickPath* path = qobject_cast<QQuickPath*>(engine->toQObject(value));
1532 r->context->m_path = path->path();
1534 QString path = engine->toString(value->ToString());
1535 QQuickSvgParser::parsePathDataFast(path, r->context->m_path);
1537 r->context->m_v8path = value;
1542 \qmlmethod object QtQuick2::Context2D::clearRect(real x, real y, real w, real h)
1543 Clears all pixels on the canvas in the given rectangle to transparent black.
1545 static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
1547 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1551 if (args.Length() == 4)
1552 r->context->clearRect(args[0]->NumberValue(),
1553 args[1]->NumberValue(),
1554 args[2]->NumberValue(),
1555 args[3]->NumberValue());
1560 \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1561 Paint the specified rectangular area using the fillStyle.
1563 \sa QtQuick2::Context2D::fillStyle
1565 static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
1567 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1570 if (args.Length() == 4)
1571 r->context->fillRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1576 \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
1577 Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin,
1578 and (if appropriate) miterLimit attributes.
1580 \sa QtQuick2::Context2D::strokeStyle
1581 \sa QtQuick2::Context2D::lineWidth
1582 \sa QtQuick2::Context2D::lineJoin
1583 \sa QtQuick2::Context2D::miterLimit
1585 static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
1587 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1590 if (args.Length() == 4)
1591 r->context->strokeRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1596 // Complex shapes (paths) API
1598 \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
1599 Adds an arc to the current subpath that lies on the circumference of the circle whose center is at the point (\c x,\cy) and whose radius is \c radius.
1600 \image qml-item-canvas-arcTo2.png
1601 \sa QtQuick2::Context2D::arcTo,
1602 {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc}
1604 static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
1606 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1609 if (args.Length() >= 5) {
1610 bool antiClockwise = false;
1612 if (args.Length() == 6)
1613 antiClockwise = args[5]->BooleanValue();
1615 qreal radius = args[2]->NumberValue();
1617 if (qIsFinite(radius) && radius < 0)
1618 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1620 r->context->arc(args[0]->NumberValue(),
1621 args[1]->NumberValue(),
1623 args[3]->NumberValue(),
1624 args[4]->NumberValue(),
1632 \qmlmethod object QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius)
1634 Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line.
1635 To draw an arc, you begin with the same steps your followed to create a line:
1637 \li Call the context.beginPath() method to set a new path.
1638 \li Call the context.moveTo(\c x, \c y) method to set your starting position on the canvas at the point (\c x,\c y).
1639 \li To draw an arc or circle, call the context.arcTo(\c x1, \c y1, \c x2, \c y2,\c radius) method.
1640 This adds an arc with starting point (\c x1,\c y1), ending point (\c x2, \c y2), and radius \c radius to the current subpath and connects
1641 it to the previous subpath by a straight line.
1643 \image qml-item-canvas-arcTo.png
1644 Both startAngle and endAngle are measured from the x axis in units of radians.
1646 \image qml-item-canvas-startAngle.png
1647 The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction.
1648 \sa QtQuick2::Context2D::arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d
1649 context standard for arcTo}
1651 static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
1653 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1656 if (args.Length() == 5) {
1657 qreal radius = args[4]->NumberValue();
1659 if (qIsFinite(radius) && radius < 0)
1660 V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
1662 r->context->arcTo(args[0]->NumberValue(),
1663 args[1]->NumberValue(),
1664 args[2]->NumberValue(),
1665 args[3]->NumberValue(),
1673 \qmlmethod object QtQuick2::Context2D::beginPath()
1675 Resets the current path to a new path.
1677 static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
1679 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1683 r->context->beginPath();
1689 \qmlmethod object QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
1691 Adds a cubic Bezier curve between the current position and the given endPoint using the control points specified by (\c cp1x, cp1y),
1692 and (\c cp2x, \c cp2y).
1693 After the curve is added, the current position is updated to be at the end point (\c x, \c y) of the curve.
1694 The following code produces the path shown below:
1696 ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
1699 ctx.moveTo(20, 0);//start point
1700 ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
1703 \image qml-item-canvas-bezierCurveTo.png
1704 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
1705 \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
1707 static v8::Handle<v8::Value> ctx2d_bezierCurveTo(const v8::Arguments &args)
1709 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1713 if (args.Length() == 6) {
1714 qreal cp1x = args[0]->NumberValue();
1715 qreal cp1y = args[1]->NumberValue();
1716 qreal cp2x = args[2]->NumberValue();
1717 qreal cp2y = args[3]->NumberValue();
1718 qreal x = args[4]->NumberValue();
1719 qreal y = args[5]->NumberValue();
1721 if (!qIsFinite(cp1x) || !qIsFinite(cp1y) || !qIsFinite(cp2x) || !qIsFinite(cp2y) || !qIsFinite(x) || !qIsFinite(y))
1724 r->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
1731 \qmlmethod object QtQuick2::Context2D::clip()
1733 Creates the clipping region from the current path.
1734 Any parts of the shape outside the clipping path are not displayed.
1735 To create a complex shape using the \a clip() method:
1738 \li Call the \c{context.beginPath()} method to set the clipping path.
1739 \li Define the clipping path by calling any combination of the \c{lineTo},
1740 \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
1741 \li Call the \c{context.clip()} method.
1744 The new shape displays. The following shows how a clipping path can
1745 modify how an image displays:
1747 \image qml-canvas-clip-complex.png
1748 \sa QtQuick2::Context2D::beginPath()
1749 \sa QtQuick2::Context2D::closePath()
1750 \sa QtQuick2::Context2D::stroke()
1751 \sa QtQuick2::Context2D::fill()
1752 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
1754 static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
1756 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1764 \qmlmethod object QtQuick2::Context2D::closePath()
1765 Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
1766 The current point of the new path is the previous subpath's first point.
1768 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
1770 static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
1772 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1776 r->context->closePath();
1782 \qmlmethod object QtQuick2::Context2D::fill()
1784 Fills the subpaths with the current fill style.
1786 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
1788 \sa QtQuick2::Context2D::fillStyle
1790 static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args)
1792 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1799 \qmlmethod object QtQuick2::Context2D::lineTo(real x, real y)
1801 Draws a line from the current position to the point (x, y).
1803 static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
1805 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1809 if (args.Length() == 2) {
1810 qreal x = args[0]->NumberValue();
1811 qreal y = args[1]->NumberValue();
1813 if (!qIsFinite(x) || !qIsFinite(y))
1816 r->context->lineTo(x, y);
1823 \qmlmethod object QtQuick2::Context2D::moveTo(real x, real y)
1825 Creates a new subpath with the given point.
1827 static v8::Handle<v8::Value> ctx2d_moveTo(const v8::Arguments &args)
1829 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1832 if (args.Length() == 2) {
1833 qreal x = args[0]->NumberValue();
1834 qreal y = args[1]->NumberValue();
1836 if (!qIsFinite(x) || !qIsFinite(y))
1838 r->context->moveTo(x, y);
1844 \qmlmethod object QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
1846 Adds a quadratic Bezier curve between the current point and the endpoint (\c x, \c y) with the control point specified by (\c cpx, \c cpy).
1848 See {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for for quadraticCurveTo}
1850 static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
1852 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1855 if (args.Length() == 4) {
1856 qreal cpx = args[0]->NumberValue();
1857 qreal cpy = args[1]->NumberValue();
1858 qreal x = args[2]->NumberValue();
1859 qreal y = args[3]->NumberValue();
1861 if (!qIsFinite(cpx) || !qIsFinite(cpy) || !qIsFinite(x) || !qIsFinite(y))
1864 r->context->quadraticCurveTo(cpx, cpy, x, y);
1871 \qmlmethod object QtQuick2::Context2D::rect(real x, real y, real w, real h)
1873 Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath.
1875 static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
1877 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1880 if (args.Length() == 4)
1881 r->context->rect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1886 \qmlmethod object QtQuick2::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius)
1888 Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the
1889 ellipses defining the corners of the rounded rectangle.
1891 static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args)
1893 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1896 if (args.Length() == 6)
1897 r->context->roundedRect(args[0]->NumberValue()
1898 , args[1]->NumberValue()
1899 , args[2]->NumberValue()
1900 , args[3]->NumberValue()
1901 , args[4]->NumberValue()
1902 , args[5]->NumberValue());
1907 \qmlmethod object QtQuick2::Context2D::ellipse(real x, real y, real w, real h)
1909 Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h,
1910 and adds it to the path as a closed subpath.
1912 The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position).
1914 static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args)
1916 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1920 if (args.Length() == 4)
1921 r->context->ellipse(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue());
1927 \qmlmethod object QtQuick2::Context2D::text(string text, real x, real y)
1929 Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied.
1930 The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y).
1932 static v8::Handle<v8::Value> ctx2d_text(const v8::Arguments &args)
1934 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1937 QV8Engine *engine = V8ENGINE();
1938 if (args.Length() == 3) {
1939 qreal x = args[1]->NumberValue();
1940 qreal y = args[2]->NumberValue();
1942 if (!qIsFinite(x) || !qIsFinite(y))
1944 r->context->text(engine->toString(args[0]), x, y);
1950 \qmlmethod object QtQuick2::Context2D::stroke()
1952 Strokes the subpaths with the current stroke style.
1954 See {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
1956 \sa QtQuick2::Context2D::strokeStyle
1958 static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
1960 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1963 r->context->stroke();
1968 \qmlmethod object QtQuick2::Context2D::isPointInPath(real x, real y)
1970 Returns true if the given point is in the current path.
1972 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
1974 static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
1976 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
1979 bool pointInPath = false;
1980 if (args.Length() == 2)
1981 pointInPath = r->context->isPointInPath(args[0]->NumberValue(), args[1]->NumberValue());
1982 return v8::Boolean::New(pointInPath);
1985 static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
1989 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
1992 static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
1996 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
1999 static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
2003 V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
2007 \qmlproperty string QtQuick2::Context2D::font
2008 Holds the current font settings.
2010 The default font value is "10px sans-serif".
2011 See {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
2013 v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &info)
2015 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2018 QV8Engine *engine = V8ENGINE_ACCESSOR();
2020 return engine->toString(r->context->state.font.toString());
2023 static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2025 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2026 CHECK_CONTEXT_SETTER(r)
2028 QV8Engine *engine = V8ENGINE_ACCESSOR();
2029 QString fs = engine->toString(value);
2030 QFont font = qt_font_from_string(fs);
2031 if (font != r->context->state.font) {
2032 r->context->state.font = font;
2037 \qmlproperty string QtQuick2::Context2D::textAlign
2039 Holds the current text alignment settings.
2040 The possible values are:
2048 Other values are ignored. The default value is "start".
2050 v8::Handle<v8::Value> ctx2d_textAlign(v8::Local<v8::String>, const v8::AccessorInfo &info)
2052 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2054 QV8Engine *engine = V8ENGINE_ACCESSOR();
2055 switch (r->context->state.textAlign) {
2056 case QQuickContext2D::Start:
2057 return engine->toString(QLatin1String("start"));
2058 case QQuickContext2D::End:
2059 return engine->toString(QLatin1String("end"));
2060 case QQuickContext2D::Left:
2061 return engine->toString(QLatin1String("left"));
2062 case QQuickContext2D::Right:
2063 return engine->toString(QLatin1String("right"));
2064 case QQuickContext2D::Center:
2065 return engine->toString(QLatin1String("center"));
2069 return engine->toString(QLatin1String("start"));
2072 static void ctx2d_textAlign_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2074 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2075 CHECK_CONTEXT_SETTER(r)
2076 QV8Engine *engine = V8ENGINE_ACCESSOR();
2078 QString textAlign = engine->toString(value);
2080 QQuickContext2D::TextAlignType ta;
2081 if (textAlign == QLatin1String("start"))
2082 ta = QQuickContext2D::Start;
2083 else if (textAlign == QLatin1String("end"))
2084 ta = QQuickContext2D::End;
2085 else if (textAlign == QLatin1String("left"))
2086 ta = QQuickContext2D::Left;
2087 else if (textAlign == QLatin1String("right"))
2088 ta = QQuickContext2D::Right;
2089 else if (textAlign == QLatin1String("center"))
2090 ta = QQuickContext2D::Center;
2094 if (ta != r->context->state.textAlign) {
2095 r->context->state.textAlign = ta;
2100 \qmlproperty string QtQuick2::Context2D::textBaseline
2102 Holds the current baseline alignment settings.
2103 The possible values are:
2112 Other values are ignored. The default value is "alphabetic".
2114 v8::Handle<v8::Value> ctx2d_textBaseline(v8::Local<v8::String>, const v8::AccessorInfo &info)
2116 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2119 QV8Engine *engine = V8ENGINE_ACCESSOR();
2120 switch (r->context->state.textBaseline) {
2121 case QQuickContext2D::Alphabetic:
2122 return engine->toString(QLatin1String("alphabetic"));
2123 case QQuickContext2D::Hanging:
2124 return engine->toString(QLatin1String("hanging"));
2125 case QQuickContext2D::Top:
2126 return engine->toString(QLatin1String("top"));
2127 case QQuickContext2D::Bottom:
2128 return engine->toString(QLatin1String("bottom"));
2129 case QQuickContext2D::Middle:
2130 return engine->toString(QLatin1String("middle"));
2134 return engine->toString(QLatin1String("alphabetic"));
2137 static void ctx2d_textBaseline_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
2139 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
2140 CHECK_CONTEXT_SETTER(r)
2141 QV8Engine *engine = V8ENGINE_ACCESSOR();
2142 QString textBaseline = engine->toString(value);
2144 QQuickContext2D::TextBaseLineType tb;
2145 if (textBaseline == QLatin1String("alphabetic"))
2146 tb = QQuickContext2D::Alphabetic;
2147 else if (textBaseline == QLatin1String("hanging"))
2148 tb = QQuickContext2D::Hanging;
2149 else if (textBaseline == QLatin1String("top"))
2150 tb = QQuickContext2D::Top;
2151 else if (textBaseline == QLatin1String("bottom"))
2152 tb = QQuickContext2D::Bottom;
2153 else if (textBaseline == QLatin1String("middle"))
2154 tb = QQuickContext2D::Middle;
2158 if (tb != r->context->state.textBaseline) {
2159 r->context->state.textBaseline = tb;
2164 \qmlmethod object QtQuick2::Context2D::fillText(text, x, y)
2165 Fills the given text at the given position.
2166 \sa QtQuick2::Context2D::font
2167 \sa QtQuick2::Context2D::textAlign
2168 \sa QtQuick2::Context2D::textBaseline
2169 \sa QtQuick2::Context2D::strokeText
2171 static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
2173 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2176 QV8Engine *engine = V8ENGINE();
2177 if (args.Length() == 3) {
2178 qreal x = args[1]->NumberValue();
2179 qreal y = args[2]->NumberValue();
2180 if (!qIsFinite(x) || !qIsFinite(y))
2182 QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0]));
2183 r->context->buffer()->fill(textPath);
2188 \qmlmethod object QtQuick2::Context2D::strokeText(text, x, y)
2189 Strokes the given text at the given position.
2190 \sa QtQuick2::Context2D::font
2191 \sa QtQuick2::Context2D::textAlign
2192 \sa QtQuick2::Context2D::textBaseline
2193 \sa QtQuick2::Context2D::fillText
2195 static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
2197 QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
2200 QV8Engine *engine = V8ENGINE();
2201 if (args.Length() == 3)
2202 r->context->drawText(engine->toString(args[0]), args[1]->NumberValue(), args[2]->NumberValue(), false);
2206 \qmlclass 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 {http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details.
2215 \sa QtQuick2::Context2D::measureText
2216 \sa QtQuick2::TextMetrics::width
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 \a {QtQuick2::CanvasImageData} object.
2252 When given as Image item, if the image isn't fully loaded, this method draws nothing.
2253 When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
2254 This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object.
2256 \sa QtQuick2::CanvasImageData
2258 \sa QtQuick2::Canvas::loadImage
2259 \sa QtQuick2::Canvas::isImageLoaded
2260 \sa QtQuick2::Canvas::imageLoaded
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 \a {QtQuick2::CanvasImageData} object.
2272 When given as Image item, if the image isn't fully loaded, this method draws nothing.
2273 When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
2274 This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object.
2276 \sa QtQuick2::CanvasImageData
2278 \sa QtQuick2::Canvas::loadImage
2279 \sa QtQuick2::Canvas::isImageLoaded
2280 \sa QtQuick2::Canvas::imageLoaded
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 \a {QtQuick2::CanvasImageData} object.
2293 When given as Image item, if the image isn't fully loaded, this method draws nothing.
2294 When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
2295 This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object.
2297 \sa QtQuick2::CanvasImageData
2299 \sa QtQuick2::Canvas::loadImage
2300 \sa QtQuick2::Canvas::isImageLoaded
2301 \sa QtQuick2::Canvas::imageLoaded
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()->canvas()));
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->canvas()));
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 \qmlclass CanvasImageData
2410 \inqmlmodule QtQuick 2
2411 \ingroup qtquick-canvas
2412 \brief Contains image pixel data in RGBA order
2414 The \a QtQuick2::CanvasImageData object holds the image pixel data.
2416 The \a QtQuick2::CanvasImageData object has the actual dimensions of the data stored in
2417 this object and holds the one-dimensional array containing the data in RGBA order,
2418 as integers in the range 0 to 255.
2420 \sa QtQuick2::CanvasImageData::width
2421 \sa QtQuick2::CanvasImageData::height
2422 \sa QtQuick2::CanvasImageData::data
2423 \sa QtQuick2::Context2D::createImageData
2424 \sa QtQuick2::Context2D::getImageData
2425 \sa QtQuick2::Context2D::putImageData
2428 \qmlproperty 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 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 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 \qmlclass CanvasPixelArray
2463 \inqmlmodule QtQuick 2
2464 \ingroup qtquick-canvas
2465 \brief Provides ordered and indexed access to the components of each pixel in image data
2467 The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
2468 The CanvasPixelArray can be accessed as normal Javascript array.
2469 \sa QtQuick2::CanvasImageData
2470 \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
2474 \qmlproperty QtQuick2::CanvasPixelArray::length
2475 The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
2476 The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
2477 This property is read only.
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 QtQuick2::CanvasImageData createImageData(real sw, real sh)
2542 Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
2545 \qmlmethod QtQuick2::CanvasImageData createImageData(QtQuick2::CanvasImageData imageData)
2546 Creates a CanvasImageData object with the same dimensions as the argument.
2549 \qmlmethod QtQuick2::CanvasImageData createImageData(Url imageUrl)
2550 Creates a CanvasImageData object with the given image loaded from \a imageUrl.
2551 Note:The \a imageUrl must be already loaded before this function call, if not, an empty
2552 CanvasImageData obect will be returned.
2554 \sa QtQuick2::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 QtQuick2::CanvasImageData getImageData(real sx, real sy, real sw, real sh)
2593 Returns an CanvasImageData object containing the image data for the given rectangle of the canvas.
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(QtQuick2::CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
2622 Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted.
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 \qmlclass CanvasGradient
2702 \inqmlmodule QtQuick 2
2704 \ingroup qtquick-canvas
2705 \brief Provides an opaque CanvasGradient interface
2709 \qmlmethod QtQuick2::CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color)
2710 Adds a color stop with the given color to the gradient at the given offset.
2711 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
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:
3268 QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
3270 return m_canvas->loadedPixmap(url);
3273 QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
3275 const QFontMetrics metrics(state.font);
3276 int yoffset = baseLineOffset(static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
3277 int xoffset = textAlignOffset(static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
3279 QPainterPath textPath;
3281 textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
3282 textPath = state.matrix.map(textPath);
3287 static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
3289 // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
3290 return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x()));
3293 static inline bool withinRange(qreal p, qreal a, qreal b)
3295 return (p >= a && p <= b) || (p >= b && p <= a);
3298 bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
3300 if (!state.invertibleCTM)
3303 if (!m_path.elementCount())
3306 if (!qIsFinite(x) || !qIsFinite(y))
3309 QPointF point(x, y);
3310 QTransform ctm = state.matrix;
3311 QPointF p = ctm.inverted().map(point);
3312 if (!qIsFinite(p.x()) || !qIsFinite(p.y()))
3315 const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
3317 bool contains = m_path.contains(p);
3320 // check whether the point is on the border
3321 QPolygonF border = m_path.toFillPolygon();
3323 QPointF p1 = border.at(0);
3326 for (int i = 1; i < border.size(); ++i) {
3328 if (areCollinear(p, p1, p2)
3329 // Once we know that the points are collinear we
3330 // only need to check one of the coordinates
3331 && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ?
3332 withinRange(p.x(), p1.x(), p2.x()) :
3333 withinRange(p.y(), p1.y(), p2.y()))) {
3342 QQuickContext2D::QQuickContext2D(QObject *parent)
3343 : QQuickCanvasContext(parent)
3344 , m_buffer(new QQuickContext2DCommandBuffer)
3346 , m_windowManager(0)
3352 QQuickContext2D::~QQuickContext2D()
3357 v8::Handle<v8::Object> QQuickContext2D::v8value() const
3362 QStringList QQuickContext2D::contextNames() const
3364 return QStringList() << QLatin1String("2d");
3367 void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args)
3371 m_canvas = canvasItem;
3372 m_renderTarget = canvasItem->renderTarget();
3374 QQuickCanvas *canvas = canvasItem->canvas();
3375 m_windowManager = QQuickCanvasPrivate::get(canvas)->windowManager;
3376 m_renderStrategy = canvasItem->renderStrategy();
3378 switch (m_renderTarget) {
3379 case QQuickCanvasItem::Image:
3380 m_texture = new QQuickContext2DImageTexture(m_renderStrategy == QQuickCanvasItem::Threaded);
3382 case QQuickCanvasItem::FramebufferObject:
3384 m_texture = new QQuickContext2DFBOTexture;
3385 // No BufferQueueingOpenGL, falls back to Cooperative mode
3386 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL))
3387 m_renderStrategy = QQuickCanvasItem::Cooperative;
3392 m_texture->setItem(canvasItem);
3393 m_texture->setCanvasWindow(canvasItem->canvasWindow().toRect());
3394 m_texture->setTileSize(canvasItem->tileSize());
3395 m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
3396 m_texture->setSmooth(canvasItem->smooth());
3398 QThread *renderThread = QThread::currentThread();
3399 QThread *sceneGraphThread = canvas->openglContext() ? canvas->openglContext()->thread() : 0;
3401 if (m_renderStrategy == QQuickCanvasItem::Threaded)
3402 renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
3403 else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
3404 renderThread = sceneGraphThread;
3406 if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
3407 QOpenGLContext *cc = QQuickCanvasPrivate::get(canvas)->context->glContext();
3409 m_glContext = new QOpenGLContext;
3410 m_glContext->setFormat(cc->format());
3411 m_glContext->setShareContext(cc);
3412 if (renderThread != QThread::currentThread())
3413 m_glContext->moveToThread(renderThread);
3416 connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
3421 void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth)
3423 m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth);
3426 void QQuickContext2D::flush()
3428 if (!m_buffer->isEmpty()) {
3429 QMutexLocker lock(&m_bufferMutex);
3430 m_bufferQueue.enqueue(m_buffer);
3431 m_buffer = new QQuickContext2DCommandBuffer;
3435 switch (m_renderStrategy) {
3436 case QQuickCanvasItem::Immediate:
3437 // Cause the texture to consume paint commands immediately
3440 case QQuickCanvasItem::Threaded:
3441 // wake up thread to consume paint commands
3444 case QQuickCanvasItem::Cooperative:
3445 // NOTE: On SG Thread
3451 QSGDynamicTexture *QQuickContext2D::texture() const
3456 QImage QQuickContext2D::toImage(const QRectF& bounds)
3458 switch (m_renderStrategy) {
3459 case QQuickCanvasItem::Immediate:
3460 case QQuickCanvasItem::Threaded:
3463 case QQuickCanvasItem::Cooperative:
3467 return m_texture->toImage(bounds);
3471 QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine)
3473 v8::HandleScope handle_scope;
3474 v8::Context::Scope scope(engine->context());
3476 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
3477 ft->InstanceTemplate()->SetHasExternalResource(true);
3478 ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine));
3479 ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine));
3480 ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine));
3481 ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine));
3482 ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine));
3483 ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine));
3484 ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine));
3485 ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine));
3486 ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine));
3487 ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine));
3488 ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine));
3489 ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine));
3490 ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine));
3491 ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine));
3492 ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine));
3493 ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine));
3494 ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine));
3495 ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine));
3496 ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine));
3497 ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
3498 ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine));
3499 ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine));
3500 ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine));
3501 ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine));
3502 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine));
3503 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine));
3504 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine));
3505 ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine));
3506 ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine));
3507 ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine));
3508 ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine));
3509 ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine));
3510 ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine));
3511 ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine));
3512 ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine));
3513 ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine));
3514 ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine));
3515 ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine));
3516 ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine));
3517 ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine));
3518 ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine));
3519 ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine));
3520 ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine));
3521 ft->PrototypeTemplate()->Set(v8::String::New("roundedRect"), V8FUNCTION(ctx2d_roundedRect, engine));
3522 ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine));
3523 ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine));
3524 ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine));
3525 ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine));
3526 ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine));
3527 ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine));
3528 ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine));
3529 ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine));
3530 ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine));
3531 ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine));
3532 ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine));
3533 ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine));
3534 ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine));
3535 ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine));
3536 ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine));
3537 ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine));
3538 ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine));
3540 constructorContext = qPersistentNew(ft->GetFunction());
3542 v8::Local<v8::FunctionTemplate> ftGradient = v8::FunctionTemplate::New();
3543 ftGradient->InstanceTemplate()->SetHasExternalResource(true);
3544 ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine));
3545 constructorGradient = qPersistentNew(ftGradient->GetFunction());
3547 v8::Local<v8::FunctionTemplate> ftPattern = v8::FunctionTemplate::New();
3548 ftPattern->InstanceTemplate()->SetHasExternalResource(true);
3549 constructorPattern = qPersistentNew(ftPattern->GetFunction());
3551 v8::Local<v8::FunctionTemplate> ftPixelArray = v8::FunctionTemplate::New();
3552 ftPixelArray->InstanceTemplate()->SetHasExternalResource(true);
3553 ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine));
3554 ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine));
3555 constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction());
3557 v8::Local<v8::FunctionTemplate> ftImageData = v8::FunctionTemplate::New();
3558 ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine));
3559 ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine));
3560 ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine));
3561 ftImageData->InstanceTemplate()->SetInternalFieldCount(1);
3562 constructorImageData = qPersistentNew(ftImageData->GetFunction());
3565 QQuickContext2DEngineData::~QQuickContext2DEngineData()
3567 qPersistentDispose(constructorContext);
3568 qPersistentDispose(constructorGradient);
3569 qPersistentDispose(constructorPattern);
3570 qPersistentDispose(constructorImageData);
3571 qPersistentDispose(constructorPixelArray);
3574 void QQuickContext2D::popState()
3576 if (m_stateStack.isEmpty())
3579 QQuickContext2D::State newState = m_stateStack.pop();
3581 if (state.matrix != newState.matrix)
3582 buffer()->updateMatrix(newState.matrix);
3584 if (newState.globalAlpha != state.globalAlpha)
3585 buffer()->setGlobalAlpha(newState.globalAlpha);
3587 if (newState.globalCompositeOperation != state.globalCompositeOperation)
3588 buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
3590 if (newState.fillStyle != state.fillStyle)
3591 buffer()->setFillStyle(newState.fillStyle);
3593 if (newState.strokeStyle != state.strokeStyle)
3594 buffer()->setStrokeStyle(newState.strokeStyle);
3596 if (newState.lineWidth != state.lineWidth)
3597 buffer()->setLineWidth(newState.lineWidth);
3599 if (newState.lineCap != state.lineCap)
3600 buffer()->setLineCap(newState.lineCap);
3602 if (newState.lineJoin != state.lineJoin)
3603 buffer()->setLineJoin(newState.lineJoin);
3605 if (newState.miterLimit != state.miterLimit)
3606 buffer()->setMiterLimit(newState.miterLimit);
3608 if (newState.clipPath != state.clipPath) {
3609 buffer()->clip(newState.clipPath);
3612 if (newState.shadowBlur != state.shadowBlur)
3613 buffer()->setShadowBlur(newState.shadowBlur);
3615 if (newState.shadowColor != state.shadowColor)
3616 buffer()->setShadowColor(newState.shadowColor);
3618 if (newState.shadowOffsetX != state.shadowOffsetX)
3619 buffer()->setShadowOffsetX(newState.shadowOffsetX);
3621 if (newState.shadowOffsetY != state.shadowOffsetY)
3622 buffer()->setShadowOffsetY(newState.shadowOffsetY);
3623 m_path = state.matrix.map(m_path);
3625 m_path = state.matrix.inverted().map(m_path);
3627 void QQuickContext2D::pushState()
3629 m_stateStack.push(state);
3632 void QQuickContext2D::reset()
3634 QQuickContext2D::State newState;
3635 newState.matrix = QTransform();
3637 m_path = QPainterPath();
3639 QPainterPath defaultClipPath;
3641 QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
3642 r = r.united(m_canvas->canvasWindow().toRect());
3643 defaultClipPath.addRect(r);
3644 newState.clipPath = defaultClipPath;
3645 newState.clipPath.setFillRule(Qt::WindingFill);
3647 newState.strokeStyle = QColor("#000000");
3648 newState.fillStyle = QColor("#000000");
3649 newState.fillPatternRepeatX = false;
3650 newState.fillPatternRepeatY = false;
3651 newState.strokePatternRepeatX = false;
3652 newState.strokePatternRepeatY = false;
3653 newState.invertibleCTM = true;
3654 newState.fillRule = Qt::WindingFill;
3655 newState.globalAlpha = 1.0;
3656 newState.lineWidth = 1;
3657 newState.lineCap = Qt::FlatCap;
3658 newState.lineJoin = Qt::MiterJoin;
3659 newState.miterLimit = 10;
3660 newState.shadowOffsetX = 0;
3661 newState.shadowOffsetY = 0;
3662 newState.shadowBlur = 0;
3663 newState.shadowColor = qRgba(0, 0, 0, 0);
3664 newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
3665 newState.font = QFont(QLatin1String("sans-serif"), 10);
3666 newState.textAlign = QQuickContext2D::Start;
3667 newState.textBaseline = QQuickContext2D::Alphabetic;
3669 m_stateStack.clear();
3670 m_stateStack.push(newState);
3672 m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
3675 void QQuickContext2D::setV8Engine(QV8Engine *engine)
3677 v8::HandleScope handle_scope;
3678 v8::Context::Scope scope(engine->context());
3680 if (m_v8engine != engine) {
3681 m_v8engine = engine;
3683 qPersistentDispose(m_v8value);
3685 if (m_v8engine == 0)
3688 QQuickContext2DEngineData *ed = engineData(engine);
3689 m_v8value = qPersistentNew(ed->constructorContext->NewInstance());
3690 QV8Context2DResource *r = new QV8Context2DResource(engine);
3692 m_v8value->SetExternalResource(r);
3696 QQuickContext2DCommandBuffer* QQuickContext2D::nextBuffer()
3698 QMutexLocker lock(&m_bufferMutex);
3699 return m_bufferQueue.isEmpty() ? 0 : m_bufferQueue.dequeue();