1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickcontext2d_p.h"
5#include "qquickcontext2dcommandbuffer_p.h"
6#include "qquickcanvasitem_p.h"
7#include <private/qtquickglobal_p.h>
8#include <private/qquickcontext2dtexture_p.h>
9#include <private/qquickitem_p.h>
10#if QT_CONFIG(quick_shadereffect)
11#include <QtQuick/private/qquickshadereffectsource_p.h>
12#endif
13#include <qsgrendererinterface.h>
14
15#include <QtQuick/private/qsgcontext_p.h>
16#include <private/qquicksvgparser_p.h>
17#if QT_CONFIG(quick_path)
18#include <private/qquickpath_p.h>
19#endif
20#include <private/qquickimage_p_p.h>
21
22#include <qqmlinfo.h>
23
24#include <qqmlengine.h>
25#include <private/qv4domerrors_p.h>
26#include <private/qv4engine_p.h>
27#include <private/qv4object_p.h>
28#include <private/qv4qobjectwrapper_p.h>
29#include <private/qquickwindow_p.h>
30
31#include <private/qv4value_p.h>
32#include <private/qv4functionobject_p.h>
33#include <private/qv4objectproto_p.h>
34#include <private/qv4scopedvalue_p.h>
35#include <private/qlocale_tools_p.h>
36
37#include <QtCore/qmath.h>
38#include <QtCore/qvector.h>
39#include <QtCore/private/qnumeric_p.h>
40#include <QtCore/QRunnable>
41#include <QtGui/qguiapplication.h>
42#include <private/qguiapplication_p.h>
43#include <qpa/qplatformintegration.h>
44
45#include <private/qsgdefaultrendercontext_p.h>
46
47#include <cmath>
48#if defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
49#include <ctype.h>
50#endif
51
52QT_BEGIN_NAMESPACE
53/*!
54 \qmltype Context2D
55 \instantiates QQuickContext2D
56 \inqmlmodule QtQuick
57 \ingroup qtquick-canvas
58 \since 5.0
59 \brief Provides 2D context for shapes on a Canvas item.
60
61 The Context2D object can be created by \c Canvas item's \c getContext()
62 method:
63 \code
64 Canvas {
65 id:canvas
66 onPaint:{
67 var ctx = canvas.getContext('2d');
68 //...
69 }
70 }
71 \endcode
72 The Context2D API implements the same \l
73 {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard} with
74 some enhanced features.
75
76 The Context2D API provides the rendering \b{context} which defines the
77 methods and attributes needed to draw on the \c Canvas item. The following
78 assigns the canvas rendering context to a \c{context} variable:
79 \code
80 var context = mycanvas.getContext("2d")
81 \endcode
82
83 The Context2D API renders the canvas as a coordinate system whose origin
84 (0,0) is at the top left corner, as shown in the figure below. Coordinates
85 increase along the \c{x} axis from left to right and along the \c{y} axis
86 from top to bottom of the canvas.
87 \image qml-item-canvas-context.gif
88*/
89
90
91
92#define CHECK_CONTEXT(r) if (!r || !r->d()->context() || !r->d()->context()->bufferValid()) \
93 THROW_GENERIC_ERROR("Not a Context2D object");
94
95#define CHECK_CONTEXT_SETTER(r) if (!r || !r->d()->context() || !r->d()->context()->bufferValid()) \
96 THROW_GENERIC_ERROR("Not a Context2D object");
97#define qClamp(val, min, max) qMin(qMax(val, min), max)
98#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
99Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name)
100{
101 QByteArray str = name.toQString().toUtf8();
102
103 char *p = str.data();
104 int len = str.size();
105 //rgb/hsl color string has at least 7 characters
106 if (!p || len > 255 || len <= 7)
107 return QColor::fromString(name: p);
108 else {
109 bool isRgb(false), isHsl(false), hasAlpha(false);
110 Q_UNUSED(isHsl);
111
112 while (isspace(*p)) p++;
113 if (strncmp(s1: p, s2: "rgb", n: 3) == 0)
114 isRgb = true;
115 else if (strncmp(s1: p, s2: "hsl", n: 3) == 0)
116 isHsl = true;
117 else
118 return QColor::fromString(name: p);
119
120 p+=3; //skip "rgb" or "hsl"
121 hasAlpha = (*p == 'a') ? true : false;
122
123 ++p; //skip "("
124
125 if (hasAlpha) ++p; //skip "a"
126
127 int rh, gs, bl, alpha = 255;
128
129 //red
130 while (isspace(*p)) p++;
131 rh = strtol(nptr: p, endptr: &p, base: 10);
132 if (*p == '%') {
133 rh = qRound(d: rh/100.0 * 255);
134 ++p;
135 }
136 if (*p++ != ',') return QColor();
137
138 //green
139 while (isspace(*p)) p++;
140 gs = strtol(nptr: p, endptr: &p, base: 10);
141 if (*p == '%') {
142 gs = qRound(d: gs/100.0 * 255);
143 ++p;
144 }
145 if (*p++ != ',') return QColor();
146
147 //blue
148 while (isspace(*p)) p++;
149 bl = strtol(nptr: p, endptr: &p, base: 10);
150 if (*p == '%') {
151 bl = qRound(d: bl/100.0 * 255);
152 ++p;
153 }
154
155 if (hasAlpha) {
156 if (*p++!= ',') return QColor();
157 while (isspace(*p)) p++;
158 bool ok = false;
159 alpha = qRound(d: qstrtod(s00: p, se: const_cast<const char **>(&p), ok: &ok) * 255);
160 }
161
162 if (*p != ')') return QColor();
163 if (isRgb)
164 return QColor::fromRgba(rgba: qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
165 else if (isHsl)
166 return QColor::fromHsl(qClamp(rh, 0, 359), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
167 }
168 return QColor();
169}
170
171static int qParseFontSizeFromToken(QStringView fontSizeToken, bool &ok)
172{
173 ok = false;
174 float size = fontSizeToken.trimmed().toFloat(ok: &ok);
175 if (ok) {
176 return int(size);
177 }
178 qWarning().nospace() << "Context2D: A font size of " << fontSizeToken << " is invalid.";
179 return 0;
180}
181
182/*
183 Attempts to set the font size of \a font to \a fontSizeToken, returning
184 \c true if successful. If the font size is invalid, \c false is returned
185 and a warning is printed.
186*/
187static bool qSetFontSizeFromToken(QFont &font, QStringView fontSizeToken)
188{
189 const QStringView trimmedToken = fontSizeToken.trimmed();
190 const QStringView unitStr = trimmedToken.right(n: 2);
191 const QStringView value = trimmedToken.left(n: trimmedToken.size() - 2);
192 bool ok = false;
193 int size = 0;
194 if (unitStr == QLatin1String("px")) {
195 size = qParseFontSizeFromToken(fontSizeToken: value, ok);
196 if (ok) {
197 font.setPixelSize(size);
198 return true;
199 }
200 } else if (unitStr == QLatin1String("pt")) {
201 size = qParseFontSizeFromToken(fontSizeToken: value, ok);
202 if (ok) {
203 font.setPointSize(size);
204 return true;
205 }
206 } else {
207 qWarning().nospace() << "Context2D: Invalid font size unit in font string.";
208 }
209 return false;
210}
211
212/*
213 Returns a list of all of the families in \a fontFamiliesString, where
214 each family is separated by spaces. Families with spaces in their name
215 must be quoted.
216*/
217static QStringList qExtractFontFamiliesFromString(QStringView fontFamiliesString)
218{
219 QStringList extractedFamilies;
220 int quoteIndex = -1;
221 QString currentFamily;
222 for (int index = 0; index < fontFamiliesString.size(); ++index) {
223 const QChar ch = fontFamiliesString.at(n: index);
224 if (ch == u'"' || ch == u'\'') {
225 if (quoteIndex == -1) {
226 quoteIndex = index;
227 } else {
228 if (ch == fontFamiliesString.at(n: quoteIndex)) {
229 // Found the matching quote. +1/-1 because we don't want the quote as part of the name.
230 const QString family = fontFamiliesString.mid(pos: quoteIndex + 1, n: index - quoteIndex - 1).toString();
231 extractedFamilies.push_back(t: family);
232 currentFamily.clear();
233 quoteIndex = -1;
234 } else {
235 qWarning().nospace() << "Context2D: Mismatched quote in font string.";
236 return QStringList();
237 }
238 }
239 } else if (ch == u' ' && quoteIndex == -1) {
240 // This is a space that's not within quotes...
241 if (!currentFamily.isEmpty()) {
242 // and there is a current family; consider it the end of the current family.
243 extractedFamilies.push_back(t: currentFamily);
244 currentFamily.clear();
245 } // else: ignore the space
246 } else {
247 currentFamily.push_back(c: ch);
248 }
249 }
250 if (!currentFamily.isEmpty()) {
251 if (quoteIndex == -1) {
252 // This is the end of the string, so add this family to our list.
253 extractedFamilies.push_back(t: currentFamily);
254 } else {
255 qWarning().nospace() << "Context2D: Unclosed quote in font string.";
256 return QStringList();
257 }
258 }
259 if (extractedFamilies.isEmpty()) {
260 qWarning().nospace() << "Context2D: Missing or misplaced font family in font string"
261 << " (it must come after the font size).";
262 }
263 return extractedFamilies;
264}
265
266/*
267 Tries to set a family on \a font using the families provided in \a fontFamilyTokens.
268
269 The list is ordered by preference, with the first family having the highest preference.
270 If the first family is invalid, the next family in the list is evaluated.
271 This process is repeated until a valid font is found (at which point the function
272 will return \c true and the family set on \a font) or there are no more
273 families left, at which point a warning is printed and \c false is returned.
274*/
275static bool qSetFontFamilyFromTokens(QFont &font, const QStringList &fontFamilyTokens)
276{
277 for (const QString &fontFamilyToken : fontFamilyTokens) {
278 if (QFontDatabase::hasFamily(family: fontFamilyToken)) {
279 font.setFamily(fontFamilyToken);
280 return true;
281 } else {
282 // Can't find a family matching this name; if it's a generic family,
283 // try searching for the default family for it by using style hints.
284 int styleHint = -1;
285 if (fontFamilyToken.compare(other: QLatin1String("serif")) == 0) {
286 styleHint = QFont::Serif;
287 } else if (fontFamilyToken.compare(other: QLatin1String("sans-serif")) == 0) {
288 styleHint = QFont::SansSerif;
289 } else if (fontFamilyToken.compare(other: QLatin1String("cursive")) == 0) {
290 styleHint = QFont::Cursive;
291 } else if (fontFamilyToken.compare(other: QLatin1String("monospace")) == 0) {
292 styleHint = QFont::Monospace;
293 } else if (fontFamilyToken.compare(other: QLatin1String("fantasy")) == 0) {
294 styleHint = QFont::Fantasy;
295 }
296 if (styleHint != -1) {
297 QFont tmp;
298 tmp.setStyleHint(static_cast<QFont::StyleHint>(styleHint));
299 font.setFamily(tmp.defaultFamily());
300 return true;
301 }
302 }
303 }
304 qWarning(msg: "Context2D: The font families specified are invalid: %s", qPrintable(fontFamilyTokens.join(QString()).trimmed()));
305 return false;
306}
307
308enum FontToken
309{
310 NoTokens = 0x00,
311 FontStyle = 0x01,
312 FontVariant = 0x02,
313 FontWeight = 0x04
314};
315
316#define Q_TRY_SET_TOKEN(token, value, setStatement) \
317if (!(usedTokens & token)) { \
318 usedTokens |= token; \
319 setStatement; \
320} else { \
321 qWarning().nospace() << "Context2D: Duplicate token " << QLatin1String(value) << " found in font string."; \
322 return currentFont; \
323}
324
325/*
326 Parses a font string based on the CSS shorthand font property.
327
328 See: http://www.w3.org/TR/css3-fonts/#font-prop
329*/
330static QFont qt_font_from_string(const QString& fontString, const QFont &currentFont) {
331 if (fontString.isEmpty()) {
332 qWarning().nospace() << "Context2D: Font string is empty.";
333 return currentFont;
334 }
335
336 // We know that font-size must be specified and it must be before font-family
337 // (which could potentially have "px" or "pt" in its name), so extract it now.
338 int fontSizeEnd = fontString.indexOf(s: QLatin1String("px"));
339 if (fontSizeEnd == -1)
340 fontSizeEnd = fontString.indexOf(s: QLatin1String("pt"));
341 if (fontSizeEnd == -1) {
342 qWarning().nospace() << "Context2D: Invalid font size unit in font string.";
343 return currentFont;
344 }
345
346 int fontSizeStart = fontString.lastIndexOf(c: u' ', from: fontSizeEnd);
347 if (fontSizeStart == -1) {
348 // The font size might be the first token in the font string, which is OK.
349 // Regardless, we'll find out if the font is invalid with qSetFontSizeFromToken().
350 fontSizeStart = 0;
351 } else {
352 // Don't want to take the leading space.
353 ++fontSizeStart;
354 }
355
356 // + 2 for the unit, +1 for the space that we require.
357 fontSizeEnd += 3;
358
359 QFont newFont;
360 if (!qSetFontSizeFromToken(font&: newFont, fontSizeToken: QStringView{fontString}.mid(pos: fontSizeStart, n: fontSizeEnd - fontSizeStart)))
361 return currentFont;
362
363 // We don't want to parse the size twice, so remove it now.
364 QString remainingFontString = fontString;
365 remainingFontString.remove(i: fontSizeStart, len: fontSizeEnd - fontSizeStart);
366 QStringView remainingFontStringRef(remainingFontString);
367
368 // Next, we have to take any font families out, as QString::split() will ruin quoted family names.
369 const QStringView fontFamiliesString = remainingFontStringRef.mid(pos: fontSizeStart);
370 remainingFontStringRef.truncate(n: fontSizeStart);
371 QStringList fontFamilies = qExtractFontFamiliesFromString(fontFamiliesString);
372 if (fontFamilies.isEmpty()) {
373 return currentFont;
374 }
375 if (!qSetFontFamilyFromTokens(font&: newFont, fontFamilyTokens: fontFamilies))
376 return currentFont;
377
378 // Now that we've removed the messy parts, we can split the font string on spaces.
379 const QStringView trimmedTokensStr = remainingFontStringRef.trimmed();
380 if (trimmedTokensStr.isEmpty()) {
381 // No optional properties.
382 return newFont;
383 }
384 const auto tokens = trimmedTokensStr.split(sep: QLatin1Char(' '));
385
386 int usedTokens = NoTokens;
387 // Optional properties can be in any order, but font-size and font-family must be last.
388 for (const QStringView &token : tokens) {
389 if (token.compare(s: QLatin1String("normal")) == 0) {
390 if (!(usedTokens & FontStyle) || !(usedTokens & FontVariant) || !(usedTokens & FontWeight)) {
391 // Could be font-style, font-variant or font-weight.
392 if (!(usedTokens & FontStyle)) {
393 // QFont::StyleNormal is the default for QFont::style.
394 usedTokens = usedTokens | FontStyle;
395 } else if (!(usedTokens & FontVariant)) {
396 // QFont::MixedCase is the default for QFont::capitalization.
397 usedTokens |= FontVariant;
398 } else if (!(usedTokens & FontWeight)) {
399 // QFont::Normal is the default for QFont::weight.
400 usedTokens |= FontWeight;
401 }
402 } else {
403 qWarning().nospace() << "Context2D: Duplicate token \"normal\" found in font string.";
404 return currentFont;
405 }
406 } else if (token.compare(s: QLatin1String("bold")) == 0) {
407 Q_TRY_SET_TOKEN(FontWeight, "bold", newFont.setBold(true))
408 } else if (token.compare(s: QLatin1String("italic")) == 0) {
409 Q_TRY_SET_TOKEN(FontStyle, "italic", newFont.setStyle(QFont::StyleItalic))
410 } else if (token.compare(s: QLatin1String("oblique")) == 0) {
411 Q_TRY_SET_TOKEN(FontStyle, "oblique", newFont.setStyle(QFont::StyleOblique))
412 } else if (token.compare(s: QLatin1String("small-caps")) == 0) {
413 Q_TRY_SET_TOKEN(FontVariant, "small-caps", newFont.setCapitalization(QFont::SmallCaps))
414 } else {
415 bool conversionOk = false;
416 int weight = token.toInt(ok: &conversionOk);
417 if (conversionOk) {
418 Q_TRY_SET_TOKEN(FontWeight, "<font-weight>",
419 newFont.setWeight(QFont::Weight(weight)))
420 } else {
421 // The token is invalid or in the wrong place/order in the font string.
422 qWarning().nospace() << "Context2D: Invalid or misplaced token " << token
423 << " found in font string.";
424 return currentFont;
425 }
426 }
427 }
428 return newFont;
429}
430
431class QQuickContext2DEngineData : public QV4::ExecutionEngine::Deletable
432{
433public:
434 QQuickContext2DEngineData(QV4::ExecutionEngine *engine);
435 ~QQuickContext2DEngineData();
436
437 QV4::PersistentValue contextPrototype;
438 QV4::PersistentValue gradientProto;
439 QV4::PersistentValue pixelArrayProto;
440};
441
442V4_DEFINE_EXTENSION(QQuickContext2DEngineData, engineData)
443
444namespace QV4 {
445namespace Heap {
446
447struct QQuickJSContext2D : Object {
448 void init()
449 {
450 Object::init();
451 m_context = nullptr;
452 }
453
454 void destroy()
455 {
456 delete m_context;
457 Object::destroy();
458 }
459
460 QQuickContext2D *context() { return m_context ? *m_context : nullptr; }
461 void setContext(QQuickContext2D *context)
462 {
463 if (m_context)
464 *m_context = context;
465 else
466 m_context = new QPointer<QQuickContext2D>(context);
467 }
468
469private:
470 QPointer<QQuickContext2D>* m_context;
471};
472
473struct QQuickJSContext2DPrototype : Object {
474 void init() { Object::init(); }
475};
476
477struct QQuickContext2DStyle : Object {
478 void init()
479 {
480 brush = new QBrush;
481 patternRepeatX = false;
482 patternRepeatY = false;
483 }
484 void destroy() {
485 delete brush;
486 Object::destroy();
487 }
488
489 QBrush *brush;
490 bool patternRepeatX:1;
491 bool patternRepeatY:1;
492};
493
494struct QQuickJSContext2DPixelData : Object {
495 void init();
496 void destroy() {
497 delete image;
498 Object::destroy();
499 }
500
501 QImage *image;
502};
503
504struct QQuickJSContext2DImageData : Object {
505 void init();
506
507 static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack) {
508 static_cast<QQuickJSContext2DImageData *>(that)->pixelData.mark(markStack);
509 Object::markObjects(base: that, stack: markStack);
510 }
511
512 QV4::Value pixelData;
513};
514
515}
516}
517
518struct QQuickJSContext2D : public QV4::Object
519{
520 V4_OBJECT2(QQuickJSContext2D, QV4::Object)
521 V4_NEEDS_DESTROY
522
523 static QV4::ReturnedValue method_get_globalAlpha(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
524 static QV4::ReturnedValue method_set_globalAlpha(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
525 static QV4::ReturnedValue method_get_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
526 static QV4::ReturnedValue method_set_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
527 static QV4::ReturnedValue method_get_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
528 static QV4::ReturnedValue method_set_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
529 static QV4::ReturnedValue method_get_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
530 static QV4::ReturnedValue method_set_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
531 static QV4::ReturnedValue method_get_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
532 static QV4::ReturnedValue method_set_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
533
534 static QV4::ReturnedValue method_get_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
535 static QV4::ReturnedValue method_set_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
536 static QV4::ReturnedValue method_get_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
537 static QV4::ReturnedValue method_set_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
538 static QV4::ReturnedValue method_get_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
539 static QV4::ReturnedValue method_set_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
540 static QV4::ReturnedValue method_get_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
541 static QV4::ReturnedValue method_set_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
542 static QV4::ReturnedValue method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
543 static QV4::ReturnedValue method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
544
545 static QV4::ReturnedValue method_get_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
546 static QV4::ReturnedValue method_set_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
547 static QV4::ReturnedValue method_get_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
548 static QV4::ReturnedValue method_set_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
549 static QV4::ReturnedValue method_get_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
550 static QV4::ReturnedValue method_set_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
551 static QV4::ReturnedValue method_get_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
552 static QV4::ReturnedValue method_set_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
553
554 // should these two be on the proto?
555#if QT_CONFIG(quick_path)
556 static QV4::ReturnedValue method_get_path(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
557 static QV4::ReturnedValue method_set_path(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
558#endif
559 static QV4::ReturnedValue method_get_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
560 static QV4::ReturnedValue method_set_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
561 static QV4::ReturnedValue method_get_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
562 static QV4::ReturnedValue method_set_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
563 static QV4::ReturnedValue method_get_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
564 static QV4::ReturnedValue method_set_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
565};
566
567DEFINE_OBJECT_VTABLE(QQuickJSContext2D);
568
569
570struct QQuickJSContext2DPrototype : public QV4::Object
571{
572 V4_OBJECT2(QQuickJSContext2DPrototype, QV4::Object)
573public:
574 static QV4::Heap::QQuickJSContext2DPrototype *create(QV4::ExecutionEngine *engine)
575 {
576 QV4::Scope scope(engine);
577 QV4::Scoped<QQuickJSContext2DPrototype> o(scope, engine->memoryManager->allocate<QQuickJSContext2DPrototype>());
578
579 o->defineDefaultProperty(QStringLiteral("quadraticCurveTo"), code: method_quadraticCurveTo, argumentCount: 0);
580 o->defineDefaultProperty(QStringLiteral("restore"), code: method_restore, argumentCount: 0);
581 o->defineDefaultProperty(QStringLiteral("moveTo"), code: method_moveTo, argumentCount: 0);
582 o->defineDefaultProperty(QStringLiteral("lineTo"), code: method_lineTo, argumentCount: 0);
583 o->defineDefaultProperty(QStringLiteral("caretBlinkRate"), code: method_caretBlinkRate, argumentCount: 0);
584 o->defineDefaultProperty(QStringLiteral("clip"), code: method_clip, argumentCount: 0);
585 o->defineDefaultProperty(QStringLiteral("setTransform"), code: method_setTransform, argumentCount: 0);
586 o->defineDefaultProperty(QStringLiteral("text"), code: method_text, argumentCount: 0);
587 o->defineDefaultProperty(QStringLiteral("roundedRect"), code: method_roundedRect, argumentCount: 0);
588 o->defineDefaultProperty(QStringLiteral("createPattern"), code: method_createPattern, argumentCount: 0);
589 o->defineDefaultProperty(QStringLiteral("stroke"), code: method_stroke, argumentCount: 0);
590 o->defineDefaultProperty(QStringLiteral("arc"), code: method_arc, argumentCount: 0);
591 o->defineDefaultProperty(QStringLiteral("createImageData"), code: method_createImageData, argumentCount: 0);
592 o->defineDefaultProperty(QStringLiteral("measureText"), code: method_measureText, argumentCount: 0);
593 o->defineDefaultProperty(QStringLiteral("ellipse"), code: method_ellipse, argumentCount: 0);
594 o->defineDefaultProperty(QStringLiteral("fill"), code: method_fill, argumentCount: 0);
595 o->defineDefaultProperty(QStringLiteral("save"), code: method_save, argumentCount: 0);
596 o->defineDefaultProperty(QStringLiteral("scale"), code: method_scale, argumentCount: 0);
597 o->defineDefaultProperty(QStringLiteral("drawImage"), code: method_drawImage, argumentCount: 0);
598 o->defineDefaultProperty(QStringLiteral("transform"), code: method_transform, argumentCount: 0);
599 o->defineDefaultProperty(QStringLiteral("fillText"), code: method_fillText, argumentCount: 0);
600 o->defineDefaultProperty(QStringLiteral("strokeText"), code: method_strokeText, argumentCount: 0);
601 o->defineDefaultProperty(QStringLiteral("translate"), code: method_translate, argumentCount: 0);
602 o->defineDefaultProperty(QStringLiteral("createRadialGradient"), code: method_createRadialGradient, argumentCount: 0);
603 o->defineDefaultProperty(QStringLiteral("shear"), code: method_shear, argumentCount: 0);
604 o->defineDefaultProperty(QStringLiteral("isPointInPath"), code: method_isPointInPath, argumentCount: 0);
605 o->defineDefaultProperty(QStringLiteral("bezierCurveTo"), code: method_bezierCurveTo, argumentCount: 0);
606 o->defineDefaultProperty(QStringLiteral("resetTransform"), code: method_resetTransform, argumentCount: 0);
607 o->defineDefaultProperty(QStringLiteral("arcTo"), code: method_arcTo, argumentCount: 0);
608 o->defineDefaultProperty(QStringLiteral("fillRect"), code: method_fillRect, argumentCount: 0);
609 o->defineDefaultProperty(QStringLiteral("createConicalGradient"), code: method_createConicalGradient, argumentCount: 0);
610 o->defineDefaultProperty(QStringLiteral("drawFocusRing"), code: method_drawFocusRing, argumentCount: 0);
611 o->defineDefaultProperty(QStringLiteral("beginPath"), code: method_beginPath, argumentCount: 0);
612 o->defineDefaultProperty(QStringLiteral("clearRect"), code: method_clearRect, argumentCount: 0);
613 o->defineDefaultProperty(QStringLiteral("rect"), code: method_rect, argumentCount: 0);
614 o->defineDefaultProperty(QStringLiteral("reset"), code: method_reset, argumentCount: 0);
615 o->defineDefaultProperty(QStringLiteral("rotate"), code: method_rotate, argumentCount: 0);
616 o->defineDefaultProperty(QStringLiteral("setCaretSelectionRect"), code: method_setCaretSelectionRect, argumentCount: 0);
617 o->defineDefaultProperty(QStringLiteral("putImageData"), code: method_putImageData, argumentCount: 0);
618 o->defineDefaultProperty(QStringLiteral("getImageData"), code: method_getImageData, argumentCount: 0);
619 o->defineDefaultProperty(QStringLiteral("createLinearGradient"), code: method_createLinearGradient, argumentCount: 0);
620 o->defineDefaultProperty(QStringLiteral("strokeRect"), code: method_strokeRect, argumentCount: 0);
621 o->defineDefaultProperty(QStringLiteral("closePath"), code: method_closePath, argumentCount: 0);
622 o->defineDefaultProperty(QStringLiteral("setLineDash"), code: method_setLineDash, argumentCount: 0);
623 o->defineDefaultProperty(QStringLiteral("getLineDash"), code: method_getLineDash, argumentCount: 0);
624 o->defineAccessorProperty(QStringLiteral("canvas"), getter: QQuickJSContext2DPrototype::method_get_canvas, setter: nullptr);
625
626 return o->d();
627 }
628
629 static QV4::ReturnedValue method_get_canvas(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
630 static QV4::ReturnedValue method_restore(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
631 static QV4::ReturnedValue method_reset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
632 static QV4::ReturnedValue method_save(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
633 static QV4::ReturnedValue method_rotate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
634 static QV4::ReturnedValue method_scale(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
635 static QV4::ReturnedValue method_translate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
636 static QV4::ReturnedValue method_setTransform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
637 static QV4::ReturnedValue method_transform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
638 static QV4::ReturnedValue method_resetTransform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
639 static QV4::ReturnedValue method_shear(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
640 static QV4::ReturnedValue method_createLinearGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
641 static QV4::ReturnedValue method_createRadialGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
642 static QV4::ReturnedValue method_createConicalGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
643 static QV4::ReturnedValue method_createPattern(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
644 static QV4::ReturnedValue method_clearRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
645 static QV4::ReturnedValue method_fillRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
646 static QV4::ReturnedValue method_strokeRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
647 static QV4::ReturnedValue method_arc(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
648 static QV4::ReturnedValue method_arcTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
649 static QV4::ReturnedValue method_beginPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
650 static QV4::ReturnedValue method_bezierCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
651 static QV4::ReturnedValue method_clip(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
652 static QV4::ReturnedValue method_closePath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
653 static QV4::ReturnedValue method_fill(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
654 static QV4::ReturnedValue method_lineTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
655 static QV4::ReturnedValue method_moveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
656 static QV4::ReturnedValue method_quadraticCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
657 static QV4::ReturnedValue method_rect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
658 static QV4::ReturnedValue method_roundedRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
659 static QV4::ReturnedValue method_ellipse(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
660 static QV4::ReturnedValue method_text(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
661 static QV4::ReturnedValue method_stroke(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
662 static QV4::ReturnedValue method_isPointInPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
663 static QV4::ReturnedValue method_drawFocusRing(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
664 static QV4::ReturnedValue method_setCaretSelectionRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
665 static QV4::ReturnedValue method_caretBlinkRate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
666 static QV4::ReturnedValue method_fillText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
667 static QV4::ReturnedValue method_strokeText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
668 static QV4::ReturnedValue method_measureText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
669 static QV4::ReturnedValue method_drawImage(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
670 static QV4::ReturnedValue method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
671 static QV4::ReturnedValue method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
672 static QV4::ReturnedValue method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
673 static QV4::ReturnedValue method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
674 static QV4::ReturnedValue method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
675
676};
677
678DEFINE_OBJECT_VTABLE(QQuickJSContext2DPrototype);
679
680
681struct QQuickContext2DStyle : public QV4::Object
682{
683 V4_OBJECT2(QQuickContext2DStyle, QV4::Object)
684 V4_NEEDS_DESTROY
685
686 static QV4::ReturnedValue gradient_proto_addColorStop(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
687};
688
689
690
691DEFINE_OBJECT_VTABLE(QQuickContext2DStyle);
692
693QImage qt_image_convolute_filter(const QImage& src, const QVector<qreal>& weights, int radius = 0)
694{
695 // weights 3x3 => delta 1
696 int delta = radius ? radius : qFloor(v: qSqrt(v: weights.size()) / qreal(2));
697 int filterDim = 2 * delta + 1;
698
699 QImage dst = QImage(src.size(), src.format());
700
701 int w = src.width();
702 int h = src.height();
703
704 const QRgb *sr = (const QRgb *)(src.constBits());
705 int srcStride = src.bytesPerLine() / 4;
706
707 QRgb *dr = (QRgb*)dst.bits();
708 int dstStride = dst.bytesPerLine() / 4;
709
710 for (int y = 0; y < h; ++y) {
711 for (int x = 0; x < w; ++x) {
712 int red = 0;
713 int green = 0;
714 int blue = 0;
715 int alpha = 0;
716
717 qreal redF = 0;
718 qreal greenF = 0;
719 qreal blueF = 0;
720 qreal alphaF = 0;
721
722 int sy = y;
723 int sx = x;
724
725 for (int cy = 0; cy < filterDim; ++cy) {
726 int scy = sy + cy - delta;
727
728 if (scy < 0 || scy >= h)
729 continue;
730
731 const QRgb *sry = sr + scy * srcStride;
732
733 for (int cx = 0; cx < filterDim; ++cx) {
734 int scx = sx + cx - delta;
735
736 if (scx < 0 || scx >= w)
737 continue;
738
739 const QRgb col = sry[scx];
740
741 if (radius) {
742 red += qRed(rgb: col);
743 green += qGreen(rgb: col);
744 blue += qBlue(rgb: col);
745 alpha += qAlpha(rgb: col);
746 } else {
747 qreal wt = weights[cy * filterDim + cx];
748
749 redF += qRed(rgb: col) * wt;
750 greenF += qGreen(rgb: col) * wt;
751 blueF += qBlue(rgb: col) * wt;
752 alphaF += qAlpha(rgb: col) * wt;
753 }
754 }
755 }
756
757 if (radius)
758 dr[x] = qRgba(r: qRound(d: red * weights[0]), g: qRound(d: green * weights[0]), b: qRound(d: blue * weights[0]), a: qRound(d: alpha * weights[0]));
759 else
760 dr[x] = qRgba(r: qRound(d: redF), g: qRound(d: greenF), b: qRound(d: blueF), a: qRound(d: alphaF));
761 }
762
763 dr += dstStride;
764 }
765
766 return dst;
767}
768
769void qt_image_boxblur(QImage& image, int radius, bool quality)
770{
771 int passes = quality? 3: 1;
772 int filterSize = 2 * radius + 1;
773 for (int i = 0; i < passes; ++i)
774 image = qt_image_convolute_filter(src: image, weights: QVector<qreal>() << 1.0 / (filterSize * filterSize), radius);
775}
776
777static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
778{
779 if (compositeOperator == QLatin1String("source-over")) {
780 return QPainter::CompositionMode_SourceOver;
781 } else if (compositeOperator == QLatin1String("source-out")) {
782 return QPainter::CompositionMode_SourceOut;
783 } else if (compositeOperator == QLatin1String("source-in")) {
784 return QPainter::CompositionMode_SourceIn;
785 } else if (compositeOperator == QLatin1String("source-atop")) {
786 return QPainter::CompositionMode_SourceAtop;
787 } else if (compositeOperator == QLatin1String("destination-atop")) {
788 return QPainter::CompositionMode_DestinationAtop;
789 } else if (compositeOperator == QLatin1String("destination-in")) {
790 return QPainter::CompositionMode_DestinationIn;
791 } else if (compositeOperator == QLatin1String("destination-out")) {
792 return QPainter::CompositionMode_DestinationOut;
793 } else if (compositeOperator == QLatin1String("destination-over")) {
794 return QPainter::CompositionMode_DestinationOver;
795 } else if (compositeOperator == QLatin1String("lighter")) {
796 return QPainter::CompositionMode_Plus;
797 } else if (compositeOperator == QLatin1String("copy")) {
798 return QPainter::CompositionMode_Source;
799 } else if (compositeOperator == QLatin1String("xor")) {
800 return QPainter::CompositionMode_Xor;
801 } else if (compositeOperator == QLatin1String("qt-clear")) {
802 return QPainter::CompositionMode_Clear;
803 } else if (compositeOperator == QLatin1String("qt-destination")) {
804 return QPainter::CompositionMode_Destination;
805 } else if (compositeOperator == QLatin1String("qt-multiply")) {
806 return QPainter::CompositionMode_Multiply;
807 } else if (compositeOperator == QLatin1String("qt-screen")) {
808 return QPainter::CompositionMode_Screen;
809 } else if (compositeOperator == QLatin1String("qt-overlay")) {
810 return QPainter::CompositionMode_Overlay;
811 } else if (compositeOperator == QLatin1String("qt-darken")) {
812 return QPainter::CompositionMode_Darken;
813 } else if (compositeOperator == QLatin1String("qt-lighten")) {
814 return QPainter::CompositionMode_Lighten;
815 } else if (compositeOperator == QLatin1String("qt-color-dodge")) {
816 return QPainter::CompositionMode_ColorDodge;
817 } else if (compositeOperator == QLatin1String("qt-color-burn")) {
818 return QPainter::CompositionMode_ColorBurn;
819 } else if (compositeOperator == QLatin1String("qt-hard-light")) {
820 return QPainter::CompositionMode_HardLight;
821 } else if (compositeOperator == QLatin1String("qt-soft-light")) {
822 return QPainter::CompositionMode_SoftLight;
823 } else if (compositeOperator == QLatin1String("qt-difference")) {
824 return QPainter::CompositionMode_Difference;
825 } else if (compositeOperator == QLatin1String("qt-exclusion")) {
826 return QPainter::CompositionMode_Exclusion;
827 }
828 return QPainter::CompositionMode_SourceOver;
829}
830
831static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
832{
833 switch (op) {
834 case QPainter::CompositionMode_SourceOver:
835 return QStringLiteral("source-over");
836 case QPainter::CompositionMode_DestinationOver:
837 return QStringLiteral("destination-over");
838 case QPainter::CompositionMode_Clear:
839 return QStringLiteral("qt-clear");
840 case QPainter::CompositionMode_Source:
841 return QStringLiteral("copy");
842 case QPainter::CompositionMode_Destination:
843 return QStringLiteral("qt-destination");
844 case QPainter::CompositionMode_SourceIn:
845 return QStringLiteral("source-in");
846 case QPainter::CompositionMode_DestinationIn:
847 return QStringLiteral("destination-in");
848 case QPainter::CompositionMode_SourceOut:
849 return QStringLiteral("source-out");
850 case QPainter::CompositionMode_DestinationOut:
851 return QStringLiteral("destination-out");
852 case QPainter::CompositionMode_SourceAtop:
853 return QStringLiteral("source-atop");
854 case QPainter::CompositionMode_DestinationAtop:
855 return QStringLiteral("destination-atop");
856 case QPainter::CompositionMode_Xor:
857 return QStringLiteral("xor");
858 case QPainter::CompositionMode_Plus:
859 return QStringLiteral("lighter");
860 case QPainter::CompositionMode_Multiply:
861 return QStringLiteral("qt-multiply");
862 case QPainter::CompositionMode_Screen:
863 return QStringLiteral("qt-screen");
864 case QPainter::CompositionMode_Overlay:
865 return QStringLiteral("qt-overlay");
866 case QPainter::CompositionMode_Darken:
867 return QStringLiteral("qt-darken");
868 case QPainter::CompositionMode_Lighten:
869 return QStringLiteral("lighter");
870 case QPainter::CompositionMode_ColorDodge:
871 return QStringLiteral("qt-color-dodge");
872 case QPainter::CompositionMode_ColorBurn:
873 return QStringLiteral("qt-color-burn");
874 case QPainter::CompositionMode_HardLight:
875 return QStringLiteral("qt-hard-light");
876 case QPainter::CompositionMode_SoftLight:
877 return QStringLiteral("qt-soft-light");
878 case QPainter::CompositionMode_Difference:
879 return QStringLiteral("qt-difference");
880 case QPainter::CompositionMode_Exclusion:
881 return QStringLiteral("qt-exclusion");
882 default:
883 break;
884 }
885 return QString();
886}
887
888struct QQuickJSContext2DPixelData : public QV4::Object
889{
890 V4_OBJECT2(QQuickJSContext2DPixelData, QV4::Object)
891 V4_NEEDS_DESTROY
892
893 static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty);
894 static bool virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, Value *receiver);
895
896 static QV4::ReturnedValue proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
897};
898
899void QV4::Heap::QQuickJSContext2DPixelData::init()
900{
901 Object::init();
902 image = new QImage;
903 QV4::Scope scope(internalClass->engine);
904 QV4::ScopedObject o(scope, this);
905 o->setArrayType(QV4::Heap::ArrayData::Custom);
906}
907
908DEFINE_OBJECT_VTABLE(QQuickJSContext2DPixelData);
909
910struct QQuickJSContext2DImageData : public QV4::Object
911{
912 V4_OBJECT2(QQuickJSContext2DImageData, QV4::Object)
913
914 static QV4::ReturnedValue method_get_width(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
915 static QV4::ReturnedValue method_get_height(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
916 static QV4::ReturnedValue method_get_data(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
917
918};
919
920void QV4::Heap::QQuickJSContext2DImageData::init()
921{
922 Object::init();
923 pixelData = QV4::Value::undefinedValue();
924
925 QV4::Scope scope(internalClass->engine);
926 QV4::ScopedObject o(scope, this);
927
928 o->defineAccessorProperty(QStringLiteral("width"), getter: ::QQuickJSContext2DImageData::method_get_width, setter: nullptr);
929 o->defineAccessorProperty(QStringLiteral("height"), getter: ::QQuickJSContext2DImageData::method_get_height, setter: nullptr);
930 o->defineAccessorProperty(QStringLiteral("data"), getter: ::QQuickJSContext2DImageData::method_get_data, setter: nullptr);
931}
932
933DEFINE_OBJECT_VTABLE(QQuickJSContext2DImageData);
934
935static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionEngine *v4, const QImage& image)
936{
937 QV4::Scope scope(v4);
938 QQuickContext2DEngineData *ed = engineData(engine: scope.engine);
939 QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DPixelData>());
940 QV4::ScopedObject p(scope, ed->pixelArrayProto.value());
941 pixelData->setPrototypeOf(p);
942
943 if (image.isNull()) {
944 *pixelData->d()->image = QImage(qRound(d: w), qRound(d: h), QImage::Format_ARGB32);
945 pixelData->d()->image->fill(pixel: 0x00000000);
946 } else {
947 // After qtbase 88e56d0932a3615231adf40d5ae033e742d72c33, the image size can be off by one.
948 Q_ASSERT(qAbs(image.width() - qRound(w * image.devicePixelRatio())) <= 1 && qAbs(image.height() - qRound(h * image.devicePixelRatio())) <= 1);
949 *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(f: QImage::Format_ARGB32);
950 }
951
952 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DImageData>());
953 imageData->d()->pixelData = pixelData.asReturnedValue();
954 return imageData.asReturnedValue();
955}
956
957//static script functions
958
959/*!
960 \qmlproperty QtQuick::Canvas QtQuick::Context2D::canvas
961 Holds the canvas item that the context paints on.
962
963 This property is read only.
964*/
965QV4::ReturnedValue QQuickJSContext2DPrototype::method_get_canvas(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
966{
967 QV4::Scope scope(b);
968 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
969 CHECK_CONTEXT(r)
970
971 RETURN_RESULT(QV4::QObjectWrapper::wrap(scope.engine, r->d()->context()->canvas()));
972}
973
974/*!
975 \qmlmethod object QtQuick::Context2D::restore()
976 Pops the top state on the stack, restoring the context to that state.
977
978 \sa save()
979*/
980QV4::ReturnedValue QQuickJSContext2DPrototype::method_restore(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
981{
982 QV4::Scope scope(b);
983 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
984 CHECK_CONTEXT(r)
985
986 r->d()->context()->popState();
987 RETURN_RESULT(thisObject->asReturnedValue());
988}
989
990/*!
991 \qmlmethod object QtQuick::Context2D::reset()
992 Resets the context state and properties to the default values.
993*/
994QV4::ReturnedValue QQuickJSContext2DPrototype::method_reset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
995{
996 QV4::Scope scope(b);
997 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
998 CHECK_CONTEXT(r)
999
1000 r->d()->context()->reset();
1001
1002 RETURN_RESULT(thisObject->asReturnedValue());
1003}
1004
1005/*!
1006 \qmlmethod object QtQuick::Context2D::save()
1007 Pushes the current state onto the state stack.
1008
1009 Before changing any state attributes, you should save the current state
1010 for future reference. The context maintains a stack of drawing states.
1011 Each state consists of the current transformation matrix, clipping region,
1012 and values of the following attributes:
1013 \list
1014 \li strokeStyle
1015 \li fillStyle
1016 \li fillRule
1017 \li globalAlpha
1018 \li lineWidth
1019 \li lineCap
1020 \li lineJoin
1021 \li miterLimit
1022 \li shadowOffsetX
1023 \li shadowOffsetY
1024 \li shadowBlur
1025 \li shadowColor
1026 \li globalCompositeOperation
1027 \li \l font
1028 \li textAlign
1029 \li textBaseline
1030 \endlist
1031
1032 The current path is NOT part of the drawing state. The path can be reset by
1033 invoking the beginPath() method.
1034*/
1035QV4::ReturnedValue QQuickJSContext2DPrototype::method_save(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1036{
1037 QV4::Scope scope(b);
1038 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1039 CHECK_CONTEXT(r)
1040
1041 r->d()->context()->pushState();
1042
1043 RETURN_RESULT(*thisObject);
1044}
1045
1046// transformations
1047/*!
1048 \qmlmethod object QtQuick::Context2D::rotate(real angle)
1049 Rotate the canvas around the current origin by \a angle in radians and clockwise direction.
1050
1051 \code
1052 ctx.rotate(Math.PI/2);
1053 \endcode
1054
1055 \image qml-item-canvas-rotate.png
1056
1057 The rotation transformation matrix is as follows:
1058
1059 \image qml-item-canvas-math-rotate.png
1060
1061 where the \a angle of rotation is in radians.
1062
1063*/
1064QV4::ReturnedValue QQuickJSContext2DPrototype::method_rotate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1065{
1066 QV4::Scope scope(b);
1067 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1068 CHECK_CONTEXT(r)
1069
1070 if (argc >= 1)
1071 r->d()->context()->rotate(angle: argv[0].toNumber());
1072 RETURN_RESULT(*thisObject);
1073}
1074
1075/*!
1076 \qmlmethod object QtQuick::Context2D::scale(real x, real y)
1077
1078 Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors
1079 to the current tranform matrix.
1080 \a x is the scale factor in the horizontal direction and \a y is the scale factor in the
1081 vertical direction.
1082
1083 The following code doubles the horizontal size of an object drawn on the canvas and halves its
1084 vertical size:
1085
1086 \code
1087 ctx.scale(2.0, 0.5);
1088 \endcode
1089
1090 \image qml-item-canvas-scale.png
1091*/
1092QV4::ReturnedValue QQuickJSContext2DPrototype::method_scale(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1093{
1094 QV4::Scope scope(b);
1095 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1096 CHECK_CONTEXT(r)
1097
1098
1099 if (argc >= 2)
1100 r->d()->context()->scale(x: argv[0].toNumber(), y: argv[1].toNumber());
1101 RETURN_RESULT(*thisObject);
1102
1103}
1104
1105/*!
1106 \qmlmethod object QtQuick::Context2D::setTransform(real a, real b, real c, real d, real e, real f)
1107
1108 Changes the transformation matrix to the matrix given by the arguments as described below.
1109
1110 Modifying the transformation matrix directly enables you to perform scaling,
1111 rotating, and translating transformations in a single step.
1112
1113 Each point on the canvas is multiplied by the matrix before anything is
1114 drawn. The \l{http://www.w3.org/TR/2dcontext/#transformations}{HTML Canvas 2D Context specification}
1115 defines the transformation matrix as:
1116
1117 \image qml-item-canvas-math.png
1118 where:
1119 \list
1120 \li \a{a} is the scale factor in the horizontal (x) direction
1121 \image qml-item-canvas-scalex.png
1122 \li \a{c} is the skew factor in the x direction
1123 \image qml-item-canvas-skewx.png
1124 \li \a{e} is the translation in the x direction
1125 \image qml-item-canvas-translate.png
1126 \li \a{b} is the skew factor in the y (vertical) direction
1127 \image qml-item-canvas-skewy.png
1128 \li \a{d} is the scale factor in the y direction
1129 \image qml-item-canvas-scaley.png
1130 \li \a{f} is the translation in the y direction
1131 \image qml-item-canvas-translatey.png
1132 \li the last row remains constant
1133 \endlist
1134
1135 The scale factors and skew factors are multiples; \a{e} and \a{f} are
1136 coordinate space units, just like the units in the translate(x,y)
1137 method.
1138
1139 \sa transform()
1140*/
1141QV4::ReturnedValue QQuickJSContext2DPrototype::method_setTransform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1142{
1143 QV4::Scope scope(b);
1144 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1145 CHECK_CONTEXT(r)
1146
1147
1148 if (argc >= 6)
1149 r->d()->context()->setTransform( a: argv[0].toNumber()
1150 , b: argv[1].toNumber()
1151 , c: argv[2].toNumber()
1152 , d: argv[3].toNumber()
1153 , e: argv[4].toNumber()
1154 , f: argv[5].toNumber());
1155
1156 RETURN_RESULT(*thisObject);
1157
1158}
1159
1160/*!
1161 \qmlmethod object QtQuick::Context2D::transform(real a, real b, real c, real d, real e, real f)
1162
1163 This method is very similar to setTransform(), but instead of replacing
1164 the old transform matrix, this method applies the given tranform matrix
1165 to the current matrix by multiplying to it.
1166
1167 The setTransform(\a a, \a b, \a c, \a d, \a e, \a f) method actually
1168 resets the current transform to the identity matrix, and then invokes
1169 the transform(\a a, \a b, \a c, \a d, \a e, \a f) method with the same
1170 arguments.
1171
1172 \sa setTransform()
1173*/
1174QV4::ReturnedValue QQuickJSContext2DPrototype::method_transform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1175{
1176 QV4::Scope scope(b);
1177 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1178 CHECK_CONTEXT(r)
1179
1180 if (argc >= 6)
1181 r->d()->context()->transform( a: argv[0].toNumber()
1182 , b: argv[1].toNumber()
1183 , c: argv[2].toNumber()
1184 , d: argv[3].toNumber()
1185 , e: argv[4].toNumber()
1186 , f: argv[5].toNumber());
1187
1188 RETURN_RESULT(*thisObject);
1189
1190}
1191
1192/*!
1193 \qmlmethod object QtQuick::Context2D::translate(real x, real y)
1194
1195 Translates the origin of the canvas by a horizontal distance of \a x,
1196 and a vertical distance of \a y, in coordinate space units.
1197
1198 Translating the origin enables you to draw patterns of different objects on the canvas
1199 without having to measure the coordinates manually for each shape.
1200*/
1201QV4::ReturnedValue QQuickJSContext2DPrototype::method_translate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1202{
1203 QV4::Scope scope(b);
1204 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1205 CHECK_CONTEXT(r)
1206
1207 if (argc >= 2)
1208 r->d()->context()->translate(x: argv[0].toNumber(), y: argv[1].toNumber());
1209 RETURN_RESULT(*thisObject);
1210
1211}
1212
1213
1214/*!
1215 \qmlmethod object QtQuick::Context2D::resetTransform()
1216
1217 Reset the transformation matrix to the default value (equivalent to calling
1218 setTransform(\c 1, \c 0, \c 0, \c 1, \c 0, \c 0)).
1219
1220 \sa transform(), setTransform(), reset()
1221*/
1222QV4::ReturnedValue QQuickJSContext2DPrototype::method_resetTransform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1223{
1224 QV4::Scope scope(b);
1225 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1226 CHECK_CONTEXT(r)
1227
1228 r->d()->context()->setTransform(a: 1, b: 0, c: 0, d: 1, e: 0, f: 0);
1229
1230 RETURN_RESULT(*thisObject);
1231
1232}
1233
1234
1235/*!
1236 \qmlmethod object QtQuick::Context2D::shear(real sh, real sv)
1237
1238 Shears the transformation matrix by \a sh in the horizontal direction and
1239 \a sv in the vertical direction.
1240*/
1241QV4::ReturnedValue QQuickJSContext2DPrototype::method_shear(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1242{
1243 QV4::Scope scope(b);
1244 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1245 CHECK_CONTEXT(r)
1246
1247 if (argc >= 2)
1248 r->d()->context()->shear(h: argv[0].toNumber(), v: argv[1].toNumber());
1249
1250 RETURN_RESULT(*thisObject);
1251
1252}
1253// compositing
1254
1255/*!
1256 \qmlproperty real QtQuick::Context2D::globalAlpha
1257
1258 Holds the current alpha value applied to rendering operations.
1259 The value must be in the range from \c 0.0 (fully transparent) to \c 1.0 (fully opaque).
1260 The default value is \c 1.0.
1261*/
1262QV4::ReturnedValue QQuickJSContext2D::method_get_globalAlpha(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1263{
1264 QV4::Scope scope(b);
1265 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1266 CHECK_CONTEXT(r)
1267
1268 RETURN_RESULT(QV4::Encode(r->d()->context()->state.globalAlpha));
1269}
1270
1271QV4::ReturnedValue QQuickJSContext2D::method_set_globalAlpha(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1272{
1273 QV4::Scope scope(b);
1274 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1275 CHECK_CONTEXT_SETTER(r)
1276
1277 double globalAlpha = argc ? argv[0].toNumber() : qt_qnan();
1278
1279
1280 if (!qt_is_finite(d: globalAlpha))
1281 RETURN_UNDEFINED();
1282
1283 if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->d()->context()->state.globalAlpha != globalAlpha) {
1284 r->d()->context()->state.globalAlpha = globalAlpha;
1285 r->d()->context()->buffer()->setGlobalAlpha(r->d()->context()->state.globalAlpha);
1286 }
1287 RETURN_UNDEFINED();
1288}
1289
1290/*!
1291 \qmlproperty string QtQuick::Context2D::globalCompositeOperation
1292 Holds the current the current composition operation. Allowed operations are:
1293
1294 \value "source-atop"
1295 QPainter::CompositionMode_SourceAtop
1296 A atop B. Display the source image wherever both images are opaque.
1297 Display the destination image wherever the destination image is opaque
1298 but the source image is transparent. Display transparency elsewhere.
1299 \value "source-in"
1300 QPainter::CompositionMode_SourceIn
1301 A in B. Display the source image wherever both the source image and
1302 destination image are opaque. Display transparency elsewhere.
1303 \value "source-out"
1304 QPainter::CompositionMode_SourceOut
1305 A out B. Display the source image wherever the source image is opaque
1306 and the destination image is transparent. Display transparency elsewhere.
1307 \value "source-over"
1308 QPainter::CompositionMode_SourceOver (default)
1309 A over B. Display the source image wherever the source image is opaque.
1310 Display the destination image elsewhere.
1311 \value "destination-atop"
1312 QPainter::CompositionMode_DestinationAtop
1313 B atop A. Same as \c source-atop but using the destination image instead
1314 of the source image and vice versa.
1315 \value "destination-in"
1316 QPainter::CompositionMode_DestinationIn
1317 B in A. Same as \c source-in but using the destination image instead of
1318 the source image and vice versa.
1319 \value "destination-out"
1320 QPainter::CompositionMode_DestinationOut
1321 B out A. Same as \c source-out but using the destination image instead
1322 of the source image and vice versa.
1323 \value "destination-over"
1324 QPainter::CompositionMode_DestinationOver
1325 B over A. Same as \c source-over but using the destination image
1326 instead of the source image and vice versa.
1327 \value "lighter"
1328 QPainter::CompositionMode_Plus
1329 A plus B. Display the sum of the source image and destination image,
1330 with color values approaching \c 255 (100%) as a limit.
1331 \value "copy"
1332 QPainter::CompositionMode_Source
1333 A (B is ignored). Display the source image instead of the destination image.
1334 \value "xor"
1335 QPainter::CompositionMode_Xor
1336 A xor B. Exclusive OR of the source image and destination image.
1337 \value "qt-clear"
1338 QPainter::CompositionMode_Clear
1339 \value "qt-destination"
1340 QPainter::CompositionMode_Destination
1341 \value "qt-multiply"
1342 QPainter::CompositionMode_Multiply
1343 \value "qt-screen"
1344 QPainter::CompositionMode_Screen
1345 \value "qt-overlay"
1346 QPainter::CompositionMode_Overlay
1347 \value "qt-darken"
1348 QPainter::CompositionMode_Darken
1349 \value "qt-lighten"
1350 QPainter::CompositionMode_Lighten
1351 \value "qt-color-dodge"
1352 QPainter::CompositionMode_ColorDodge
1353 \value "qt-color-burn"
1354 QPainter::CompositionMode_ColorBurn
1355 \value "qt-hard-light"
1356 QPainter::CompositionMode_HardLight
1357 \value "qt-soft-light"
1358 QPainter::CompositionMode_SoftLight
1359 \value "qt-difference"
1360 QPainter::CompositionMode_Difference
1361 \value "qt-exclusion"
1362 QPainter::CompositionMode_Exclusion
1363
1364 In compliance with the W3C standard, the extended composition modes beyond
1365 the required modes are provided as "vendorName-operationName" syntax, for
1366 example: QPainter::CompositionMode_Exclusion is provided as "qt-exclusion".
1367*/
1368QV4::ReturnedValue QQuickJSContext2D::method_get_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1369{
1370 QV4::Scope scope(b);
1371 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1372 CHECK_CONTEXT(r)
1373
1374 RETURN_RESULT(scope.engine->newString(qt_composite_mode_to_string(r->d()->context()->state.globalCompositeOperation)));
1375}
1376
1377QV4::ReturnedValue QQuickJSContext2D::method_set_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1378{
1379 QV4::Scope scope(b);
1380 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1381 CHECK_CONTEXT_SETTER(r)
1382
1383 if (!argc)
1384 THROW_TYPE_ERROR();
1385
1386 QString mode = argv[0].toQString();
1387 QPainter::CompositionMode cm = qt_composite_mode_from_string(compositeOperator: mode);
1388 if (cm == QPainter::CompositionMode_SourceOver && mode != QLatin1String("source-over"))
1389 RETURN_UNDEFINED();
1390
1391 if (cm != r->d()->context()->state.globalCompositeOperation) {
1392 r->d()->context()->state.globalCompositeOperation = cm;
1393 r->d()->context()->buffer()->setGlobalCompositeOperation(cm);
1394 }
1395
1396 RETURN_UNDEFINED();
1397}
1398
1399// colors and styles
1400/*!
1401 \qmlproperty variant QtQuick::Context2D::fillStyle
1402 Holds the current style used for filling shapes.
1403 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored.
1404 This property accepts several color syntaxes:
1405 \list
1406 \li 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)'
1407 \li 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)'
1408 \li 'hsl(hue, saturation, lightness)'
1409 \li 'hsla(hue, saturation, lightness, alpha)'
1410 \li '#RRGGBB' - for example: '#00FFCC'
1411 \li Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0)
1412 \endlist
1413 If the \c fillStyle or \l strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the
1414 best performance, because it's already a valid QColor value, does not need to be parsed everytime.
1415
1416 The default value is '#000000'.
1417 \sa createLinearGradient()
1418 \sa createRadialGradient()
1419 \sa createPattern()
1420 \sa strokeStyle
1421 */
1422QV4::ReturnedValue QQuickJSContext2D::method_get_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1423{
1424 QV4::Scope scope(b);
1425 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1426 CHECK_CONTEXT(r)
1427
1428 const QColor color = r->d()->context()->state.fillStyle.color().toRgb();
1429 if (color.isValid()) {
1430 if (color.alpha() == 255)
1431 RETURN_RESULT(scope.engine->newString(color.name()));
1432 QString alphaString = QString::number(color.alphaF(), format: 'f');
1433 while (alphaString.endsWith(c: QLatin1Char('0')))
1434 alphaString.chop(n: 1);
1435 if (alphaString.endsWith(c: QLatin1Char('.')))
1436 alphaString += QLatin1Char('0');
1437 QString str = QString::fromLatin1(ba: "rgba(%1, %2, %3, %4)").arg(a: color.red()).arg(a: color.green()).arg(a: color.blue()).arg(a: alphaString);
1438 RETURN_RESULT(scope.engine->newString(str));
1439 }
1440 RETURN_RESULT(r->d()->context()->m_fillStyle.value());
1441}
1442
1443QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1444{
1445 QV4::Scope scope(b);
1446 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1447 CHECK_CONTEXT_SETTER(r)
1448
1449 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
1450
1451 if (value->as<Object>()) {
1452 QColor color = QV4::ExecutionEngine::toVariant(value, typeHint: QMetaType::fromType<QColor>()).value<QColor>();
1453 if (color.isValid()) {
1454 r->d()->context()->state.fillStyle = color;
1455 r->d()->context()->buffer()->setFillStyle(style: color);
1456 r->d()->context()->m_fillStyle.set(engine: scope.engine, value);
1457 } else {
1458 QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
1459 if (style && *style->d()->brush != r->d()->context()->state.fillStyle) {
1460 r->d()->context()->state.fillStyle = *style->d()->brush;
1461 r->d()->context()->buffer()->setFillStyle(style: *style->d()->brush, repeatX: style->d()->patternRepeatX, repeatY: style->d()->patternRepeatY);
1462 r->d()->context()->m_fillStyle.set(engine: scope.engine, value);
1463 r->d()->context()->state.fillPatternRepeatX = style->d()->patternRepeatX;
1464 r->d()->context()->state.fillPatternRepeatY = style->d()->patternRepeatY;
1465 }
1466 }
1467 } else if (value->isString()) {
1468 QColor color = qt_color_from_string(name: value);
1469 if (color.isValid() && r->d()->context()->state.fillStyle != QBrush(color)) {
1470 r->d()->context()->state.fillStyle = QBrush(color);
1471 r->d()->context()->buffer()->setFillStyle(style: r->d()->context()->state.fillStyle);
1472 r->d()->context()->m_fillStyle.set(engine: scope.engine, value);
1473 }
1474 }
1475 RETURN_UNDEFINED();
1476}
1477
1478/*!
1479 \qmlproperty enumeration QtQuick::Context2D::fillRule
1480 Holds the current fill rule used for filling shapes. The following fill rules are supported:
1481
1482 \value Qt.OddEvenFill Qt::OddEvenFill
1483 \value Qt.WindingFill (default) Qt::WindingFill
1484
1485 \note Unlike QPainterPath, the Canvas API uses the winding fill as the default fill rule.
1486 The fillRule property is part of the context rendering state.
1487
1488 \sa fillStyle
1489*/
1490QV4::ReturnedValue QQuickJSContext2D::method_get_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1491{
1492 QV4::Scope scope(b);
1493 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1494 CHECK_CONTEXT(r)
1495
1496 RETURN_RESULT(scope.engine->fromVariant(r->d()->context()->state.fillRule));
1497}
1498
1499QV4::ReturnedValue QQuickJSContext2D::method_set_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1500{
1501 QV4::Scope scope(b);
1502 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1503 CHECK_CONTEXT_SETTER(r)
1504
1505 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
1506
1507 if ((value->isString() && value->toQString() == QLatin1String("WindingFill"))
1508 || (value->isInt32() && value->integerValue() == Qt::WindingFill)) {
1509 r->d()->context()->state.fillRule = Qt::WindingFill;
1510 } else if ((value->isString() && value->toQStringNoThrow() == QLatin1String("OddEvenFill"))
1511 || (value->isInt32() && value->integerValue() == Qt::OddEvenFill)) {
1512 r->d()->context()->state.fillRule = Qt::OddEvenFill;
1513 } else {
1514 //error
1515 }
1516 r->d()->context()->m_path.setFillRule(r->d()->context()->state.fillRule);
1517 RETURN_UNDEFINED();
1518}
1519/*!
1520 \qmlproperty variant QtQuick::Context2D::strokeStyle
1521 Holds the current color or style to use for the lines around shapes,
1522 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object.
1523 Invalid values are ignored.
1524
1525 The default value is '#000000'.
1526
1527 \sa createLinearGradient()
1528 \sa createRadialGradient()
1529 \sa createPattern()
1530 \sa fillStyle
1531 */
1532QV4::ReturnedValue QQuickJSContext2D::method_get_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1533{
1534 QV4::Scope scope(b);
1535 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1536 CHECK_CONTEXT(r)
1537
1538 const QColor color = r->d()->context()->state.strokeStyle.color().toRgb();
1539 if (color.isValid()) {
1540 if (color.alpha() == 255)
1541 RETURN_RESULT(scope.engine->newString(color.name()));
1542 QString alphaString = QString::number(color.alphaF(), format: 'f');
1543 while (alphaString.endsWith(c: QLatin1Char('0')))
1544 alphaString.chop(n: 1);
1545 if (alphaString.endsWith(c: QLatin1Char('.')))
1546 alphaString += QLatin1Char('0');
1547 QString str = QString::fromLatin1(ba: "rgba(%1, %2, %3, %4)").arg(a: color.red()).arg(a: color.green()).arg(a: color.blue()).arg(a: alphaString);
1548 RETURN_RESULT(scope.engine->newString(str));
1549 }
1550 RETURN_RESULT(r->d()->context()->m_strokeStyle.value());
1551}
1552
1553QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1554{
1555 QV4::Scope scope(b);
1556 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1557 CHECK_CONTEXT_SETTER(r)
1558
1559 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
1560
1561 if (value->as<Object>()) {
1562 QColor color = QV4::ExecutionEngine::toVariant(value, typeHint: QMetaType::fromType<QColor>()).value<QColor>();
1563 if (color.isValid()) {
1564 r->d()->context()->state.strokeStyle = color;
1565 r->d()->context()->buffer()->setStrokeStyle(style: color);
1566 r->d()->context()->m_strokeStyle.set(engine: scope.engine, value);
1567 } else {
1568 QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
1569 if (style && *style->d()->brush != r->d()->context()->state.strokeStyle) {
1570 r->d()->context()->state.strokeStyle = *style->d()->brush;
1571 r->d()->context()->buffer()->setStrokeStyle(style: *style->d()->brush, repeatX: style->d()->patternRepeatX, repeatY: style->d()->patternRepeatY);
1572 r->d()->context()->m_strokeStyle.set(engine: scope.engine, value);
1573 r->d()->context()->state.strokePatternRepeatX = style->d()->patternRepeatX;
1574 r->d()->context()->state.strokePatternRepeatY = style->d()->patternRepeatY;
1575 } else if (!style && r->d()->context()->state.strokeStyle != QBrush(QColor())) {
1576 // If there is no style object, then ensure that the strokeStyle is at least
1577 // QColor in case it was previously set
1578 r->d()->context()->state.strokeStyle = QBrush(QColor());
1579 r->d()->context()->buffer()->setStrokeStyle(style: r->d()->context()->state.strokeStyle);
1580 r->d()->context()->m_strokeStyle.set(engine: scope.engine, value);
1581 }
1582 }
1583 } else if (value->isString()) {
1584 QColor color = qt_color_from_string(name: value);
1585 if (color.isValid() && r->d()->context()->state.strokeStyle != QBrush(color)) {
1586 r->d()->context()->state.strokeStyle = QBrush(color);
1587 r->d()->context()->buffer()->setStrokeStyle(style: r->d()->context()->state.strokeStyle);
1588 r->d()->context()->m_strokeStyle.set(engine: scope.engine, value);
1589 }
1590 }
1591 RETURN_UNDEFINED();
1592}
1593
1594/*!
1595 \qmlmethod object QtQuick::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
1596 Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between
1597 the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
1598
1599 A gradient is a smooth transition between colors. There are two types of gradients: linear and radial.
1600 Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between
1601 to the gradient's starting and end points or circles.
1602
1603 \sa CanvasGradient::addColorStop()
1604 \sa createRadialGradient()
1605 \sa createConicalGradient()
1606 \sa createPattern()
1607 \sa fillStyle
1608 \sa strokeStyle
1609 */
1610
1611QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1612{
1613 QV4::Scope scope(b);
1614 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1615 CHECK_CONTEXT(r)
1616
1617 if (argc >= 4) {
1618 qreal x0 = argv[0].toNumber();
1619 qreal y0 = argv[1].toNumber();
1620 qreal x1 = argv[2].toNumber();
1621 qreal y1 = argv[3].toNumber();
1622
1623 if (!qt_is_finite(d: x0)
1624 || !qt_is_finite(d: y0)
1625 || !qt_is_finite(d: x1)
1626 || !qt_is_finite(d: y1)) {
1627 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
1628 }
1629 QQuickContext2DEngineData *ed = engineData(engine: scope.engine);
1630
1631 QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1632 QV4::ScopedObject p(scope, ed->gradientProto.value());
1633 gradient->setPrototypeOf(p);
1634 *gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
1635 RETURN_RESULT(*gradient);
1636 }
1637
1638 RETURN_RESULT(*thisObject);
1639
1640}
1641
1642/*!
1643 \qmlmethod object QtQuick::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
1644
1645 Returns a CanvasGradient object that represents a radial gradient that
1646 paints along the cone given by the start circle with origin (\a x0, \a y0)
1647 and radius \a r0, and the end circle with origin (\a x1, \a y1) and radius
1648 \a r1.
1649
1650 \sa CanvasGradient::addColorStop()
1651 \sa createLinearGradient()
1652 \sa createConicalGradient()
1653 \sa createPattern()
1654 \sa fillStyle
1655 \sa strokeStyle
1656 */
1657
1658QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1659{
1660 QV4::Scope scope(b);
1661 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1662 CHECK_CONTEXT(r)
1663
1664 if (argc >= 6) {
1665 qreal x0 = argv[0].toNumber();
1666 qreal y0 = argv[1].toNumber();
1667 qreal r0 = argv[2].toNumber();
1668 qreal x1 = argv[3].toNumber();
1669 qreal y1 = argv[4].toNumber();
1670 qreal r1 = argv[5].toNumber();
1671
1672 if (!qt_is_finite(d: x0)
1673 || !qt_is_finite(d: y0)
1674 || !qt_is_finite(d: x1)
1675 || !qt_is_finite(d: r0)
1676 || !qt_is_finite(d: r1)
1677 || !qt_is_finite(d: y1)) {
1678 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
1679 }
1680
1681 if (r0 < 0 || r1 < 0)
1682 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
1683
1684 QQuickContext2DEngineData *ed = engineData(engine: scope.engine);
1685
1686 QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1687 QV4::ScopedObject p(scope, ed->gradientProto.value());
1688 gradient->setPrototypeOf(p);
1689 *gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r1, QPointF(x0, y0), r0);
1690 RETURN_RESULT(*gradient);
1691 }
1692
1693 RETURN_RESULT(*thisObject);
1694
1695}
1696
1697/*!
1698 \qmlmethod object QtQuick::Context2D::createConicalGradient(real x, real y, real angle)
1699
1700 Returns a CanvasGradient object that represents a conical gradient that
1701 interpolates colors counter-clockwise around a center point (\a x, \a y)
1702 with a start angle \a angle in units of radians.
1703
1704 \sa CanvasGradient::addColorStop()
1705 \sa createLinearGradient()
1706 \sa createRadialGradient()
1707 \sa createPattern()
1708 \sa fillStyle
1709 \sa strokeStyle
1710 */
1711
1712QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1713{
1714 QV4::Scope scope(b);
1715 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1716 CHECK_CONTEXT(r)
1717
1718 if (argc >= 3) {
1719 qreal x = argv[0].toNumber();
1720 qreal y = argv[1].toNumber();
1721 qreal angle = qRadiansToDegrees(radians: argv[2].toNumber());
1722 if (!qt_is_finite(d: x) || !qt_is_finite(d: y)) {
1723 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
1724 }
1725
1726 if (!qt_is_finite(d: angle)) {
1727 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
1728 }
1729
1730 QQuickContext2DEngineData *ed = engineData(engine: scope.engine);
1731
1732 QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1733 QV4::ScopedObject p(scope, ed->gradientProto.value());
1734 gradient->setPrototypeOf(p);
1735 *gradient->d()->brush = QConicalGradient(x, y, angle);
1736 RETURN_RESULT(*gradient);
1737 }
1738
1739 RETURN_RESULT(*thisObject);
1740
1741}
1742/*!
1743 \qmlmethod variant QtQuick::Context2D::createPattern(color color, enumeration patternMode)
1744 This is an overloaded function.
1745 Returns a CanvasPattern object that uses the given \a color and \a patternMode.
1746 The valid pattern modes are:
1747
1748 \value Qt.SolidPattern Qt::SolidPattern
1749 \value Qt.Dense1Pattern Qt::Dense1Pattern
1750 \value Qt.Dense2Pattern Qt::Dense2Pattern
1751 \value Qt.Dense3Pattern Qt::Dense3Pattern
1752 \value Qt.Dense4Pattern Qt::Dense4Pattern
1753 \value Qt.Dense5Pattern Qt::Dense5Pattern
1754 \value Qt.Dense6Pattern Qt::Dense6Pattern
1755 \value Qt.Dense7Pattern Qt::Dense7Pattern
1756 \value Qt.HorPattern Qt::HorPattern
1757 \value Qt.VerPattern Qt::VerPattern
1758 \value Qt.CrossPattern Qt::CrossPattern
1759 \value Qt.BDiagPattern Qt::BDiagPattern
1760 \value Qt.FDiagPattern Qt::FDiagPattern
1761 \value Qt.DiagCrossPattern Qt::DiagCrossPattern
1762
1763 \sa Qt::BrushStyle
1764*/
1765/*!
1766 \qmlmethod variant QtQuick::Context2D::createPattern(Image image, string repetition)
1767 Returns a CanvasPattern object that uses the given image and repeats in the
1768 direction(s) given by the repetition argument.
1769
1770 The \a image parameter must be a valid Image item, a valid CanvasImageData
1771 object or loaded image url. If there is no image data, thus function throws an
1772 INVALID_STATE_ERR exception.
1773
1774 The allowed values for \a repetition are:
1775
1776 \value "repeat" both directions
1777 \value "repeat-x horizontal only
1778 \value "repeat-y" vertical only
1779 \value "no-repeat" neither
1780
1781 If the repetition argument is empty or null, the value "repeat" is used.
1782
1783 \sa strokeStyle
1784 \sa fillStyle
1785*/
1786QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1787{
1788 QV4::Scope scope(b);
1789 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1790 CHECK_CONTEXT(r)
1791
1792 if (argc >= 2) {
1793 QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1794
1795 QColor color = QV4::ExecutionEngine::toVariant(
1796 value: argv[0], typeHint: QMetaType::fromType<QColor>()).value<QColor>();
1797 if (color.isValid()) {
1798 int patternMode = argv[1].toInt32();
1799 Qt::BrushStyle style = Qt::SolidPattern;
1800 if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
1801 style = static_cast<Qt::BrushStyle>(patternMode);
1802 }
1803 *pattern->d()->brush = QBrush(color, style);
1804 } else {
1805 QImage patternTexture;
1806
1807 if (const QV4::Object *o = argv[0].as<Object>()) {
1808 QV4::ScopedString s(scope, scope.engine->newString(QStringLiteral("data")));
1809 QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, o->get(name: s));
1810 if (!!pixelData) {
1811 patternTexture = *pixelData->d()->image;
1812 }
1813 } else {
1814 patternTexture = r->d()->context()->createPixmap(url: QUrl(argv[0].toQStringNoThrow()))->image();
1815 }
1816
1817 if (!patternTexture.isNull()) {
1818 pattern->d()->brush->setTextureImage(patternTexture);
1819
1820 QString repetition = argv[1].toQStringNoThrow();
1821 if (repetition == QLatin1String("repeat") || repetition.isEmpty()) {
1822 pattern->d()->patternRepeatX = true;
1823 pattern->d()->patternRepeatY = true;
1824 } else if (repetition == QLatin1String("repeat-x")) {
1825 pattern->d()->patternRepeatX = true;
1826 pattern->d()->patternRepeatY = false;
1827 } else if (repetition == QLatin1String("repeat-y")) {
1828 pattern->d()->patternRepeatX = false;
1829 pattern->d()->patternRepeatY = true;
1830 } else if (repetition == QLatin1String("no-repeat")) {
1831 pattern->d()->patternRepeatX = false;
1832 pattern->d()->patternRepeatY = false;
1833 } else {
1834 //TODO: exception: SYNTAX_ERR
1835 }
1836
1837 }
1838 }
1839
1840 RETURN_RESULT(*pattern);
1841
1842 }
1843 RETURN_UNDEFINED();
1844}
1845
1846// line styles
1847/*!
1848 \qmlproperty string QtQuick::Context2D::lineCap
1849 Holds the current line cap style.
1850 The possible line cap styles are:
1851
1852 \value "butt"
1853 (default) Qt::FlatCap the end of each line has a flat edge
1854 perpendicular to the direction of the line.
1855 \value "round"
1856 Qt::RoundCap a semi-circle with the diameter equal to the width of the
1857 line is added on to the end of the line.
1858 \value "square"
1859 Qt::SquareCap a rectangle with the length of the line width and the
1860 width of half the line width, placed flat against the edge
1861 perpendicular to the direction of the line.
1862
1863 Other values are ignored.
1864*/
1865QV4::ReturnedValue QQuickJSContext2D::method_get_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1866{
1867 QV4::Scope scope(b);
1868 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1869 CHECK_CONTEXT(r)
1870
1871 switch (r->d()->context()->state.lineCap) {
1872 case Qt::RoundCap:
1873 RETURN_RESULT(scope.engine->newString(QStringLiteral("round")));
1874 case Qt::SquareCap:
1875 RETURN_RESULT(scope.engine->newString(QStringLiteral("square")));
1876 case Qt::FlatCap:
1877 default:
1878 break;
1879 }
1880 RETURN_RESULT(scope.engine->newString(QStringLiteral("butt")));
1881}
1882
1883QV4::ReturnedValue QQuickJSContext2D::method_set_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1884{
1885 if (!argc)
1886 return QV4::Encode::undefined();
1887
1888 QV4::Scope scope(b);
1889 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1890 CHECK_CONTEXT_SETTER(r)
1891
1892 QString lineCap = argv[0].toQString();
1893 Qt::PenCapStyle cap;
1894 if (lineCap == QLatin1String("round"))
1895 cap = Qt::RoundCap;
1896 else if (lineCap == QLatin1String("butt"))
1897 cap = Qt::FlatCap;
1898 else if (lineCap == QLatin1String("square"))
1899 cap = Qt::SquareCap;
1900 else
1901 RETURN_UNDEFINED();
1902
1903 if (cap != r->d()->context()->state.lineCap) {
1904 r->d()->context()->state.lineCap = cap;
1905 r->d()->context()->buffer()->setLineCap(cap);
1906 }
1907 RETURN_UNDEFINED();
1908}
1909
1910/*!
1911 \qmlproperty string QtQuick::Context2D::lineJoin
1912 Holds the current line join style. A join exists at any point in a subpath
1913 shared by two consecutive lines. When a subpath is closed, then a join also
1914 exists at its first point (equivalent to its last point) connecting the
1915 first and last lines in the subpath.
1916
1917 The possible line join styles are:
1918
1919 \value "bevel" Qt::BevelJoin The triangular notch between the two lines is filled.
1920 \value "round" Qt::RoundJoin A circular arc between the two lines is filled.
1921 \value "miter" (default) Qt::MiterJoin The outer edges of the lines are extended to
1922 meet at an angle, and this area is filled.
1923
1924 Other values are ignored.
1925*/
1926QV4::ReturnedValue QQuickJSContext2D::method_get_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1927{
1928 QV4::Scope scope(b);
1929 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1930 CHECK_CONTEXT(r)
1931
1932 switch (r->d()->context()->state.lineJoin) {
1933 case Qt::RoundJoin:
1934 RETURN_RESULT(scope.engine->newString(QStringLiteral("round")));
1935 case Qt::BevelJoin:
1936 RETURN_RESULT(scope.engine->newString(QStringLiteral("bevel")));
1937 case Qt::MiterJoin:
1938 default:
1939 break;
1940 }
1941 RETURN_RESULT(scope.engine->newString(QStringLiteral("miter")));
1942}
1943
1944QV4::ReturnedValue QQuickJSContext2D::method_set_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1945{
1946 QV4::Scope scope(b);
1947 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1948 CHECK_CONTEXT_SETTER(r)
1949
1950 if (!argc)
1951 THROW_TYPE_ERROR();
1952
1953 QString lineJoin = argv[0].toQString();
1954 Qt::PenJoinStyle join;
1955 if (lineJoin == QLatin1String("round"))
1956 join = Qt::RoundJoin;
1957 else if (lineJoin == QLatin1String("bevel"))
1958 join = Qt::BevelJoin;
1959 else if (lineJoin == QLatin1String("miter"))
1960 join = Qt::SvgMiterJoin;
1961 else
1962 RETURN_UNDEFINED();
1963
1964 if (join != r->d()->context()->state.lineJoin) {
1965 r->d()->context()->state.lineJoin = join;
1966 r->d()->context()->buffer()->setLineJoin(join);
1967 }
1968 RETURN_UNDEFINED();
1969}
1970
1971/*!
1972 \qmlproperty real QtQuick::Context2D::lineWidth
1973 Holds the current line width. Values that are not finite values greater than zero are ignored.
1974 */
1975QV4::ReturnedValue QQuickJSContext2D::method_get_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1976{
1977 QV4::Scope scope(b);
1978 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1979 CHECK_CONTEXT(r)
1980
1981 RETURN_RESULT(QV4::Encode(r->d()->context()->state.lineWidth));
1982}
1983
1984QV4::ReturnedValue QQuickJSContext2D::method_set_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1985{
1986 QV4::Scope scope(b);
1987 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1988 CHECK_CONTEXT_SETTER(r)
1989
1990 qreal w = argc ? argv[0].toNumber() : -1;
1991
1992 if (w > 0 && qt_is_finite(d: w) && w != r->d()->context()->state.lineWidth) {
1993 r->d()->context()->state.lineWidth = w;
1994 r->d()->context()->buffer()->setLineWidth(w);
1995 }
1996 RETURN_UNDEFINED();
1997}
1998
1999/*!
2000 \qmlproperty real QtQuick::Context2D::miterLimit
2001 Holds the current miter limit ratio.
2002 The default miter limit value is 10.0.
2003 */
2004QV4::ReturnedValue QQuickJSContext2D::method_get_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2005{
2006 QV4::Scope scope(b);
2007 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2008 CHECK_CONTEXT(r)
2009
2010 RETURN_RESULT(QV4::Encode(r->d()->context()->state.miterLimit));
2011}
2012
2013QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2014{
2015 QV4::Scope scope(b);
2016 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2017 CHECK_CONTEXT_SETTER(r)
2018
2019 qreal ml = argc ? argv[0].toNumber() : -1;
2020
2021 if (ml > 0 && qt_is_finite(d: ml) && ml != r->d()->context()->state.miterLimit) {
2022 r->d()->context()->state.miterLimit = ml;
2023 r->d()->context()->buffer()->setMiterLimit(ml);
2024 }
2025 RETURN_UNDEFINED();
2026}
2027
2028/*!
2029 \qmlmethod array QtQuick::Context2D::getLineDash()
2030 \since QtQuick 2.11
2031 Returns an array of qreals representing the dash pattern of the line.
2032
2033 \sa setLineDash(), lineDashOffset
2034 */
2035QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2036{
2037 QV4::Scope scope(b);
2038 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2039 CHECK_CONTEXT(r)
2040
2041 const QVector<qreal> pattern = r->d()->context()->state.lineDash;
2042 QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject(count: pattern.size()));
2043 array->arrayReserve(n: pattern.size());
2044 for (int i = 0; i < pattern.size(); i++)
2045 array->put(idx: i, v: QV4::Value::fromDouble(d: pattern[i]));
2046
2047 array->setArrayLengthUnchecked(pattern.size());
2048
2049 RETURN_RESULT(*array);
2050}
2051
2052/*!
2053 \qmlmethod QtQuick::Context2D::setLineDash(array pattern)
2054 \since QtQuick 2.11
2055 Sets the dash pattern to the given pattern.
2056
2057 \a pattern a list of numbers that specifies distances to alternately draw a line and a gap.
2058
2059 If the number of elements in the array is odd, the elements of the array get copied
2060 and concatenated. For example, [5, 15, 25] will become [5, 15, 25, 5, 15, 25].
2061
2062 \table 100%
2063 \row
2064 \li \inlineimage qml-item-canvas-lineDash.png
2065 \li
2066 \code
2067 var space = 4
2068 ctx.setLineDash([1, space, 3, space, 9, space, 27, space, 9, space])
2069 ...
2070 ctx.stroke();
2071 \endcode
2072 \endtable
2073
2074 \sa getLineDash(), lineDashOffset
2075 */
2076QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2077{
2078 QV4::Scope scope(b);
2079 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2080 CHECK_CONTEXT_SETTER(r)
2081
2082 if (!argc)
2083 RETURN_UNDEFINED();
2084
2085 QV4::ScopedArrayObject array(scope, argv[0]);
2086 if (!array)
2087 RETURN_UNDEFINED();
2088
2089 QV4::ScopedValue v(scope);
2090 const uint arrayLength = array->getLength();
2091 QVector<qreal> dashes;
2092 dashes.reserve(size: arrayLength);
2093 for (uint i = 0; i < arrayLength; ++i) {
2094 v = array->get(idx: i);
2095 const double number = v->toNumber();
2096
2097 if (!qt_is_finite(d: number) || (number < 0))
2098 RETURN_UNDEFINED();
2099
2100 dashes.append(t: v->toNumber());
2101 }
2102 if (dashes.size() % 2 != 0) {
2103 dashes += dashes;
2104 }
2105
2106 r->d()->context()->state.lineDash = dashes;
2107 r->d()->context()->buffer()->setLineDash(dashes);
2108
2109 RETURN_UNDEFINED();
2110}
2111
2112/*!
2113 \qmlproperty real QtQuick::Context2D::lineDashOffset
2114 \since QtQuick 2.11
2115
2116 Holds the current line dash offset.
2117 The default line dash offset value is \c 0.
2118
2119 \sa getLineDash(), setLineDash()
2120 */
2121QV4::ReturnedValue QQuickJSContext2D::method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2122{
2123 QV4::Scope scope(b);
2124 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2125 CHECK_CONTEXT(r)
2126
2127 RETURN_RESULT(QV4::Encode(r->d()->context()->state.lineDashOffset));
2128}
2129
2130QV4::ReturnedValue QQuickJSContext2D::method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2131{
2132 QV4::Scope scope(b);
2133 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2134 CHECK_CONTEXT_SETTER(r)
2135
2136 const qreal offset = argc ? argv[0].toNumber() : -1;
2137
2138 if (qt_is_finite(d: offset) && offset != r->d()->context()->state.lineDashOffset) {
2139 r->d()->context()->state.lineDashOffset = offset;
2140 r->d()->context()->buffer()->setLineDashOffset(offset);
2141 }
2142 RETURN_UNDEFINED();
2143}
2144
2145
2146// shadows
2147/*!
2148 \qmlproperty real QtQuick::Context2D::shadowBlur
2149 Holds the current level of blur applied to shadows
2150 */
2151QV4::ReturnedValue QQuickJSContext2D::method_get_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2152{
2153 QV4::Scope scope(b);
2154 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2155 CHECK_CONTEXT(r)
2156
2157 RETURN_RESULT(QV4::Encode(r->d()->context()->state.shadowBlur));
2158}
2159
2160QV4::ReturnedValue QQuickJSContext2D::method_set_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2161{
2162 QV4::Scope scope(b);
2163 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2164 CHECK_CONTEXT_SETTER(r)
2165
2166 qreal blur = argc ? argv[0].toNumber() : -1;
2167
2168 if (blur > 0 && qt_is_finite(d: blur) && blur != r->d()->context()->state.shadowBlur) {
2169 r->d()->context()->state.shadowBlur = blur;
2170 r->d()->context()->buffer()->setShadowBlur(blur);
2171 }
2172 RETURN_UNDEFINED();
2173}
2174
2175/*!
2176 \qmlproperty string QtQuick::Context2D::shadowColor
2177 Holds the current shadow color.
2178 */
2179QV4::ReturnedValue QQuickJSContext2D::method_get_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2180{
2181 QV4::Scope scope(b);
2182 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2183 CHECK_CONTEXT(r)
2184
2185 RETURN_RESULT(scope.engine->newString(r->d()->context()->state.shadowColor.name()));
2186}
2187
2188QV4::ReturnedValue QQuickJSContext2D::method_set_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2189{
2190 QV4::Scope scope(b);
2191 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2192 CHECK_CONTEXT_SETTER(r)
2193
2194 QColor color;
2195 if (argc)
2196 color = qt_color_from_string(name: argv[0]);
2197
2198 if (color.isValid() && color != r->d()->context()->state.shadowColor) {
2199 r->d()->context()->state.shadowColor = color;
2200 r->d()->context()->buffer()->setShadowColor(color);
2201 }
2202 RETURN_UNDEFINED();
2203}
2204
2205
2206/*!
2207 \qmlproperty qreal QtQuick::Context2D::shadowOffsetX
2208 Holds the current shadow offset in the positive horizontal distance.
2209
2210 \sa shadowOffsetY
2211 */
2212QV4::ReturnedValue QQuickJSContext2D::method_get_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2213{
2214 QV4::Scope scope(b);
2215 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2216 CHECK_CONTEXT(r)
2217
2218 RETURN_RESULT(QV4::Encode(r->d()->context()->state.shadowOffsetX));
2219}
2220
2221QV4::ReturnedValue QQuickJSContext2D::method_set_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2222{
2223 QV4::Scope scope(b);
2224 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2225 CHECK_CONTEXT_SETTER(r)
2226
2227 qreal offsetX = argc ? argv[0].toNumber() : qt_qnan();
2228 if (qt_is_finite(d: offsetX) && offsetX != r->d()->context()->state.shadowOffsetX) {
2229 r->d()->context()->state.shadowOffsetX = offsetX;
2230 r->d()->context()->buffer()->setShadowOffsetX(offsetX);
2231 }
2232 RETURN_UNDEFINED();
2233}
2234/*!
2235 \qmlproperty qreal QtQuick::Context2D::shadowOffsetY
2236 Holds the current shadow offset in the positive vertical distance.
2237
2238 \sa shadowOffsetX
2239 */
2240QV4::ReturnedValue QQuickJSContext2D::method_get_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2241{
2242 QV4::Scope scope(b);
2243 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2244 CHECK_CONTEXT(r)
2245
2246 RETURN_RESULT(QV4::Encode(r->d()->context()->state.shadowOffsetY));
2247}
2248
2249QV4::ReturnedValue QQuickJSContext2D::method_set_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2250{
2251 QV4::Scope scope(b);
2252 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2253 CHECK_CONTEXT_SETTER(r)
2254
2255 qreal offsetY = argc ? argv[0].toNumber() : qt_qnan();
2256 if (qt_is_finite(d: offsetY) && offsetY != r->d()->context()->state.shadowOffsetY) {
2257 r->d()->context()->state.shadowOffsetY = offsetY;
2258 r->d()->context()->buffer()->setShadowOffsetY(offsetY);
2259 }
2260 RETURN_UNDEFINED();
2261}
2262
2263#if QT_CONFIG(quick_path)
2264QV4::ReturnedValue QQuickJSContext2D::method_get_path(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2265{
2266 QV4::Scope scope(b);
2267 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2268 CHECK_CONTEXT(r)
2269
2270 RETURN_RESULT(r->d()->context()->m_v4path.value());
2271}
2272
2273QV4::ReturnedValue QQuickJSContext2D::method_set_path(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2274{
2275 QV4::Scope scope(b);
2276 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2277 CHECK_CONTEXT_SETTER(r)
2278
2279 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
2280 r->d()->context()->beginPath();
2281 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, value);
2282 if (!!qobjectWrapper) {
2283 if (QQuickPath *path = qobject_cast<QQuickPath*>(object: qobjectWrapper->object()))
2284 r->d()->context()->m_path = path->path();
2285 } else {
2286 QString path =value->toQStringNoThrow();
2287 QQuickSvgParser::parsePathDataFast(dataStr: path, path&: r->d()->context()->m_path);
2288 }
2289 r->d()->context()->m_v4path.set(engine: scope.engine, value);
2290 RETURN_UNDEFINED();
2291}
2292#endif // QT_CONFIG(quick_path)
2293
2294//rects
2295/*!
2296 \qmlmethod object QtQuick::Context2D::clearRect(real x, real y, real w, real h)
2297
2298 Clears all pixels on the canvas in the rectangle specified by
2299 (\a x, \a y, \a w, \a h) to transparent black.
2300 */
2301QV4::ReturnedValue QQuickJSContext2DPrototype::method_clearRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2302{
2303 QV4::Scope scope(b);
2304 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2305 CHECK_CONTEXT(r)
2306
2307
2308 if (argc >= 4)
2309 r->d()->context()->clearRect(x: argv[0].toNumber(),
2310 y: argv[1].toNumber(),
2311 w: argv[2].toNumber(),
2312 h: argv[3].toNumber());
2313
2314 RETURN_RESULT(*thisObject);
2315
2316}
2317/*!
2318 \qmlmethod object QtQuick::Context2D::fillRect(real x, real y, real w, real h)
2319
2320 Paints a rectangular area specified by (\a x, \a y, \a w, \a h) using fillStyle.
2321
2322 \sa fillStyle
2323 */
2324QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2325{
2326 QV4::Scope scope(b);
2327 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2328 CHECK_CONTEXT(r)
2329
2330 if (argc >= 4)
2331 r->d()->context()->fillRect(x: argv[0].toNumber(), y: argv[1].toNumber(), w: argv[2].toNumber(), h: argv[3].toNumber());
2332 RETURN_RESULT(*thisObject);
2333
2334}
2335
2336/*!
2337 \qmlmethod object QtQuick::Context2D::strokeRect(real x, real y, real w, real h)
2338
2339 Strokes the path of the rectangle specified by (\a x, \a y, \a w, \a h) using
2340 strokeStyle, lineWidth, lineJoin, and (if appropriate) miterLimit attributes.
2341
2342 \sa strokeStyle, lineWidth, lineJoin, miterLimit
2343 */
2344QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2345{
2346 QV4::Scope scope(b);
2347 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2348 CHECK_CONTEXT(r)
2349
2350 if (argc >= 4)
2351 r->d()->context()->strokeRect(x: argv[0].toNumber(), y: argv[1].toNumber(), w: argv[2].toNumber(), h: argv[3].toNumber());
2352
2353 RETURN_RESULT(*thisObject);
2354
2355}
2356
2357// Complex shapes (paths) API
2358/*!
2359 \qmlmethod object QtQuick::Context2D::arc(real x, real y, real radius,
2360 real startAngle, real endAngle, bool anticlockwise)
2361
2362 Adds an arc to the current subpath that lies on the circumference of the
2363 circle whose center is at the point (\a x, \a y) and whose radius is
2364 \a radius.
2365
2366 Both \a startAngle and \a endAngle are measured from the x-axis in radians.
2367
2368 \image qml-item-canvas-arc.png
2369
2370 \image qml-item-canvas-startAngle.png
2371
2372 The \a anticlockwise parameter is \c false for each arc in the figure above
2373 because they are all drawn in the clockwise direction.
2374
2375 \sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
2376 Context Standard for arc()}
2377*/
2378QV4::ReturnedValue QQuickJSContext2DPrototype::method_arc(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2379{
2380 QV4::Scope scope(b);
2381 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2382 CHECK_CONTEXT(r)
2383
2384 if (argc >= 5) {
2385 bool antiClockwise = false;
2386
2387 if (argc == 6)
2388 antiClockwise = argv[5].toBoolean();
2389
2390 qreal radius = argv[2].toNumber();
2391
2392 if (qt_is_finite(d: radius) && radius < 0)
2393 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
2394
2395 r->d()->context()->arc(x: argv[0].toNumber(),
2396 y: argv[1].toNumber(),
2397 radius,
2398 startAngle: argv[3].toNumber(),
2399 endAngle: argv[4].toNumber(),
2400 anticlockwise: antiClockwise);
2401 }
2402
2403 RETURN_RESULT(*thisObject);
2404
2405}
2406
2407/*!
2408 \qmlmethod object QtQuick::Context2D::arcTo(real x1, real y1, real x2,
2409 real y2, real radius)
2410
2411 Adds an arc with the given control points and radius to the current subpath,
2412 connected to the previous point by a straight line. To draw an arc, you
2413 begin with the same steps you followed to create a line:
2414
2415 \list
2416 \li Call the beginPath() method to set a new path.
2417 \li Call the moveTo(\c x, \c y) method to set your starting position on the
2418 canvas at the point (\c x, \c y).
2419 \li To draw an arc or circle, call the arcTo(\a x1, \a y1, \a x2, \a y2,
2420 \a radius) method. This adds an arc with starting point (\a x1, \a y1),
2421 ending point (\a x2, \a y2), and \a radius to the current subpath and
2422 connects it to the previous subpath by a straight line.
2423 \endlist
2424
2425 \image qml-item-canvas-arcTo.png
2426
2427 \sa arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C's 2D
2428 Context Standard for arcTo()}
2429*/
2430QV4::ReturnedValue QQuickJSContext2DPrototype::method_arcTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2431{
2432 QV4::Scope scope(b);
2433 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2434 CHECK_CONTEXT(r)
2435
2436 if (argc >= 5) {
2437 qreal radius = argv[4].toNumber();
2438
2439 if (qt_is_finite(d: radius) && radius < 0)
2440 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
2441
2442 r->d()->context()->arcTo(x1: argv[0].toNumber(),
2443 y1: argv[1].toNumber(),
2444 x2: argv[2].toNumber(),
2445 y2: argv[3].toNumber(),
2446 radius);
2447 }
2448
2449 RETURN_RESULT(*thisObject);
2450
2451}
2452
2453/*!
2454 \qmlmethod object QtQuick::Context2D::beginPath()
2455
2456 Resets the current path to a new path.
2457 */
2458QV4::ReturnedValue QQuickJSContext2DPrototype::method_beginPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2459{
2460 QV4::Scope scope(b);
2461 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2462 CHECK_CONTEXT(r)
2463
2464 r->d()->context()->beginPath();
2465
2466 RETURN_RESULT(*thisObject);
2467
2468}
2469
2470/*!
2471 \qmlmethod object QtQuick::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
2472
2473 Adds a cubic bezier curve between the current position and the given endPoint using the control points specified by (\a {cp1x}, \a {cp1y}),
2474 and (\a {cp2x}, \a {cp2y}).
2475 After the curve is added, the current position is updated to be at the end point (\a {x}, \a {y}) of the curve.
2476 The following code produces the path shown below:
2477
2478 \code
2479 ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
2480 ctx.lineWidth = 1;
2481 ctx.beginPath();
2482 ctx.moveTo(20, 0);//start point
2483 ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
2484 ctx.stroke();
2485 \endcode
2486
2487 \image qml-item-canvas-bezierCurveTo.png
2488
2489 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
2490 \sa {https://web.archive.org/web/20130505222636if_/http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
2491 */
2492QV4::ReturnedValue QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2493{
2494 QV4::Scope scope(b);
2495 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2496 CHECK_CONTEXT(r)
2497
2498 if (argc >= 6) {
2499 qreal cp1x = argv[0].toNumber();
2500 qreal cp1y = argv[1].toNumber();
2501 qreal cp2x = argv[2].toNumber();
2502 qreal cp2y = argv[3].toNumber();
2503 qreal x = argv[4].toNumber();
2504 qreal y = argv[5].toNumber();
2505
2506 if (!qt_is_finite(d: cp1x) || !qt_is_finite(d: cp1y) || !qt_is_finite(d: cp2x) || !qt_is_finite(d: cp2y) || !qt_is_finite(d: x) || !qt_is_finite(d: y))
2507 RETURN_UNDEFINED();
2508
2509 r->d()->context()->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
2510 }
2511 RETURN_RESULT(*thisObject);
2512}
2513
2514/*!
2515 \qmlmethod object QtQuick::Context2D::clip()
2516
2517 Creates the clipping region from the current path.
2518 Any parts of the shape outside the clipping path are not displayed.
2519 To create a complex shape using the \c clip() method:
2520
2521 \list 1
2522 \li Call the \c{context.beginPath()} method to set the clipping path.
2523 \li Define the clipping path by calling any combination of the \c{lineTo},
2524 \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
2525 \li Call the \c{context.clip()} method.
2526 \endlist
2527
2528 The new shape displays. The following shows how a clipping path can
2529 modify how an image displays:
2530
2531 \image qml-item-canvas-clip-complex.png
2532 \sa beginPath()
2533 \sa closePath()
2534 \sa stroke()
2535 \sa fill()
2536 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
2537 */
2538QV4::ReturnedValue QQuickJSContext2DPrototype::method_clip(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2539{
2540 QV4::Scope scope(b);
2541 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2542 CHECK_CONTEXT(r)
2543
2544 r->d()->context()->clip();
2545 RETURN_RESULT(*thisObject);
2546}
2547
2548/*!
2549 \qmlmethod object QtQuick::Context2D::closePath()
2550 Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
2551 The current point of the new path is the previous subpath's first point.
2552
2553 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
2554 */
2555QV4::ReturnedValue QQuickJSContext2DPrototype::method_closePath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2556{
2557 QV4::Scope scope(b);
2558 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2559 CHECK_CONTEXT(r)
2560
2561 r->d()->context()->closePath();
2562
2563 RETURN_RESULT(*thisObject);
2564}
2565
2566/*!
2567 \qmlmethod object QtQuick::Context2D::fill()
2568
2569 Fills the subpaths with the current fill style.
2570
2571 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
2572
2573 \sa fillStyle
2574 */
2575QV4::ReturnedValue QQuickJSContext2DPrototype::method_fill(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2576{
2577 QV4::Scope scope(b);
2578 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2579 CHECK_CONTEXT(r);
2580 r->d()->context()->fill();
2581 RETURN_RESULT(*thisObject);
2582}
2583
2584/*!
2585 \qmlmethod object QtQuick::Context2D::lineTo(real x, real y)
2586
2587 Draws a line from the current position to the point at (\a x, \a y).
2588 */
2589QV4::ReturnedValue QQuickJSContext2DPrototype::method_lineTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2590{
2591 QV4::Scope scope(b);
2592 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2593 CHECK_CONTEXT(r)
2594
2595 if (argc >= 2) {
2596 qreal x = argv[0].toNumber();
2597 qreal y = argv[1].toNumber();
2598
2599 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
2600 RETURN_UNDEFINED();
2601
2602 r->d()->context()->lineTo(x, y);
2603 }
2604
2605 RETURN_RESULT(*thisObject);
2606}
2607
2608/*!
2609 \qmlmethod object QtQuick::Context2D::moveTo(real x, real y)
2610
2611 Creates a new subpath with a point at (\a x, \a y).
2612 */
2613QV4::ReturnedValue QQuickJSContext2DPrototype::method_moveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2614{
2615 QV4::Scope scope(b);
2616 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2617 CHECK_CONTEXT(r)
2618
2619 if (argc >= 2) {
2620 qreal x = argv[0].toNumber();
2621 qreal y = argv[1].toNumber();
2622
2623 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
2624 RETURN_UNDEFINED();
2625 r->d()->context()->moveTo(x, y);
2626 }
2627
2628 RETURN_RESULT(*thisObject);
2629}
2630
2631/*!
2632 \qmlmethod object QtQuick::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
2633
2634 Adds a quadratic bezier curve between the current point and the endpoint
2635 (\a x, \a y) with the control point specified by (\a cpx, \a cpy).
2636
2637 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for quadraticCurveTo}
2638 */
2639QV4::ReturnedValue QQuickJSContext2DPrototype::method_quadraticCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2640{
2641 QV4::Scope scope(b);
2642 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2643 CHECK_CONTEXT(r)
2644
2645 if (argc >= 4) {
2646 qreal cpx = argv[0].toNumber();
2647 qreal cpy = argv[1].toNumber();
2648 qreal x = argv[2].toNumber();
2649 qreal y = argv[3].toNumber();
2650
2651 if (!qt_is_finite(d: cpx) || !qt_is_finite(d: cpy) || !qt_is_finite(d: x) || !qt_is_finite(d: y))
2652 RETURN_UNDEFINED();
2653
2654 r->d()->context()->quadraticCurveTo(cpx, cpy, x, y);
2655 }
2656
2657 RETURN_RESULT(*thisObject);
2658}
2659
2660/*!
2661 \qmlmethod object QtQuick::Context2D::rect(real x, real y, real w, real h)
2662
2663 Adds a rectangle at position (\a x, \a y), with the given width \a w and
2664 height \a h, as a closed subpath.
2665 */
2666QV4::ReturnedValue QQuickJSContext2DPrototype::method_rect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2667{
2668 QV4::Scope scope(b);
2669 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2670 CHECK_CONTEXT(r)
2671
2672 if (argc >= 4)
2673 r->d()->context()->rect(x: argv[0].toNumber(), y: argv[1].toNumber(), w: argv[2].toNumber(), h: argv[3].toNumber());
2674 RETURN_RESULT(*thisObject);
2675
2676}
2677
2678/*!
2679 \qmlmethod object QtQuick::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius)
2680
2681 Adds a rounded-corner rectangle, specified by (\a x, \a y, \a w, \a h), to the path.
2682 The \a xRadius and \a yRadius arguments specify the radius of the
2683 ellipses defining the corners of the rounded rectangle.
2684 */
2685QV4::ReturnedValue QQuickJSContext2DPrototype::method_roundedRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2686{
2687 QV4::Scope scope(b);
2688 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2689 CHECK_CONTEXT(r)
2690
2691 if (argc >= 6)
2692 r->d()->context()->roundedRect(x: argv[0].toNumber()
2693 , y: argv[1].toNumber()
2694 , w: argv[2].toNumber()
2695 , h: argv[3].toNumber()
2696 , xr: argv[4].toNumber()
2697 , yr: argv[5].toNumber());
2698 RETURN_RESULT(*thisObject);
2699
2700}
2701
2702/*!
2703 \qmlmethod object QtQuick::Context2D::ellipse(real x, real y, real w, real h)
2704
2705 Creates an ellipse within the bounding rectangle defined by its top-left
2706 corner at (\a x, \a y), width \a w and height \a h, and adds it to the
2707 path as a closed subpath.
2708
2709 The ellipse is composed of a clockwise curve, starting and finishing at
2710 zero degrees (the 3 o'clock position).
2711 */
2712QV4::ReturnedValue QQuickJSContext2DPrototype::method_ellipse(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2713{
2714 QV4::Scope scope(b);
2715 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2716 CHECK_CONTEXT(r)
2717
2718 if (argc >= 4)
2719 r->d()->context()->ellipse(x: argv[0].toNumber(), y: argv[1].toNumber(), w: argv[2].toNumber(), h: argv[3].toNumber());
2720
2721 RETURN_RESULT(*thisObject);
2722
2723}
2724
2725/*!
2726 \qmlmethod object QtQuick::Context2D::text(string text, real x, real y)
2727
2728 Adds the given \a text to the path as a set of closed subpaths created
2729 from the current context font supplied.
2730
2731 The subpaths are positioned so that the left end of the text's baseline
2732 lies at the point specified by (\a x, \a y).
2733 */
2734QV4::ReturnedValue QQuickJSContext2DPrototype::method_text(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2735{
2736 QV4::Scope scope(b);
2737 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2738 CHECK_CONTEXT(r)
2739
2740 if (argc >= 3) {
2741 qreal x = argv[1].toNumber();
2742 qreal y = argv[2].toNumber();
2743
2744 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
2745 RETURN_UNDEFINED();
2746 r->d()->context()->text(str: argv[0].toQStringNoThrow(), x, y);
2747 }
2748
2749 RETURN_RESULT(*thisObject);
2750}
2751
2752/*!
2753 \qmlmethod object QtQuick::Context2D::stroke()
2754
2755 Strokes the subpaths with the current stroke style.
2756
2757 \sa strokeStyle, {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
2758 */
2759QV4::ReturnedValue QQuickJSContext2DPrototype::method_stroke(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2760{
2761 QV4::Scope scope(b);
2762 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2763 CHECK_CONTEXT(r)
2764
2765 r->d()->context()->stroke();
2766 RETURN_RESULT(*thisObject);
2767
2768}
2769
2770/*!
2771 \qmlmethod object QtQuick::Context2D::isPointInPath(real x, real y)
2772
2773 Returns \c true if the point (\a x, \a y) is in the current path.
2774
2775 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
2776 */
2777QV4::ReturnedValue QQuickJSContext2DPrototype::method_isPointInPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2778{
2779 QV4::Scope scope(b);
2780 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2781 CHECK_CONTEXT(r)
2782
2783 bool pointInPath = false;
2784 if (argc >= 2)
2785 pointInPath = r->d()->context()->isPointInPath(x: argv[0].toNumber(), y: argv[1].toNumber());
2786 RETURN_RESULT(QV4::Value::fromBoolean(pointInPath).asReturnedValue());
2787}
2788
2789QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawFocusRing(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2790{
2791 QV4::Scope scope(b);
2792 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
2793}
2794
2795QV4::ReturnedValue QQuickJSContext2DPrototype::method_setCaretSelectionRect(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2796{
2797 QV4::Scope scope(b);
2798 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
2799}
2800
2801QV4::ReturnedValue QQuickJSContext2DPrototype::method_caretBlinkRate(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2802{
2803 QV4::Scope scope(b);
2804 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
2805}
2806
2807/*!
2808 \qmlproperty string QtQuick::Context2D::font
2809 Holds the current font settings.
2810
2811 A subset of the
2812 \l {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
2813 is supported:
2814
2815 \list
2816 \li font-style (optional):
2817 normal | italic | oblique
2818 \li font-variant (optional): normal | small-caps
2819 \li font-weight (optional): normal | bold | 1 ... 1000
2820 \li font-size: Npx | Npt (where N is a positive number)
2821 \li font-family: See \l {http://www.w3.org/TR/CSS2/fonts.html#propdef-font-family}
2822 \endlist
2823
2824 \note The font-size and font-family properties are mandatory and must be in
2825 the order they are shown in above. In addition, a font family with spaces in
2826 its name must be quoted.
2827
2828 The default font value is "10px sans-serif".
2829 */
2830QV4::ReturnedValue QQuickJSContext2D::method_get_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2831{
2832 QV4::Scope scope(b);
2833 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2834 CHECK_CONTEXT(r)
2835
2836 RETURN_RESULT(scope.engine->newString(r->d()->context()->state.font.toString()));
2837}
2838
2839QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2840{
2841 QV4::Scope scope(b);
2842 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2843 CHECK_CONTEXT_SETTER(r)
2844
2845 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2846 if (scope.hasException())
2847 RETURN_UNDEFINED();
2848 QFont font = qt_font_from_string(fontString: s->toQString(), currentFont: r->d()->context()->state.font);
2849 if (font != r->d()->context()->state.font) {
2850 r->d()->context()->state.font = font;
2851 }
2852 RETURN_UNDEFINED();
2853}
2854
2855/*!
2856 \qmlproperty string QtQuick::Context2D::textAlign
2857
2858 Holds the current text alignment settings. The possible values are:
2859
2860 \value "start" (default) Align to the start edge of the text (left side in
2861 left-to-right text, right side in right-to-left text).
2862 \value "end" Align to the end edge of the text (right side in left-to-right
2863 text, left side in right-to-left text).
2864 \value "left" Qt::AlignLeft
2865 \value "right" Qt::AlignRight
2866 \value "center" Qt::AlignHCenter
2867
2868 Other values are ignored.
2869*/
2870QV4::ReturnedValue QQuickJSContext2D::method_get_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2871{
2872 QV4::Scope scope(b);
2873 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2874 CHECK_CONTEXT(r)
2875
2876 switch (r->d()->context()->state.textAlign) {
2877 case QQuickContext2D::End:
2878 RETURN_RESULT(scope.engine->newString(QStringLiteral("end")));
2879 case QQuickContext2D::Left:
2880 RETURN_RESULT(scope.engine->newString(QStringLiteral("left")));
2881 case QQuickContext2D::Right:
2882 RETURN_RESULT(scope.engine->newString(QStringLiteral("right")));
2883 case QQuickContext2D::Center:
2884 RETURN_RESULT(scope.engine->newString(QStringLiteral("center")));
2885 case QQuickContext2D::Start:
2886 default:
2887 break;
2888 }
2889 RETURN_RESULT(scope.engine->newString(QStringLiteral("start")));
2890}
2891
2892QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2893{
2894 QV4::Scope scope(b);
2895 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2896 CHECK_CONTEXT_SETTER(r)
2897
2898 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2899 if (scope.hasException())
2900 RETURN_UNDEFINED();
2901 QString textAlign = s->toQString();
2902
2903 QQuickContext2D::TextAlignType ta;
2904 if (textAlign == QLatin1String("start"))
2905 ta = QQuickContext2D::Start;
2906 else if (textAlign == QLatin1String("end"))
2907 ta = QQuickContext2D::End;
2908 else if (textAlign == QLatin1String("left"))
2909 ta = QQuickContext2D::Left;
2910 else if (textAlign == QLatin1String("right"))
2911 ta = QQuickContext2D::Right;
2912 else if (textAlign == QLatin1String("center"))
2913 ta = QQuickContext2D::Center;
2914 else
2915 RETURN_UNDEFINED();
2916
2917 if (ta != r->d()->context()->state.textAlign)
2918 r->d()->context()->state.textAlign = ta;
2919
2920 RETURN_UNDEFINED();
2921}
2922
2923/*!
2924 \qmlproperty string QtQuick::Context2D::textBaseline
2925
2926 Holds the current baseline alignment settings. The possible values are:
2927
2928 \value "top" The top of the em square
2929 \value "hanging" The hanging baseline
2930 \value "middle" The middle of the em square
2931 \value "alphabetic" (default) The alphabetic baseline
2932 \value "ideographic" The ideographic-under baseline
2933 \value "bottom" The bottom of the em square
2934
2935 Other values are ignored. The default value is "alphabetic".
2936*/
2937QV4::ReturnedValue QQuickJSContext2D::method_get_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2938{
2939 QV4::Scope scope(b);
2940 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2941 CHECK_CONTEXT(r)
2942
2943 switch (r->d()->context()->state.textBaseline) {
2944 case QQuickContext2D::Hanging:
2945 RETURN_RESULT(scope.engine->newString(QStringLiteral("hanging")));
2946 case QQuickContext2D::Top:
2947 RETURN_RESULT(scope.engine->newString(QStringLiteral("top")));
2948 case QQuickContext2D::Bottom:
2949 RETURN_RESULT(scope.engine->newString(QStringLiteral("bottom")));
2950 case QQuickContext2D::Middle:
2951 RETURN_RESULT(scope.engine->newString(QStringLiteral("middle")));
2952 case QQuickContext2D::Alphabetic:
2953 default:
2954 break;
2955 }
2956 RETURN_RESULT(scope.engine->newString(QStringLiteral("alphabetic")));
2957}
2958
2959QV4::ReturnedValue QQuickJSContext2D::method_set_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2960{
2961 QV4::Scope scope(b);
2962 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2963 CHECK_CONTEXT_SETTER(r)
2964 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2965 if (scope.hasException())
2966 RETURN_UNDEFINED();
2967 QString textBaseline = s->toQString();
2968
2969 QQuickContext2D::TextBaseLineType tb;
2970 if (textBaseline == QLatin1String("alphabetic"))
2971 tb = QQuickContext2D::Alphabetic;
2972 else if (textBaseline == QLatin1String("hanging"))
2973 tb = QQuickContext2D::Hanging;
2974 else if (textBaseline == QLatin1String("top"))
2975 tb = QQuickContext2D::Top;
2976 else if (textBaseline == QLatin1String("bottom"))
2977 tb = QQuickContext2D::Bottom;
2978 else if (textBaseline == QLatin1String("middle"))
2979 tb = QQuickContext2D::Middle;
2980 else
2981 RETURN_UNDEFINED();
2982
2983 if (tb != r->d()->context()->state.textBaseline)
2984 r->d()->context()->state.textBaseline = tb;
2985
2986 RETURN_UNDEFINED();
2987}
2988
2989/*!
2990 \qmlmethod object QtQuick::Context2D::fillText(text, x, y)
2991
2992 Fills the specified \a text at the given position (\a x, \a y).
2993
2994 \sa font
2995 \sa textAlign
2996 \sa textBaseline
2997 \sa strokeText
2998 */
2999QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3000{
3001 QV4::Scope scope(b);
3002 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3003 CHECK_CONTEXT(r)
3004
3005 if (argc >= 3) {
3006 qreal x = argv[1].toNumber();
3007 qreal y = argv[2].toNumber();
3008 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
3009 RETURN_UNDEFINED();
3010 QPainterPath textPath = r->d()->context()->createTextGlyphs(x, y, text: argv[0].toQStringNoThrow());
3011 r->d()->context()->buffer()->fill(path: textPath);
3012 }
3013
3014 RETURN_RESULT(*thisObject);
3015}
3016/*!
3017 \qmlmethod object QtQuick::Context2D::strokeText(text, x, y)
3018
3019 Strokes the given \a text at a position specified by (\a x, \a y).
3020
3021 \sa font
3022 \sa textAlign
3023 \sa textBaseline
3024 \sa fillText
3025*/
3026QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3027{
3028 QV4::Scope scope(b);
3029 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3030 CHECK_CONTEXT(r)
3031
3032 if (argc >= 3)
3033 r->d()->context()->drawText(text: argv[0].toQStringNoThrow(), x: argv[1].toNumber(), y: argv[2].toNumber(), fill: false);
3034
3035 RETURN_RESULT(*thisObject);
3036}
3037
3038/*!
3039 \qmlmethod object QtQuick::Context2D::measureText(text)
3040
3041 Returns an object with a \c width property, whose value is equivalent to
3042 calling QFontMetrics::horizontalAdvance() with the given \a text in the
3043 current font.
3044 */
3045QV4::ReturnedValue QQuickJSContext2DPrototype::method_measureText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3046{
3047 QV4::Scope scope(b);
3048 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3049 CHECK_CONTEXT(r)
3050
3051 if (argc >= 1) {
3052 QFontMetrics fm(r->d()->context()->state.font);
3053 uint width = fm.horizontalAdvance(argv[0].toQStringNoThrow());
3054 QV4::ScopedObject tm(scope, scope.engine->newObject());
3055 tm->put(name: QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("width"))).getPointer(),
3056 v: QV4::ScopedValue(scope, QV4::Value::fromDouble(d: width)));
3057 RETURN_RESULT(*tm);
3058 }
3059 RETURN_UNDEFINED();
3060}
3061
3062// drawing images
3063/*!
3064 \qmlmethod QtQuick::Context2D::drawImage(variant image, real dx, real dy)
3065 Draws the given \a image on the canvas at position (\a dx, \a dy).
3066 Note:
3067 The \a image type can be an Image item, an image url or a CanvasImageData object.
3068 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3069 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3070 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3071
3072 \sa CanvasImageData
3073 \sa Image
3074 \sa Canvas::loadImage
3075 \sa Canvas::isImageLoaded
3076 \sa Canvas::imageLoaded
3077
3078 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3079 */
3080/*!
3081 \qmlmethod QtQuick::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
3082 This is an overloaded function.
3083 Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
3084 height \a dh.
3085
3086 Note:
3087 The \a image type can be an Image item, an image url or a CanvasImageData object.
3088 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3089 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3090 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3091
3092 \sa CanvasImageData
3093 \sa Image
3094 \sa Canvas::loadImage()
3095 \sa Canvas::isImageLoaded
3096 \sa Canvas::imageLoaded
3097
3098 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3099 */
3100/*!
3101 \qmlmethod QtQuick::Context2D::drawImage(variant image, real sx, real sy, real sw, real sh, real dx, real dy, real dw, real dh)
3102 This is an overloaded function.
3103 Draws the given item as \a image from source point (\a sx, \a sy) and source width \a sw, source height \a sh
3104 onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
3105
3106
3107 Note:
3108 The \a image type can be an Image or Canvas item, an image url or a CanvasImageData object.
3109 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3110 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3111 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3112
3113 \sa CanvasImageData
3114 \sa Image
3115 \sa Canvas::loadImage()
3116 \sa Canvas::isImageLoaded
3117 \sa Canvas::imageLoaded
3118
3119 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3120*/
3121QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3122{
3123 QV4::Scope scope(b);
3124 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3125 CHECK_CONTEXT(r)
3126
3127 qreal sx, sy, sw, sh, dx, dy, dw, dh;
3128
3129 if (!argc)
3130 RETURN_UNDEFINED();
3131
3132 //FIXME:This function should be moved to QQuickContext2D::drawImage(...)
3133 if (!r->d()->context()->state.invertibleCTM)
3134 RETURN_UNDEFINED();
3135
3136 QQmlRefPointer<QQuickCanvasPixmap> pixmap;
3137
3138 QV4::ScopedValue arg(scope, argv[0]);
3139 if (arg->isString()) {
3140 QUrl url(arg->toQString());
3141 if (!url.isValid())
3142 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3143
3144 pixmap = r->d()->context()->createPixmap(url);
3145 } else if (arg->isObject()) {
3146 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, arg);
3147 if (!!qobjectWrapper) {
3148 if (QQuickImage *imageItem = qobject_cast<QQuickImage*>(object: qobjectWrapper->object())) {
3149 pixmap = r->d()->context()->createPixmap(url: imageItem->source());
3150 } else if (QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(object: qobjectWrapper->object())) {
3151 QImage img = canvas->toImage();
3152 if (!img.isNull())
3153 pixmap.adopt(new QQuickCanvasPixmap(img));
3154 } else {
3155 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3156 }
3157 } else {
3158 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg);
3159 if (!!imageData) {
3160 QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3161 if (pix && !pix->d()->image->isNull()) {
3162 pixmap.adopt(new QQuickCanvasPixmap(*pix->d()->image));
3163 } else {
3164 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3165 }
3166 } else {
3167 QUrl url(arg->toQStringNoThrow());
3168 if (url.isValid())
3169 pixmap = r->d()->context()->createPixmap(url);
3170 else
3171 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3172 }
3173 }
3174 } else {
3175 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3176 }
3177
3178 if (pixmap.isNull() || !pixmap->isValid())
3179 RETURN_UNDEFINED();
3180
3181 if (argc >= 9) {
3182 sx = argv[1].toNumber();
3183 sy = argv[2].toNumber();
3184 sw = argv[3].toNumber();
3185 sh = argv[4].toNumber();
3186 dx = argv[5].toNumber();
3187 dy = argv[6].toNumber();
3188 dw = argv[7].toNumber();
3189 dh = argv[8].toNumber();
3190 } else if (argc >= 5) {
3191 sx = 0;
3192 sy = 0;
3193 sw = pixmap->width();
3194 sh = pixmap->height();
3195 dx = argv[1].toNumber();
3196 dy = argv[2].toNumber();
3197 dw = argv[3].toNumber();
3198 dh = argv[4].toNumber();
3199 } else if (argc >= 3) {
3200 dx = argv[1].toNumber();
3201 dy = argv[2].toNumber();
3202 sx = 0;
3203 sy = 0;
3204 sw = pixmap->width();
3205 sh = pixmap->height();
3206 dw = sw;
3207 dh = sh;
3208 } else {
3209 RETURN_UNDEFINED();
3210 }
3211
3212 if (!qt_is_finite(d: sx)
3213 || !qt_is_finite(d: sy)
3214 || !qt_is_finite(d: sw)
3215 || !qt_is_finite(d: sh)
3216 || !qt_is_finite(d: dx)
3217 || !qt_is_finite(d: dy)
3218 || !qt_is_finite(d: dw)
3219 || !qt_is_finite(d: dh))
3220 RETURN_UNDEFINED();
3221
3222 if (sx < 0
3223 || sy < 0
3224 || sw == 0
3225 || sh == 0
3226 || sx + sw > pixmap->width()
3227 || sy + sh > pixmap->height()
3228 || sx + sw < 0 || sy + sh < 0) {
3229 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error");
3230 }
3231
3232 r->d()->context()->buffer()->drawPixmap(pixmap, sr: QRectF(sx, sy, sw, sh), dr: QRectF(dx, dy, dw, dh));
3233
3234 RETURN_RESULT(*thisObject);
3235}
3236
3237// pixel manipulation
3238/*!
3239 \qmltype CanvasImageData
3240 \inqmlmodule QtQuick
3241 \ingroup qtquick-canvas
3242 \brief Contains image pixel data in RGBA order.
3243
3244 The CanvasImageData object holds the image pixel data.
3245
3246 The CanvasImageData object has the actual dimensions of the data stored in
3247 this object and holds the one-dimensional array containing the data in RGBA order,
3248 as integers in the range 0 to 255.
3249
3250 \sa width
3251 \sa height
3252 \sa data
3253 \sa Context2D::createImageData()
3254 \sa Context2D::getImageData()
3255 \sa Context2D::putImageData()
3256 */
3257/*!
3258 \qmlproperty int QtQuick::CanvasImageData::width
3259 Holds the actual width dimension of the data in the ImageData object, in device pixels.
3260 */
3261QV4::ReturnedValue QQuickJSContext2DImageData::method_get_width(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3262{
3263 QV4::Scope scope(b);
3264 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3265 if (!imageData)
3266 THROW_TYPE_ERROR();
3267 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3268 int width = r ? r->d()->image->width() : 0;
3269 RETURN_RESULT(QV4::Encode(width));
3270}
3271
3272/*!
3273 \qmlproperty int QtQuick::CanvasImageData::height
3274 Holds the actual height dimension of the data in the ImageData object, in device pixels.
3275 */
3276QV4::ReturnedValue QQuickJSContext2DImageData::method_get_height(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3277{
3278 QV4::Scope scope(b);
3279 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3280 if (!imageData)
3281 THROW_TYPE_ERROR();
3282 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3283 int height = r ? r->d()->image->height() : 0;
3284 RETURN_RESULT(QV4::Encode(height));
3285}
3286
3287/*!
3288 \qmlproperty object QtQuick::CanvasImageData::data
3289 Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
3290 */
3291QV4::ReturnedValue QQuickJSContext2DImageData::method_get_data(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3292{
3293 QV4::Scope scope(b);
3294 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3295 if (!imageData)
3296 THROW_TYPE_ERROR();
3297 RETURN_RESULT(imageData->d()->pixelData);
3298}
3299
3300/*!
3301 \qmltype CanvasPixelArray
3302 \inqmlmodule QtQuick
3303 \ingroup qtquick-canvas
3304 \brief Provides ordered and indexed access to the components of each pixel in image data.
3305
3306 The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
3307 The CanvasPixelArray can be accessed as normal Javascript array.
3308 \sa CanvasImageData
3309 \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
3310 */
3311
3312/*!
3313 \qmlproperty int QtQuick::CanvasPixelArray::length
3314 The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
3315 The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
3316 This property is read only.
3317*/
3318QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3319{
3320 QV4::Scope scope(b);
3321 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, thisObject->as<QQuickJSContext2DPixelData>());
3322 if (!r || r->d()->image->isNull())
3323 RETURN_UNDEFINED();
3324
3325 RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4));
3326}
3327
3328QV4::ReturnedValue QQuickJSContext2DPixelData::virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
3329{
3330 if (!id.isArrayIndex())
3331 return QV4::Object::virtualGet(m, id, receiver, hasProperty);
3332
3333 uint index = id.asArrayIndex();
3334 Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
3335 QV4::ExecutionEngine *v4 = static_cast<const QQuickJSContext2DPixelData *>(m)->engine();
3336 QV4::Scope scope(v4);
3337 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<const QQuickJSContext2DPixelData *>(m));
3338
3339 if (index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4)) {
3340 if (hasProperty)
3341 *hasProperty = true;
3342 const quint32 w = r->d()->image->width();
3343 const quint32 row = (index / 4) / w;
3344 const quint32 col = (index / 4) % w;
3345 const QRgb* pixel = reinterpret_cast<const QRgb*>(r->d()->image->constScanLine(row));
3346 pixel += col;
3347 switch (index % 4) {
3348 case 0:
3349 return QV4::Encode(qRed(rgb: *pixel));
3350 case 1:
3351 return QV4::Encode(qGreen(rgb: *pixel));
3352 case 2:
3353 return QV4::Encode(qBlue(rgb: *pixel));
3354 case 3:
3355 return QV4::Encode(qAlpha(rgb: *pixel));
3356 }
3357 }
3358
3359 if (hasProperty)
3360 *hasProperty = false;
3361 return QV4::Encode::undefined();
3362}
3363
3364bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, QV4::Value *receiver)
3365{
3366 if (!id.isArrayIndex())
3367 return Object::virtualPut(m, id, value, receiver);
3368
3369 Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
3370 QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine();
3371 QV4::Scope scope(v4);
3372 if (scope.hasException())
3373 return false;
3374
3375 uint index = id.asArrayIndex();
3376 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
3377
3378 const int v = value.toInt32();
3379 if (r && index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4) && v >= 0 && v <= 255) {
3380 const quint32 w = r->d()->image->width();
3381 const quint32 row = (index / 4) / w;
3382 const quint32 col = (index / 4) % w;
3383
3384 QRgb* pixel = reinterpret_cast<QRgb*>(r->d()->image->scanLine(row));
3385 pixel += col;
3386 switch (index % 4) {
3387 case 0:
3388 *pixel = qRgba(r: v, g: qGreen(rgb: *pixel), b: qBlue(rgb: *pixel), a: qAlpha(rgb: *pixel));
3389 break;
3390 case 1:
3391 *pixel = qRgba(r: qRed(rgb: *pixel), g: v, b: qBlue(rgb: *pixel), a: qAlpha(rgb: *pixel));
3392 break;
3393 case 2:
3394 *pixel = qRgba(r: qRed(rgb: *pixel), g: qGreen(rgb: *pixel), b: v, a: qAlpha(rgb: *pixel));
3395 break;
3396 case 3:
3397 *pixel = qRgba(r: qRed(rgb: *pixel), g: qGreen(rgb: *pixel), b: qBlue(rgb: *pixel), a: v);
3398 break;
3399 }
3400 return true;
3401 }
3402
3403 return false;
3404}
3405/*!
3406 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(real sw, real sh)
3407
3408 Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
3409*/
3410/*!
3411 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(CanvasImageData imageData)
3412
3413 Creates a CanvasImageData object with the same dimensions as the argument.
3414*/
3415/*!
3416 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(Url imageUrl)
3417
3418 Creates a CanvasImageData object with the given image loaded from \a imageUrl.
3419
3420 \note The \a imageUrl must be already loaded before this function call,
3421 otherwise an empty CanvasImageData obect will be returned.
3422
3423 \sa Canvas::loadImage(), QtQuick::Canvas::unloadImage(),
3424 QtQuick::Canvas::isImageLoaded
3425 */
3426QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3427{
3428 QV4::Scope scope(b);
3429 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3430 CHECK_CONTEXT(r)
3431
3432 if (argc == 1) {
3433 QV4::ScopedValue arg0(scope, argv[0]);
3434 QV4::Scoped<QQuickJSContext2DImageData> imgData(scope, arg0);
3435 if (!!imgData) {
3436 QV4::Scoped<QQuickJSContext2DPixelData> pa(scope, imgData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3437 if (pa) {
3438 qreal w = pa->d()->image->width();
3439 qreal h = pa->d()->image->height();
3440 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, QImage()));
3441 }
3442 } else if (arg0->isString()) {
3443 QImage image = r->d()->context()->createPixmap(url: QUrl(arg0->toQStringNoThrow()))->image();
3444 RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, image));
3445 }
3446 } else if (argc == 2) {
3447 qreal w = argv[0].toNumber();
3448 qreal h = argv[1].toNumber();
3449
3450 if (!qt_is_finite(d: w) || !qt_is_finite(d: h))
3451 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments");
3452
3453 if (w > 0 && h > 0)
3454 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, QImage()));
3455 else
3456 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments");
3457 }
3458 RETURN_UNDEFINED();
3459}
3460
3461/*!
3462 \qmlmethod CanvasImageData QtQuick::Context2D::getImageData(real x, real y, real w, real h)
3463
3464 Returns an CanvasImageData object containing the image data for the canvas
3465 rectangle specified by (\a x, \a y, \a w, \a h).
3466 */
3467QV4::ReturnedValue QQuickJSContext2DPrototype::method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3468{
3469 QV4::Scope scope(b);
3470 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3471 CHECK_CONTEXT(r)
3472
3473 if (argc >= 4) {
3474 qreal x = argv[0].toNumber();
3475 qreal y = argv[1].toNumber();
3476 qreal w = argv[2].toNumber();
3477 qreal h = argv[3].toNumber();
3478 if (!qt_is_finite(d: x) || !qt_is_finite(d: y) || !qt_is_finite(d: w) || !qt_is_finite(d: h))
3479 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments");
3480
3481 if (w <= 0 || h <= 0)
3482 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
3483
3484 QImage image = r->d()->context()->canvas()->toImage(rect: QRectF(x, y, w, h));
3485 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, image));
3486 }
3487 RETURN_RESULT(QV4::Encode::null());
3488}
3489
3490/*!
3491 \qmlmethod object QtQuick::Context2D::putImageData(CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
3492
3493 Paints the data from the given \a imageData object onto the canvas at
3494 (\a dx, \a dy).
3495
3496 If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight)
3497 is provided, only the pixels from that rectangle are painted.
3498 */
3499QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3500{
3501 QV4::Scope scope(b);
3502 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3503 CHECK_CONTEXT(r)
3504 if (argc < 7)
3505 RETURN_UNDEFINED();
3506
3507 QV4::ScopedValue arg0(scope, argv[0]);
3508 if (!arg0->isObject())
3509 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch");
3510
3511 qreal dx = argv[1].toNumber();
3512 qreal dy = argv[2].toNumber();
3513 qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
3514
3515 if (!qt_is_finite(d: dx) || !qt_is_finite(d: dy))
3516 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
3517
3518 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg0);
3519 if (!imageData)
3520 RETURN_UNDEFINED();
3521
3522 QV4::Scoped<QQuickJSContext2DPixelData> pixelArray(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3523 if (pixelArray) {
3524 w = pixelArray->d()->image->width();
3525 h = pixelArray->d()->image->height();
3526
3527 if (argc == 7) {
3528 dirtyX = argv[3].toNumber();
3529 dirtyY = argv[4].toNumber();
3530 dirtyWidth = argv[5].toNumber();
3531 dirtyHeight = argv[6].toNumber();
3532
3533 if (!qt_is_finite(d: dirtyX) || !qt_is_finite(d: dirtyY) || !qt_is_finite(d: dirtyWidth) || !qt_is_finite(d: dirtyHeight))
3534 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
3535
3536
3537 if (dirtyWidth < 0) {
3538 dirtyX = dirtyX+dirtyWidth;
3539 dirtyWidth = -dirtyWidth;
3540 }
3541
3542 if (dirtyHeight < 0) {
3543 dirtyY = dirtyY+dirtyHeight;
3544 dirtyHeight = -dirtyHeight;
3545 }
3546
3547 if (dirtyX < 0) {
3548 dirtyWidth = dirtyWidth+dirtyX;
3549 dirtyX = 0;
3550 }
3551
3552 if (dirtyY < 0) {
3553 dirtyHeight = dirtyHeight+dirtyY;
3554 dirtyY = 0;
3555 }
3556
3557 if (dirtyX+dirtyWidth > w) {
3558 dirtyWidth = w - dirtyX;
3559 }
3560
3561 if (dirtyY+dirtyHeight > h) {
3562 dirtyHeight = h - dirtyY;
3563 }
3564
3565 if (dirtyWidth <=0 || dirtyHeight <= 0)
3566 RETURN_UNDEFINED();
3567 } else {
3568 dirtyX = 0;
3569 dirtyY = 0;
3570 dirtyWidth = w;
3571 dirtyHeight = h;
3572 }
3573
3574 QImage image = pixelArray->d()->image->copy(x: dirtyX, y: dirtyY, w: dirtyWidth, h: dirtyHeight);
3575 r->d()->context()->buffer()->drawImage(image, sr: QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), dr: QRectF(dx, dy, dirtyWidth, dirtyHeight));
3576 }
3577
3578 RETURN_RESULT(*thisObject);
3579}
3580
3581/*!
3582 \qmltype CanvasGradient
3583 \inqmlmodule QtQuick
3584 \since 5.0
3585 \ingroup qtquick-canvas
3586 \brief Provides an opaque CanvasGradient interface.
3587 */
3588
3589/*!
3590 \qmlmethod CanvasGradient QtQuick::CanvasGradient::addColorStop(real offset, string color)
3591
3592 Adds a color stop with the given \a color to the gradient at the given \a offset.
3593 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
3594
3595 For example:
3596
3597 \code
3598 var gradient = ctx.createLinearGradient(0, 0, 100, 100);
3599 gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
3600 gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1)');
3601 \endcode
3602 */
3603QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3604{
3605 QV4::Scope scope(b);
3606 QV4::Scoped<QQuickContext2DStyle> style(scope, thisObject->as<QQuickContext2DStyle>());
3607 if (!style)
3608 THROW_GENERIC_ERROR("Not a CanvasGradient object");
3609
3610 if (argc == 2) {
3611
3612 if (!style->d()->brush->gradient())
3613 THROW_GENERIC_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
3614 QGradient gradient = *(style->d()->brush->gradient());
3615 qreal pos = argv[0].toNumber();
3616 QColor color;
3617
3618 if (argv[1].as<Object>()) {
3619 color = QV4::ExecutionEngine::toVariant(
3620 value: argv[1], typeHint: QMetaType::fromType<QColor>()).value<QColor>();
3621 } else {
3622 color = qt_color_from_string(name: argv[1]);
3623 }
3624 if (pos < 0.0 || pos > 1.0 || !qt_is_finite(d: pos)) {
3625 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range");
3626 }
3627
3628 if (color.isValid()) {
3629 gradient.setColorAt(pos, color);
3630 } else {
3631 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
3632 }
3633 *style->d()->brush = gradient;
3634 }
3635
3636 return thisObject->asReturnedValue();
3637}
3638
3639void QQuickContext2D::scale(qreal x, qreal y)
3640{
3641 if (!state.invertibleCTM)
3642 return;
3643
3644 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
3645 return;
3646
3647 QTransform newTransform = state.matrix;
3648 newTransform.scale(sx: x, sy: y);
3649
3650 if (!newTransform.isInvertible()) {
3651 state.invertibleCTM = false;
3652 return;
3653 }
3654
3655 state.matrix = newTransform;
3656 buffer()->updateMatrix(matrix: state.matrix);
3657 m_path = QTransform().scale(sx: 1.0 / x, sy: 1.0 / y).map(p: m_path);
3658}
3659
3660void QQuickContext2D::rotate(qreal angle)
3661{
3662 if (!state.invertibleCTM)
3663 return;
3664
3665 if (!qt_is_finite(d: angle))
3666 return;
3667
3668 QTransform newTransform =state.matrix;
3669 newTransform.rotate(a: qRadiansToDegrees(radians: angle));
3670
3671 if (!newTransform.isInvertible()) {
3672 state.invertibleCTM = false;
3673 return;
3674 }
3675
3676 state.matrix = newTransform;
3677 buffer()->updateMatrix(matrix: state.matrix);
3678 m_path = QTransform().rotate(a: -qRadiansToDegrees(radians: angle)).map(p: m_path);
3679}
3680
3681void QQuickContext2D::shear(qreal h, qreal v)
3682{
3683 if (!state.invertibleCTM)
3684 return;
3685
3686 if (!qt_is_finite(d: h) || !qt_is_finite(d: v))
3687 return ;
3688
3689 QTransform newTransform = state.matrix;
3690 newTransform.shear(sh: h, sv: v);
3691
3692 if (!newTransform.isInvertible()) {
3693 state.invertibleCTM = false;
3694 return;
3695 }
3696
3697 state.matrix = newTransform;
3698 buffer()->updateMatrix(matrix: state.matrix);
3699 m_path = QTransform().shear(sh: -h, sv: -v).map(p: m_path);
3700}
3701
3702void QQuickContext2D::translate(qreal x, qreal y)
3703{
3704 if (!state.invertibleCTM)
3705 return;
3706
3707 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
3708 return ;
3709
3710 QTransform newTransform = state.matrix;
3711 newTransform.translate(dx: x, dy: y);
3712
3713 if (!newTransform.isInvertible()) {
3714 state.invertibleCTM = false;
3715 return;
3716 }
3717
3718 state.matrix = newTransform;
3719 buffer()->updateMatrix(matrix: state.matrix);
3720 m_path = QTransform().translate(dx: -x, dy: -y).map(p: m_path);
3721}
3722
3723void QQuickContext2D::transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
3724{
3725 if (!state.invertibleCTM)
3726 return;
3727
3728 if (!qt_is_finite(d: a) || !qt_is_finite(d: b) || !qt_is_finite(d: c) || !qt_is_finite(d) || !qt_is_finite(d: e) || !qt_is_finite(d: f))
3729 return;
3730
3731 QTransform transform(a, b, c, d, e, f);
3732 QTransform newTransform = state.matrix * transform;
3733
3734 if (!newTransform.isInvertible()) {
3735 state.invertibleCTM = false;
3736 return;
3737 }
3738 state.matrix = newTransform;
3739 buffer()->updateMatrix(matrix: state.matrix);
3740 m_path = transform.inverted().map(p: m_path);
3741}
3742
3743void QQuickContext2D::setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
3744{
3745 if (!qt_is_finite(d: a) || !qt_is_finite(d: b) || !qt_is_finite(d: c) || !qt_is_finite(d) || !qt_is_finite(d: e) || !qt_is_finite(d: f))
3746 return;
3747
3748 QTransform ctm = state.matrix;
3749 if (!ctm.isInvertible())
3750 return;
3751
3752 state.matrix = ctm.inverted() * state.matrix;
3753 m_path = ctm.map(p: m_path);
3754 state.invertibleCTM = true;
3755 transform(a, b, c, d, e, f);
3756}
3757
3758void QQuickContext2D::fill()
3759{
3760 if (!state.invertibleCTM)
3761 return;
3762
3763 if (!m_path.elementCount())
3764 return;
3765
3766 m_path.setFillRule(state.fillRule);
3767 buffer()->fill(path: m_path);
3768}
3769
3770void QQuickContext2D::clip()
3771{
3772 if (!state.invertibleCTM)
3773 return;
3774
3775 QPainterPath clipPath = m_path;
3776 clipPath.closeSubpath();
3777 if (state.clip) {
3778 state.clipPath = clipPath.intersected(r: state.clipPath);
3779 } else {
3780 state.clip = true;
3781 state.clipPath = clipPath;
3782 }
3783 buffer()->clip(enabled: state.clip, path: state.clipPath);
3784}
3785
3786void QQuickContext2D::stroke()
3787{
3788 if (!state.invertibleCTM)
3789 return;
3790
3791 if (!m_path.elementCount())
3792 return;
3793
3794 buffer()->stroke(path: m_path);
3795}
3796
3797void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
3798{
3799 if (!state.invertibleCTM)
3800 return;
3801
3802 if (!qt_is_finite(d: x) || !qt_is_finite(d: y) || !qt_is_finite(d: w) || !qt_is_finite(d: h))
3803 return;
3804
3805 buffer()->fillRect(r: QRectF(x, y, w, h));
3806}
3807
3808void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
3809{
3810 if (!state.invertibleCTM)
3811 return;
3812
3813 if (!qt_is_finite(d: x) || !qt_is_finite(d: y) || !qt_is_finite(d: w) || !qt_is_finite(d: h))
3814 return;
3815
3816 buffer()->strokeRect(r: QRectF(x, y, w, h));
3817}
3818
3819void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
3820{
3821 if (!state.invertibleCTM)
3822 return;
3823
3824 if (!qt_is_finite(d: x) || !qt_is_finite(d: y) || !qt_is_finite(d: w) || !qt_is_finite(d: h))
3825 return;
3826
3827 buffer()->clearRect(r: QRectF(x, y, w, h));
3828}
3829
3830void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill)
3831{
3832 if (!state.invertibleCTM)
3833 return;
3834
3835 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
3836 return;
3837
3838 QPainterPath textPath = createTextGlyphs(x, y, text);
3839 if (fill)
3840 buffer()->fill(path: textPath);
3841 else
3842 buffer()->stroke(path: textPath);
3843}
3844
3845
3846void QQuickContext2D::beginPath()
3847{
3848 if (!m_path.elementCount())
3849 return;
3850 m_path = QPainterPath();
3851}
3852
3853void QQuickContext2D::closePath()
3854{
3855 if (!m_path.elementCount())
3856 return;
3857
3858 QRectF boundRect = m_path.boundingRect();
3859 if (boundRect.width() || boundRect.height())
3860 m_path.closeSubpath();
3861 //FIXME:QPainterPath set the current point to (0,0) after close subpath
3862 //should be the first point of the previous subpath
3863}
3864
3865void QQuickContext2D::moveTo( qreal x, qreal y)
3866{
3867 if (!state.invertibleCTM)
3868 return;
3869
3870 //FIXME: moveTo should not close the previous subpath
3871 m_path.moveTo(p: QPointF(x, y));
3872}
3873
3874void QQuickContext2D::lineTo( qreal x, qreal y)
3875{
3876 if (!state.invertibleCTM)
3877 return;
3878
3879 QPointF pt(x, y);
3880
3881 if (!m_path.elementCount())
3882 m_path.moveTo(p: pt);
3883 else if (m_path.currentPosition() != pt)
3884 m_path.lineTo(p: pt);
3885}
3886
3887void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
3888 qreal x, qreal y)
3889{
3890 if (!state.invertibleCTM)
3891 return;
3892
3893 if (!m_path.elementCount())
3894 m_path.moveTo(p: QPointF(cpx, cpy));
3895
3896 QPointF pt(x, y);
3897 if (m_path.currentPosition() != pt)
3898 m_path.quadTo(ctrlPt: QPointF(cpx, cpy), endPt: pt);
3899}
3900
3901void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
3902 qreal cp2x, qreal cp2y,
3903 qreal x, qreal y)
3904{
3905 if (!state.invertibleCTM)
3906 return;
3907
3908 if (!m_path.elementCount())
3909 m_path.moveTo(p: QPointF(cp1x, cp1y));
3910
3911 QPointF pt(x, y);
3912 if (m_path.currentPosition() != pt)
3913 m_path.cubicTo(ctrlPt1: QPointF(cp1x, cp1y), ctrlPt2: QPointF(cp2x, cp2y), endPt: pt);
3914}
3915
3916void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, qreal radius)
3917{
3918 QPointF p0(m_path.currentPosition());
3919
3920 QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
3921 QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
3922 qreal p1p0_length = std::hypot(x: p1p0.x(), y: p1p0.y());
3923 qreal p1p2_length = std::hypot(x: p1p2.x(), y: p1p2.y());
3924
3925 qreal cos_phi = QPointF::dotProduct(p1: p1p0, p2: p1p2) / (p1p0_length * p1p2_length);
3926
3927 // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
3928 // We could have used areCollinear() here, but since we're reusing
3929 // the variables computed above later on we keep this logic.
3930 if (qFuzzyCompare(p1: std::abs(x: cos_phi), p2: 1.0)) {
3931 m_path.lineTo(p: p1);
3932 return;
3933 }
3934
3935 qreal tangent = radius / std::tan(x: std::acos(x: cos_phi) / 2);
3936 qreal factor_p1p0 = tangent / p1p0_length;
3937 QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
3938
3939 QPointF orth_p1p0(p1p0.y(), -p1p0.x());
3940 qreal orth_p1p0_length = std::hypot(x: orth_p1p0.x(), y: orth_p1p0.y());
3941 qreal factor_ra = radius / orth_p1p0_length;
3942
3943 // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
3944 qreal cos_alpha = QPointF::dotProduct(p1: orth_p1p0, p2: p1p2) / (orth_p1p0_length * p1p2_length);
3945 if (cos_alpha < 0.f)
3946 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3947
3948 QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
3949
3950 // calculate angles for addArc
3951 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3952 qreal sa = std::atan2(y: orth_p1p0.y(), x: orth_p1p0.x());
3953
3954 // anticlockwise logic
3955 bool anticlockwise = false;
3956
3957 qreal factor_p1p2 = tangent / p1p2_length;
3958 QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
3959 QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
3960 qreal ea = std::atan2(y: orth_p1p2.y(), x: orth_p1p2.x());
3961 if ((sa > ea) && ((sa - ea) < M_PI))
3962 anticlockwise = true;
3963 if ((sa < ea) && ((ea - sa) > M_PI))
3964 anticlockwise = true;
3965
3966 arc(x: p.x(), y: p.y(), radius, startAngle: sa, endAngle: ea, anticlockwise);
3967}
3968
3969void QQuickContext2D::arcTo(qreal x1, qreal y1,
3970 qreal x2, qreal y2,
3971 qreal radius)
3972{
3973 if (!state.invertibleCTM)
3974 return;
3975
3976 if (!qt_is_finite(d: x1) || !qt_is_finite(d: y1) || !qt_is_finite(d: x2) || !qt_is_finite(d: y2) || !qt_is_finite(d: radius))
3977 return;
3978
3979 QPointF st(x1, y1);
3980 QPointF end(x2, y2);
3981
3982 if (!m_path.elementCount())
3983 m_path.moveTo(p: st);
3984 else if (st == m_path.currentPosition() || st == end || !radius)
3985 lineTo(x: x1, y: y1);
3986 else
3987 addArcTo(p1: st, p2: end, radius);
3988 }
3989
3990void QQuickContext2D::rect(qreal x, qreal y, qreal w, qreal h)
3991{
3992 if (!state.invertibleCTM)
3993 return;
3994 if (!qt_is_finite(d: x) || !qt_is_finite(d: y) || !qt_is_finite(d: w) || !qt_is_finite(d: h))
3995 return;
3996
3997 if (!w && !h) {
3998 m_path.moveTo(x, y);
3999 return;
4000 }
4001 m_path.addRect(x, y, w, h);
4002}
4003
4004void QQuickContext2D::roundedRect(qreal x, qreal y,
4005 qreal w, qreal h,
4006 qreal xr, qreal yr)
4007{
4008 if (!state.invertibleCTM)
4009 return;
4010
4011 if (!qt_is_finite(d: x) || !qt_is_finite(d: y) || !qt_is_finite(d: w) || !qt_is_finite(d: h) || !qt_is_finite(d: xr) || !qt_is_finite(d: yr))
4012 return;
4013
4014 if (!w && !h) {
4015 m_path.moveTo(x, y);
4016 return;
4017 }
4018 m_path.addRoundedRect(rect: QRectF(x, y, w, h), xRadius: xr, yRadius: yr, mode: Qt::AbsoluteSize);
4019}
4020
4021void QQuickContext2D::ellipse(qreal x, qreal y,
4022 qreal w, qreal h)
4023{
4024 if (!state.invertibleCTM)
4025 return;
4026
4027 if (!qt_is_finite(d: x) || !qt_is_finite(d: y) || !qt_is_finite(d: w) || !qt_is_finite(d: h))
4028 return;
4029
4030 if (!w && !h) {
4031 m_path.moveTo(x, y);
4032 return;
4033 }
4034
4035 m_path.addEllipse(x, y, w, h);
4036}
4037
4038void QQuickContext2D::text(const QString& str, qreal x, qreal y)
4039{
4040 if (!state.invertibleCTM)
4041 return;
4042
4043 QPainterPath path;
4044 path.addText(x, y, f: state.font, text: str);
4045 m_path.addPath(path);
4046}
4047
4048void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise)
4049{
4050 if (!state.invertibleCTM)
4051 return;
4052
4053 if (!qt_is_finite(d: xc) || !qt_is_finite(d: yc) || !qt_is_finite(d: sar) || !qt_is_finite(d: ear) || !qt_is_finite(d: radius))
4054 return;
4055
4056 if (sar == ear)
4057 return;
4058
4059
4060 //### HACK
4061
4062 // In Qt we don't switch the coordinate system for degrees
4063 // and still use the 0,0 as bottom left for degrees so we need
4064 // to switch
4065 sar = -sar;
4066 ear = -ear;
4067 antiClockWise = !antiClockWise;
4068 //end hack
4069
4070 float sa = qRadiansToDegrees(radians: sar);
4071 float ea = qRadiansToDegrees(radians: ear);
4072
4073 double span = 0;
4074
4075 double xs = xc - radius;
4076 double ys = yc - radius;
4077 double width = radius*2;
4078 double height = radius*2;
4079 if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360)))
4080 // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
4081 // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
4082 // circumference of this circle.
4083 span = 360;
4084 else {
4085 if (!antiClockWise && (ea < sa)) {
4086 span += 360;
4087 } else if (antiClockWise && (sa < ea)) {
4088 span -= 360;
4089 }
4090 //### this is also due to switched coordinate system
4091 // we would end up with a 0 span instead of 360
4092 if (!(qFuzzyCompare(p1: span + (ea - sa) + 1, p2: 1) &&
4093 qFuzzyCompare(p1: qAbs(t: span), p2: 360))) {
4094 span += ea - sa;
4095 }
4096 }
4097
4098 // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
4099 if (!m_path.elementCount())
4100 m_path.arcMoveTo(x: xs, y: ys, w: width, h: height, angle: sa);
4101 else if (!radius) {
4102 m_path.lineTo(x: xc, y: yc);
4103 return;
4104 }
4105
4106 m_path.arcTo(x: xs, y: ys, w: width, h: height, startAngle: sa, arcLength: span);
4107}
4108
4109int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
4110{
4111 int offset = 0;
4112 switch (value) {
4113 case QQuickContext2D::Top:
4114 case QQuickContext2D::Hanging:
4115 break;
4116 case QQuickContext2D::Middle:
4117 offset = (metrics.ascent() >> 1) + metrics.height() - metrics.ascent();
4118 break;
4119 case QQuickContext2D::Alphabetic:
4120 offset = metrics.ascent();
4121 break;
4122 case QQuickContext2D::Bottom:
4123 offset = metrics.height();
4124 break;
4125 }
4126 return offset;
4127}
4128
4129static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
4130{
4131 int offset = 0;
4132 if (value == QQuickContext2D::Start)
4133 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Left : QQuickContext2D::Right;
4134 else if (value == QQuickContext2D::End)
4135 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Right: QQuickContext2D::Left;
4136 switch (value) {
4137 case QQuickContext2D::Center:
4138 offset = metrics.horizontalAdvance(text) / 2;
4139 break;
4140 case QQuickContext2D::Right:
4141 offset = metrics.horizontalAdvance(text);
4142 case QQuickContext2D::Left:
4143 default:
4144 break;
4145 }
4146 return offset;
4147}
4148
4149void QQuickContext2D::setGrabbedImage(const QImage& grab)
4150{
4151 m_grabbedImage = grab;
4152 m_grabbed = true;
4153}
4154
4155QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url)
4156{
4157 return m_canvas->loadedPixmap(url);
4158}
4159
4160QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
4161{
4162 const QFontMetrics metrics(state.font);
4163 int yoffset = baseLineOffset(value: static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
4164 int xoffset = textAlignOffset(value: static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
4165
4166 QPainterPath textPath;
4167
4168 textPath.addText(x: x - xoffset, y: y - yoffset+metrics.ascent(), f: state.font, text);
4169 return textPath;
4170}
4171
4172
4173static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
4174{
4175 // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
4176 return qFuzzyCompare(p1: (c.y() - b.y()) * (a.x() - b.x()), p2: (a.y() - b.y()) * (c.x() - b.x()));
4177}
4178
4179static inline bool withinRange(qreal p, qreal a, qreal b)
4180{
4181 return (p >= a && p <= b) || (p >= b && p <= a);
4182}
4183
4184bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
4185{
4186 if (!state.invertibleCTM)
4187 return false;
4188
4189 if (!m_path.elementCount())
4190 return false;
4191
4192 if (!qt_is_finite(d: x) || !qt_is_finite(d: y))
4193 return false;
4194
4195 QPointF point(x, y);
4196 QTransform ctm = state.matrix;
4197 QPointF p = ctm.inverted().map(p: point);
4198 if (!qt_is_finite(d: p.x()) || !qt_is_finite(d: p.y()))
4199 return false;
4200
4201 const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
4202
4203 bool contains = m_path.contains(pt: p);
4204
4205 if (!contains) {
4206 // check whether the point is on the border
4207 QPolygonF border = m_path.toFillPolygon();
4208
4209 QPointF p1 = border.at(i: 0);
4210 QPointF p2;
4211
4212 for (int i = 1; i < border.size(); ++i) {
4213 p2 = border.at(i);
4214 if (areCollinear(a: p, b: p1, c: p2)
4215 // Once we know that the points are collinear we
4216 // only need to check one of the coordinates
4217 && (qAbs(t: p2.x() - p1.x()) > qAbs(t: p2.y() - p1.y()) ?
4218 withinRange(p: p.x(), a: p1.x(), b: p2.x()) :
4219 withinRange(p: p.y(), a: p1.y(), b: p2.y()))) {
4220 return true;
4221 }
4222 p1 = p2;
4223 }
4224 }
4225 return contains;
4226}
4227
4228QMutex QQuickContext2D::mutex;
4229
4230QQuickContext2D::QQuickContext2D(QObject *parent)
4231 : QQuickCanvasContext(parent)
4232 , m_buffer(new QQuickContext2DCommandBuffer)
4233 , m_v4engine(nullptr)
4234 , m_surface(nullptr)
4235 , m_thread(nullptr)
4236 , m_grabbed(false)
4237{
4238}
4239
4240QQuickContext2D::~QQuickContext2D()
4241{
4242 mutex.lock();
4243 m_texture->setItem(nullptr);
4244 delete m_buffer;
4245 m_texture->deleteLater();
4246
4247 mutex.unlock();
4248}
4249
4250QV4::ReturnedValue QQuickContext2D::v4value() const
4251{
4252 return m_v4value.value();
4253}
4254
4255QStringList QQuickContext2D::contextNames() const
4256{
4257 return QStringList() << QStringLiteral("2d");
4258}
4259
4260void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args)
4261{
4262 Q_UNUSED(args);
4263
4264 m_canvas = canvasItem;
4265 m_renderTarget = canvasItem->renderTarget();
4266 m_renderStrategy = canvasItem->renderStrategy();
4267
4268 // Disable threaded background rendering if the platform has issues with it
4269 if (m_renderTarget == QQuickCanvasItem::FramebufferObject
4270 && m_renderStrategy == QQuickCanvasItem::Threaded
4271 && !QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::ThreadedOpenGL)) {
4272 m_renderTarget = QQuickCanvasItem::Image;
4273 }
4274
4275 // Disable framebuffer object based rendering always in Qt 6. It
4276 // is not implemented in the new RHI-based graphics stack, but the
4277 // enum value is still present. Switch to Image instead.
4278 if (m_renderTarget == QQuickCanvasItem::FramebufferObject)
4279 m_renderTarget = QQuickCanvasItem::Image;
4280
4281 m_texture = new QQuickContext2DImageTexture;
4282
4283 m_texture->setItem(canvasItem);
4284 m_texture->setCanvasWindow(canvasItem->canvasWindow().toRect());
4285 m_texture->setTileSize(canvasItem->tileSize());
4286 m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
4287 m_texture->setSmooth(canvasItem->smooth());
4288 m_texture->setAntialiasing(canvasItem->antialiasing());
4289 m_texture->setOnCustomThread(m_renderStrategy == QQuickCanvasItem::Threaded);
4290 m_thread = QThread::currentThread();
4291
4292 QThread *renderThread = m_thread;
4293 if (m_renderStrategy == QQuickCanvasItem::Threaded)
4294 renderThread = QQuickContext2DRenderThread::instance(engine: qmlEngine(canvasItem));
4295 if (renderThread && renderThread != QThread::currentThread())
4296 m_texture->moveToThread(thread: renderThread);
4297 connect(asender: m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
4298
4299 reset();
4300}
4301
4302void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing)
4303{
4304 if (m_texture->thread() == QThread::currentThread()) {
4305 m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth, antialiasing);
4306 } else {
4307 QEvent *e = new QQuickContext2DTexture::CanvasChangeEvent(canvasSize,
4308 tileSize,
4309 canvasWindow,
4310 dirtyRect,
4311 smooth,
4312 antialiasing);
4313 QCoreApplication::postEvent(receiver: m_texture, event: e);
4314 }
4315}
4316
4317void QQuickContext2D::flush()
4318{
4319 if (m_buffer) {
4320 if (m_texture->thread() == QThread::currentThread())
4321 m_texture->paint(ccb: m_buffer);
4322 else
4323 QCoreApplication::postEvent(receiver: m_texture, event: new QQuickContext2DTexture::PaintEvent(m_buffer));
4324 }
4325 m_buffer = new QQuickContext2DCommandBuffer();
4326}
4327
4328QQuickContext2DTexture *QQuickContext2D::texture() const
4329{
4330 return m_texture;
4331}
4332
4333QImage QQuickContext2D::toImage(const QRectF& bounds)
4334{
4335 if (m_texture->thread() == QThread::currentThread()) {
4336 flush();
4337 m_texture->grabImage(region: bounds);
4338 } else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
4339 qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode";
4340 return QImage();
4341 } else {
4342 flush();
4343 QCoreApplication::postEvent(receiver: m_texture, event: new QEvent(QEvent::Type(QEvent::User + 10)));
4344 QMetaObject::invokeMethod(obj: m_texture,
4345 member: "grabImage",
4346 c: Qt::BlockingQueuedConnection,
4347 Q_ARG(QRectF, bounds));
4348 }
4349 QImage img = m_grabbedImage;
4350 m_grabbedImage = QImage();
4351 m_grabbed = false;
4352 return img;
4353}
4354
4355
4356QQuickContext2DEngineData::QQuickContext2DEngineData(QV4::ExecutionEngine *v4)
4357{
4358 QV4::Scope scope(v4);
4359
4360 QV4::ScopedObject proto(scope, QQuickJSContext2DPrototype::create(engine: v4));
4361 proto->defineAccessorProperty(QStringLiteral("strokeStyle"), getter: QQuickJSContext2D::method_get_strokeStyle, setter: QQuickJSContext2D::method_set_strokeStyle);
4362 proto->defineAccessorProperty(QStringLiteral("font"), getter: QQuickJSContext2D::method_get_font, setter: QQuickJSContext2D::method_set_font);
4363 proto->defineAccessorProperty(QStringLiteral("fillRule"), getter: QQuickJSContext2D::method_get_fillRule, setter: QQuickJSContext2D::method_set_fillRule);
4364 proto->defineAccessorProperty(QStringLiteral("globalAlpha"), getter: QQuickJSContext2D::method_get_globalAlpha, setter: QQuickJSContext2D::method_set_globalAlpha);
4365 proto->defineAccessorProperty(QStringLiteral("lineCap"), getter: QQuickJSContext2D::method_get_lineCap, setter: QQuickJSContext2D::method_set_lineCap);
4366 proto->defineAccessorProperty(QStringLiteral("shadowOffsetX"), getter: QQuickJSContext2D::method_get_shadowOffsetX, setter: QQuickJSContext2D::method_set_shadowOffsetX);
4367 proto->defineAccessorProperty(QStringLiteral("shadowOffsetY"), getter: QQuickJSContext2D::method_get_shadowOffsetY, setter: QQuickJSContext2D::method_set_shadowOffsetY);
4368 proto->defineAccessorProperty(QStringLiteral("globalCompositeOperation"), getter: QQuickJSContext2D::method_get_globalCompositeOperation, setter: QQuickJSContext2D::method_set_globalCompositeOperation);
4369 proto->defineAccessorProperty(QStringLiteral("miterLimit"), getter: QQuickJSContext2D::method_get_miterLimit, setter: QQuickJSContext2D::method_set_miterLimit);
4370 proto->defineAccessorProperty(QStringLiteral("fillStyle"), getter: QQuickJSContext2D::method_get_fillStyle, setter: QQuickJSContext2D::method_set_fillStyle);
4371 proto->defineAccessorProperty(QStringLiteral("shadowColor"), getter: QQuickJSContext2D::method_get_shadowColor, setter: QQuickJSContext2D::method_set_shadowColor);
4372 proto->defineAccessorProperty(QStringLiteral("textBaseline"), getter: QQuickJSContext2D::method_get_textBaseline, setter: QQuickJSContext2D::method_set_textBaseline);
4373#if QT_CONFIG(quick_path)
4374 proto->defineAccessorProperty(QStringLiteral("path"), getter: QQuickJSContext2D::method_get_path, setter: QQuickJSContext2D::method_set_path);
4375#endif
4376 proto->defineAccessorProperty(QStringLiteral("lineJoin"), getter: QQuickJSContext2D::method_get_lineJoin, setter: QQuickJSContext2D::method_set_lineJoin);
4377 proto->defineAccessorProperty(QStringLiteral("lineWidth"), getter: QQuickJSContext2D::method_get_lineWidth, setter: QQuickJSContext2D::method_set_lineWidth);
4378 proto->defineAccessorProperty(QStringLiteral("textAlign"), getter: QQuickJSContext2D::method_get_textAlign, setter: QQuickJSContext2D::method_set_textAlign);
4379 proto->defineAccessorProperty(QStringLiteral("shadowBlur"), getter: QQuickJSContext2D::method_get_shadowBlur, setter: QQuickJSContext2D::method_set_shadowBlur);
4380 proto->defineAccessorProperty(QStringLiteral("lineDashOffset"), getter: QQuickJSContext2D::method_get_lineDashOffset, setter: QQuickJSContext2D::method_set_lineDashOffset);
4381 contextPrototype = proto;
4382
4383 proto = scope.engine->newObject();
4384 proto->defineDefaultProperty(QStringLiteral("addColorStop"), code: QQuickContext2DStyle::gradient_proto_addColorStop, argumentCount: 0);
4385 gradientProto = proto;
4386
4387 proto = scope.engine->newObject();
4388 proto->defineAccessorProperty(name: scope.engine->id_length(), getter: QQuickJSContext2DPixelData::proto_get_length, setter: nullptr);
4389 pixelArrayProto = proto;
4390}
4391
4392QQuickContext2DEngineData::~QQuickContext2DEngineData()
4393{
4394}
4395
4396void QQuickContext2D::popState()
4397{
4398 if (m_stateStack.isEmpty())
4399 return;
4400
4401 QQuickContext2D::State newState = m_stateStack.pop();
4402
4403 if (state.matrix != newState.matrix)
4404 buffer()->updateMatrix(matrix: newState.matrix);
4405
4406 if (newState.globalAlpha != state.globalAlpha)
4407 buffer()->setGlobalAlpha(newState.globalAlpha);
4408
4409 if (newState.globalCompositeOperation != state.globalCompositeOperation)
4410 buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
4411
4412 if (newState.fillStyle != state.fillStyle)
4413 buffer()->setFillStyle(style: newState.fillStyle);
4414
4415 if (newState.strokeStyle != state.strokeStyle)
4416 buffer()->setStrokeStyle(style: newState.strokeStyle);
4417
4418 if (newState.lineWidth != state.lineWidth)
4419 buffer()->setLineWidth(newState.lineWidth);
4420
4421 if (newState.lineCap != state.lineCap)
4422 buffer()->setLineCap(newState.lineCap);
4423
4424 if (newState.lineJoin != state.lineJoin)
4425 buffer()->setLineJoin(newState.lineJoin);
4426
4427 if (newState.miterLimit != state.miterLimit)
4428 buffer()->setMiterLimit(newState.miterLimit);
4429
4430 if (newState.clip != state.clip || newState.clipPath != state.clipPath)
4431 buffer()->clip(enabled: newState.clip, path: newState.clipPath);
4432
4433 if (newState.shadowBlur != state.shadowBlur)
4434 buffer()->setShadowBlur(newState.shadowBlur);
4435
4436 if (newState.shadowColor != state.shadowColor)
4437 buffer()->setShadowColor(newState.shadowColor);
4438
4439 if (newState.shadowOffsetX != state.shadowOffsetX)
4440 buffer()->setShadowOffsetX(newState.shadowOffsetX);
4441
4442 if (newState.shadowOffsetY != state.shadowOffsetY)
4443 buffer()->setShadowOffsetY(newState.shadowOffsetY);
4444
4445 if (newState.lineDash != state.lineDash)
4446 buffer()->setLineDash(newState.lineDash);
4447
4448 m_path = state.matrix.map(p: m_path);
4449 state = newState;
4450 m_path = state.matrix.inverted().map(p: m_path);
4451}
4452void QQuickContext2D::pushState()
4453{
4454 m_stateStack.push(t: state);
4455}
4456
4457void QQuickContext2D::reset()
4458{
4459 QQuickContext2D::State newState;
4460
4461 m_path = QPainterPath();
4462
4463 newState.clipPath.setFillRule(Qt::WindingFill);
4464
4465 m_stateStack.clear();
4466 m_stateStack.push(t: newState);
4467 popState();
4468 m_buffer->clearRect(r: QRectF(0, 0, m_canvas->width(), m_canvas->height()));
4469}
4470
4471QV4::ExecutionEngine *QQuickContext2D::v4Engine() const
4472{
4473 return m_v4engine;
4474}
4475
4476void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
4477{
4478 if (m_v4engine != engine) {
4479 m_v4engine = engine;
4480
4481 if (m_v4engine == nullptr)
4482 return;
4483
4484 QQuickContext2DEngineData *ed = engineData(engine);
4485 QV4::Scope scope(engine);
4486 QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocate<QQuickJSContext2D>());
4487 QV4::ScopedObject p(scope, ed->contextPrototype.value());
4488 wrapper->setPrototypeOf(p);
4489 wrapper->d()->setContext(this);
4490 m_v4value = wrapper;
4491 }
4492}
4493
4494QT_END_NAMESPACE
4495
4496#include "moc_qquickcontext2d_p.cpp"
4497

source code of qtdeclarative/src/quick/items/context2d/qquickcontext2d.cpp