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