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 <private/qquicksvgparser_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 QQuickSvgParser::parsePathDataFast(path, r->d()->context()->m_path);
2297 }
2298 r->d()->context()->m_v4path.set(scope.engine, value);
2299 RETURN_UNDEFINED();
2300}
2301#endif // QT_CONFIG(quick_path)
2302
2303//rects
2304/*!
2305 \qmlmethod Context2D QtQuick::Context2D::clearRect(real x, real y, real w, real h)
2306
2307 Clears all pixels on the canvas in the rectangle specified by
2308 (\a x, \a y, \a w, \a h) to transparent black.
2309 */
2310QV4::ReturnedValue QQuickJSContext2DPrototype::method_clearRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2311{
2312 QV4::Scope scope(b);
2313 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2314 CHECK_CONTEXT(r)
2315
2316
2317 if (argc >= 4)
2318 r->d()->context()->clearRect(argv[0].toNumber(),
2319 argv[1].toNumber(),
2320 argv[2].toNumber(),
2321 argv[3].toNumber());
2322
2323 RETURN_RESULT(*thisObject);
2324
2325}
2326/*!
2327 \qmlmethod Context2D QtQuick::Context2D::fillRect(real x, real y, real w, real h)
2328
2329 Paints a rectangular area specified by (\a x, \a y, \a w, \a h) using fillStyle.
2330
2331 \sa fillStyle
2332 */
2333QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2334{
2335 QV4::Scope scope(b);
2336 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2337 CHECK_CONTEXT(r)
2338
2339 if (argc >= 4)
2340 r->d()->context()->fillRect(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2341 RETURN_RESULT(*thisObject);
2342
2343}
2344
2345/*!
2346 \qmlmethod Context2D QtQuick::Context2D::strokeRect(real x, real y, real w, real h)
2347
2348 Strokes the path of the rectangle specified by (\a x, \a y, \a w, \a h) using
2349 strokeStyle, lineWidth, lineJoin, and (if appropriate) miterLimit attributes.
2350
2351 \sa strokeStyle, lineWidth, lineJoin, miterLimit
2352 */
2353QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2354{
2355 QV4::Scope scope(b);
2356 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2357 CHECK_CONTEXT(r)
2358
2359 if (argc >= 4)
2360 r->d()->context()->strokeRect(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2361
2362 RETURN_RESULT(*thisObject);
2363
2364}
2365
2366// Complex shapes (paths) API
2367/*!
2368 \qmlmethod Context2D QtQuick::Context2D::arc(real x, real y, real radius,
2369 real startAngle, real endAngle, bool anticlockwise)
2370
2371 Adds an arc to the current subpath that lies on the circumference of the
2372 circle whose center is at the point (\a x, \a y) and whose radius is
2373 \a radius.
2374
2375 Both \a startAngle and \a endAngle are measured from the x-axis in radians.
2376
2377 \image qml-item-canvas-arc.png {Circle and arc showing center point
2378 (x,y) and radius}
2379
2380 \image qml-item-canvas-startAngle.png {Four arcs showing different
2381 endAngle values from π/2 to 2π, all starting at angle 0}
2382
2383 The \a anticlockwise parameter is \c false for each arc in the figure above
2384 because they are all drawn in the clockwise direction.
2385
2386 \sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
2387 Context Standard for arc()}
2388*/
2389QV4::ReturnedValue QQuickJSContext2DPrototype::method_arc(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2390{
2391 QV4::Scope scope(b);
2392 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2393 CHECK_CONTEXT(r)
2394
2395 if (argc >= 5) {
2396 bool antiClockwise = false;
2397
2398 if (argc == 6)
2399 antiClockwise = argv[5].toBoolean();
2400
2401 qreal radius = argv[2].toNumber();
2402
2403 if (qt_is_finite(radius) && radius < 0)
2404 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
2405
2406 r->d()->context()->arc(argv[0].toNumber(),
2407 argv[1].toNumber(),
2408 radius,
2409 argv[3].toNumber(),
2410 argv[4].toNumber(),
2411 antiClockwise);
2412 }
2413
2414 RETURN_RESULT(*thisObject);
2415
2416}
2417
2418/*!
2419 \qmlmethod Context2D QtQuick::Context2D::arcTo(real x1, real y1, real x2,
2420 real y2, real radius)
2421
2422 Adds an arc with the given control points and radius to the current subpath,
2423 connected to the previous point by a straight line. To draw an arc, you
2424 begin with the same steps you followed to create a line:
2425
2426 \list
2427 \li Call the beginPath() method to set a new path.
2428 \li Call the moveTo(\c x, \c y) method to set your starting position on the
2429 canvas at the point (\c x, \c y).
2430 \li To draw an arc or circle, call the arcTo(\a x1, \a y1, \a x2, \a y2,
2431 \a radius) method. This adds an arc with starting point (\a x1, \a y1),
2432 ending point (\a x2, \a y2), and \a radius to the current subpath and
2433 connects it to the previous subpath by a straight line.
2434 \endlist
2435
2436 \image qml-item-canvas-arcTo.png {Arc construction showing control
2437 points (x1,y1), (x2,y2) and radius for tangent arc}
2438
2439 \sa arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C's 2D
2440 Context Standard for arcTo()}
2441*/
2442QV4::ReturnedValue QQuickJSContext2DPrototype::method_arcTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2443{
2444 QV4::Scope scope(b);
2445 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2446 CHECK_CONTEXT(r)
2447
2448 if (argc >= 5) {
2449 qreal radius = argv[4].toNumber();
2450
2451 if (qt_is_finite(radius) && radius < 0)
2452 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
2453
2454 r->d()->context()->arcTo(argv[0].toNumber(),
2455 argv[1].toNumber(),
2456 argv[2].toNumber(),
2457 argv[3].toNumber(),
2458 radius);
2459 }
2460
2461 RETURN_RESULT(*thisObject);
2462
2463}
2464
2465/*!
2466 \qmlmethod Context2D QtQuick::Context2D::beginPath()
2467
2468 Resets the current path to a new path.
2469 */
2470QV4::ReturnedValue QQuickJSContext2DPrototype::method_beginPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2471{
2472 QV4::Scope scope(b);
2473 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2474 CHECK_CONTEXT(r)
2475
2476 r->d()->context()->beginPath();
2477
2478 RETURN_RESULT(*thisObject);
2479
2480}
2481
2482/*!
2483 \qmlmethod Context2D QtQuick::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
2484
2485 Adds a cubic bezier curve between the current position and the given endPoint using the control points specified by (\a {cp1x}, \a {cp1y}),
2486 and (\a {cp2x}, \a {cp2y}).
2487 After the curve is added, the current position is updated to be at the end point (\a {x}, \a {y}) of the curve.
2488 The following code produces the path shown below:
2489
2490 \code
2491 ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
2492 ctx.lineWidth = 1;
2493 ctx.beginPath();
2494 ctx.moveTo(20, 0);//start point
2495 ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
2496 ctx.stroke();
2497 \endcode
2498
2499 \image qml-item-canvas-bezierCurveTo.png {Cubic bezier curve forming
2500 a smooth downward arc}
2501
2502 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
2503 \sa {https://web.archive.org/web/20130505222636if_/http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
2504 */
2505QV4::ReturnedValue QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2506{
2507 QV4::Scope scope(b);
2508 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2509 CHECK_CONTEXT(r)
2510
2511 if (argc >= 6) {
2512 qreal cp1x = argv[0].toNumber();
2513 qreal cp1y = argv[1].toNumber();
2514 qreal cp2x = argv[2].toNumber();
2515 qreal cp2y = argv[3].toNumber();
2516 qreal x = argv[4].toNumber();
2517 qreal y = argv[5].toNumber();
2518
2519 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))
2520 RETURN_UNDEFINED();
2521
2522 r->d()->context()->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
2523 }
2524 RETURN_RESULT(*thisObject);
2525}
2526
2527/*!
2528 \qmlmethod Context2D QtQuick::Context2D::clip()
2529
2530 Creates the clipping region from the current path.
2531 Any parts of the shape outside the clipping path are not displayed.
2532 To create a complex shape using the \c clip() method:
2533
2534 \list 1
2535 \li Call the \c{context.beginPath()} method to set the clipping path.
2536 \li Define the clipping path by calling any combination of the \c{lineTo},
2537 \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
2538 \li Call the \c{context.clip()} method.
2539 \endlist
2540
2541 The new shape displays. The following shows how a clipping path can
2542 modify how an image displays:
2543
2544 \image qml-item-canvas-clip-complex.png {Image before and after
2545 clipping to a star shape, showing only the clipped region}
2546 \sa beginPath()
2547 \sa closePath()
2548 \sa stroke()
2549 \sa fill()
2550 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
2551 */
2552QV4::ReturnedValue QQuickJSContext2DPrototype::method_clip(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2553{
2554 QV4::Scope scope(b);
2555 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2556 CHECK_CONTEXT(r)
2557
2558 r->d()->context()->clip();
2559 RETURN_RESULT(*thisObject);
2560}
2561
2562/*!
2563 \qmlmethod Context2D QtQuick::Context2D::closePath()
2564 Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
2565 The current point of the new path is the previous subpath's first point.
2566
2567 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
2568 */
2569QV4::ReturnedValue QQuickJSContext2DPrototype::method_closePath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2570{
2571 QV4::Scope scope(b);
2572 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2573 CHECK_CONTEXT(r)
2574
2575 r->d()->context()->closePath();
2576
2577 RETURN_RESULT(*thisObject);
2578}
2579
2580/*!
2581 \qmlmethod Context2D QtQuick::Context2D::fill()
2582
2583 Fills the subpaths with the current fill style.
2584
2585 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
2586
2587 \sa fillStyle
2588 */
2589QV4::ReturnedValue QQuickJSContext2DPrototype::method_fill(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2590{
2591 QV4::Scope scope(b);
2592 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2593 CHECK_CONTEXT(r);
2594 r->d()->context()->fill();
2595 RETURN_RESULT(*thisObject);
2596}
2597
2598/*!
2599 \qmlmethod Context2D QtQuick::Context2D::lineTo(real x, real y)
2600
2601 Draws a line from the current position to the point at (\a x, \a y).
2602 */
2603QV4::ReturnedValue QQuickJSContext2DPrototype::method_lineTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2604{
2605 QV4::Scope scope(b);
2606 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2607 CHECK_CONTEXT(r)
2608
2609 if (argc >= 2) {
2610 qreal x = argv[0].toNumber();
2611 qreal y = argv[1].toNumber();
2612
2613 if (!qt_is_finite(x) || !qt_is_finite(y))
2614 RETURN_UNDEFINED();
2615
2616 r->d()->context()->lineTo(x, y);
2617 }
2618
2619 RETURN_RESULT(*thisObject);
2620}
2621
2622/*!
2623 \qmlmethod Context2D QtQuick::Context2D::moveTo(real x, real y)
2624
2625 Creates a new subpath with a point at (\a x, \a y).
2626 */
2627QV4::ReturnedValue QQuickJSContext2DPrototype::method_moveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2628{
2629 QV4::Scope scope(b);
2630 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2631 CHECK_CONTEXT(r)
2632
2633 if (argc >= 2) {
2634 qreal x = argv[0].toNumber();
2635 qreal y = argv[1].toNumber();
2636
2637 if (!qt_is_finite(x) || !qt_is_finite(y))
2638 RETURN_UNDEFINED();
2639 r->d()->context()->moveTo(x, y);
2640 }
2641
2642 RETURN_RESULT(*thisObject);
2643}
2644
2645/*!
2646 \qmlmethod Context2D QtQuick::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
2647
2648 Adds a quadratic bezier curve between the current point and the endpoint
2649 (\a x, \a y) with the control point specified by (\a cpx, \a cpy).
2650
2651 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for quadraticCurveTo}
2652 */
2653QV4::ReturnedValue QQuickJSContext2DPrototype::method_quadraticCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2654{
2655 QV4::Scope scope(b);
2656 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2657 CHECK_CONTEXT(r)
2658
2659 if (argc >= 4) {
2660 qreal cpx = argv[0].toNumber();
2661 qreal cpy = argv[1].toNumber();
2662 qreal x = argv[2].toNumber();
2663 qreal y = argv[3].toNumber();
2664
2665 if (!qt_is_finite(cpx) || !qt_is_finite(cpy) || !qt_is_finite(x) || !qt_is_finite(y))
2666 RETURN_UNDEFINED();
2667
2668 r->d()->context()->quadraticCurveTo(cpx, cpy, x, y);
2669 }
2670
2671 RETURN_RESULT(*thisObject);
2672}
2673
2674/*!
2675 \qmlmethod Context2D QtQuick::Context2D::rect(real x, real y, real w, real h)
2676
2677 Adds a rectangle at position (\a x, \a y), with the given width \a w and
2678 height \a h, as a closed subpath.
2679 */
2680QV4::ReturnedValue QQuickJSContext2DPrototype::method_rect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2681{
2682 QV4::Scope scope(b);
2683 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2684 CHECK_CONTEXT(r)
2685
2686 if (argc >= 4)
2687 r->d()->context()->rect(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2688 RETURN_RESULT(*thisObject);
2689
2690}
2691
2692/*!
2693 \qmlmethod Context2D QtQuick::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius)
2694
2695 Adds a rounded-corner rectangle, specified by (\a x, \a y, \a w, \a h), to the path.
2696 The \a xRadius and \a yRadius arguments specify the radius of the
2697 ellipses defining the corners of the rounded rectangle.
2698 */
2699QV4::ReturnedValue QQuickJSContext2DPrototype::method_roundedRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2700{
2701 QV4::Scope scope(b);
2702 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2703 CHECK_CONTEXT(r)
2704
2705 if (argc >= 6)
2706 r->d()->context()->roundedRect(argv[0].toNumber()
2707 , argv[1].toNumber()
2708 , argv[2].toNumber()
2709 , argv[3].toNumber()
2710 , argv[4].toNumber()
2711 , argv[5].toNumber());
2712 RETURN_RESULT(*thisObject);
2713
2714}
2715
2716/*!
2717 \qmlmethod Context2D QtQuick::Context2D::ellipse(real x, real y, real w, real h)
2718
2719 Creates an ellipse within the bounding rectangle defined by its top-left
2720 corner at (\a x, \a y), width \a w and height \a h, and adds it to the
2721 path as a closed subpath.
2722
2723 The ellipse is composed of a clockwise curve, starting and finishing at
2724 zero degrees (the 3 o'clock position).
2725 */
2726QV4::ReturnedValue QQuickJSContext2DPrototype::method_ellipse(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2727{
2728 QV4::Scope scope(b);
2729 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2730 CHECK_CONTEXT(r)
2731
2732 if (argc >= 4)
2733 r->d()->context()->ellipse(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2734
2735 RETURN_RESULT(*thisObject);
2736
2737}
2738
2739/*!
2740 \qmlmethod Context2D QtQuick::Context2D::text(string text, real x, real y)
2741
2742 Adds the given \a text to the path as a set of closed subpaths created
2743 from the current context font supplied.
2744
2745 The subpaths are positioned so that the left end of the text's baseline
2746 lies at the point specified by (\a x, \a y).
2747 */
2748QV4::ReturnedValue QQuickJSContext2DPrototype::method_text(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2749{
2750 QV4::Scope scope(b);
2751 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2752 CHECK_CONTEXT(r)
2753
2754 if (argc >= 3) {
2755 qreal x = argv[1].toNumber();
2756 qreal y = argv[2].toNumber();
2757
2758 if (!qt_is_finite(x) || !qt_is_finite(y))
2759 RETURN_UNDEFINED();
2760 r->d()->context()->text(argv[0].toQStringNoThrow(), x, y);
2761 }
2762
2763 RETURN_RESULT(*thisObject);
2764}
2765
2766/*!
2767 \qmlmethod Context2D QtQuick::Context2D::stroke()
2768
2769 Strokes the subpaths with the current stroke style.
2770
2771 \sa strokeStyle, {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
2772 */
2773QV4::ReturnedValue QQuickJSContext2DPrototype::method_stroke(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2774{
2775 QV4::Scope scope(b);
2776 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2777 CHECK_CONTEXT(r)
2778
2779 r->d()->context()->stroke();
2780 RETURN_RESULT(*thisObject);
2781
2782}
2783
2784/*!
2785 \qmlmethod bool QtQuick::Context2D::isPointInPath(real x, real y)
2786
2787 Returns \c true if the point (\a x, \a y) is in the current path.
2788
2789 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
2790 */
2791QV4::ReturnedValue QQuickJSContext2DPrototype::method_isPointInPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2792{
2793 QV4::Scope scope(b);
2794 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2795 CHECK_CONTEXT(r)
2796
2797 bool pointInPath = false;
2798 if (argc >= 2)
2799 pointInPath = r->d()->context()->isPointInPath(argv[0].toNumber(), argv[1].toNumber());
2800 RETURN_RESULT(QV4::Value::fromBoolean(pointInPath).asReturnedValue());
2801}
2802
2803QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawFocusRing(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2804{
2805 QV4::Scope scope(b);
2806 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
2807}
2808
2809QV4::ReturnedValue QQuickJSContext2DPrototype::method_setCaretSelectionRect(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2810{
2811 QV4::Scope scope(b);
2812 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
2813}
2814
2815QV4::ReturnedValue QQuickJSContext2DPrototype::method_caretBlinkRate(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2816{
2817 QV4::Scope scope(b);
2818 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
2819}
2820
2821/*!
2822 \qmlproperty string QtQuick::Context2D::font
2823 Holds the current font settings.
2824
2825 A subset of the
2826 \l {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
2827 is supported:
2828
2829 \list
2830 \li font-style (optional):
2831 normal | italic | oblique
2832 \li font-variant (optional): normal | small-caps
2833 \li font-weight (optional): normal | bold | 1 ... 1000
2834 \li font-size: Npx | Npt (where N is a positive number)
2835 \li font-family: See \l {http://www.w3.org/TR/CSS2/fonts.html#propdef-font-family}
2836 \endlist
2837
2838 \note The font-size and font-family properties are mandatory and must be in
2839 the order they are shown in above. In addition, a font family with spaces in
2840 its name must be quoted.
2841
2842 The default font value is "10px sans-serif".
2843 */
2844QV4::ReturnedValue QQuickJSContext2D::method_get_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2845{
2846 QV4::Scope scope(b);
2847 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2848 CHECK_CONTEXT(r)
2849
2850 RETURN_RESULT(scope.engine->newString(r->d()->context()->state.font.toString()));
2851}
2852
2853QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2854{
2855 QV4::Scope scope(b);
2856 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2858
2859 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2860 if (scope.hasException())
2861 RETURN_UNDEFINED();
2862 QFont font = qt_font_from_string(s->toQString(), r->d()->context()->state.font);
2863 if (font != r->d()->context()->state.font) {
2864 r->d()->context()->state.font = font;
2865 }
2866 RETURN_UNDEFINED();
2867}
2868
2869/*!
2870 \qmlproperty string QtQuick::Context2D::textAlign
2871
2872 Holds the current text alignment settings. The possible values are:
2873
2874 \value "start" (default) Align to the start edge of the text (left side in
2875 left-to-right text, right side in right-to-left text).
2876 \value "end" Align to the end edge of the text (right side in left-to-right
2877 text, left side in right-to-left text).
2878 \value "left" Qt::AlignLeft
2879 \value "right" Qt::AlignRight
2880 \value "center" Qt::AlignHCenter
2881
2882 Other values are ignored.
2883*/
2884QV4::ReturnedValue QQuickJSContext2D::method_get_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2885{
2886 QV4::Scope scope(b);
2887 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2888 CHECK_CONTEXT(r)
2889
2890 switch (r->d()->context()->state.textAlign) {
2892 RETURN_RESULT(scope.engine->newString(QStringLiteral("end")));
2894 RETURN_RESULT(scope.engine->newString(QStringLiteral("left")));
2896 RETURN_RESULT(scope.engine->newString(QStringLiteral("right")));
2898 RETURN_RESULT(scope.engine->newString(QStringLiteral("center")));
2900 default:
2901 break;
2902 }
2903 RETURN_RESULT(scope.engine->newString(QStringLiteral("start")));
2904}
2905
2906QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2907{
2908 QV4::Scope scope(b);
2909 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2911
2912 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2913 if (scope.hasException())
2914 RETURN_UNDEFINED();
2915 QString textAlign = s->toQString();
2916
2918 if (textAlign == QLatin1String("start"))
2920 else if (textAlign == QLatin1String("end"))
2922 else if (textAlign == QLatin1String("left"))
2924 else if (textAlign == QLatin1String("right"))
2926 else if (textAlign == QLatin1String("center"))
2928 else
2929 RETURN_UNDEFINED();
2930
2931 if (ta != r->d()->context()->state.textAlign)
2932 r->d()->context()->state.textAlign = ta;
2933
2934 RETURN_UNDEFINED();
2935}
2936
2937/*!
2938 \qmlproperty string QtQuick::Context2D::textBaseline
2939
2940 Holds the current baseline alignment settings. The possible values are:
2941
2942 \value "top" The top of the em square
2943 \value "hanging" The hanging baseline
2944 \value "middle" The middle of the em square
2945 \value "alphabetic" (default) The alphabetic baseline
2946 \value "ideographic" The ideographic-under baseline
2947 \value "bottom" The bottom of the em square
2948
2949 Other values are ignored. The default value is "alphabetic".
2950*/
2951QV4::ReturnedValue QQuickJSContext2D::method_get_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2952{
2953 QV4::Scope scope(b);
2954 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2955 CHECK_CONTEXT(r)
2956
2957 switch (r->d()->context()->state.textBaseline) {
2958 case QQuickContext2D::Hanging:
2959 RETURN_RESULT(scope.engine->newString(QStringLiteral("hanging")));
2960 case QQuickContext2D::Top:
2961 RETURN_RESULT(scope.engine->newString(QStringLiteral("top")));
2962 case QQuickContext2D::Bottom:
2963 RETURN_RESULT(scope.engine->newString(QStringLiteral("bottom")));
2964 case QQuickContext2D::Middle:
2965 RETURN_RESULT(scope.engine->newString(QStringLiteral("middle")));
2966 case QQuickContext2D::Alphabetic:
2967 default:
2968 break;
2969 }
2970 RETURN_RESULT(scope.engine->newString(QStringLiteral("alphabetic")));
2971}
2972
2973QV4::ReturnedValue QQuickJSContext2D::method_set_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2974{
2975 QV4::Scope scope(b);
2976 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2978 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2979 if (scope.hasException())
2980 RETURN_UNDEFINED();
2981 QString textBaseline = s->toQString();
2982
2983 QQuickContext2D::TextBaseLineType tb;
2984 if (textBaseline == QLatin1String("alphabetic"))
2985 tb = QQuickContext2D::Alphabetic;
2986 else if (textBaseline == QLatin1String("hanging"))
2987 tb = QQuickContext2D::Hanging;
2988 else if (textBaseline == QLatin1String("top"))
2989 tb = QQuickContext2D::Top;
2990 else if (textBaseline == QLatin1String("bottom"))
2991 tb = QQuickContext2D::Bottom;
2992 else if (textBaseline == QLatin1String("middle"))
2993 tb = QQuickContext2D::Middle;
2994 else
2995 RETURN_UNDEFINED();
2996
2997 if (tb != r->d()->context()->state.textBaseline)
2998 r->d()->context()->state.textBaseline = tb;
2999
3000 RETURN_UNDEFINED();
3001}
3002
3003/*!
3004 \qmlmethod Context2D QtQuick::Context2D::fillText(text, x, y)
3005
3006 Fills the specified \a text at the given position (\a x, \a y).
3007
3008 \sa font
3009 \sa textAlign
3010 \sa textBaseline
3011 \sa strokeText
3012 */
3013QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3014{
3015 QV4::Scope scope(b);
3016 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3017 CHECK_CONTEXT(r)
3018
3019 if (argc >= 3) {
3020 qreal x = argv[1].toNumber();
3021 qreal y = argv[2].toNumber();
3022 if (!qt_is_finite(x) || !qt_is_finite(y))
3023 RETURN_UNDEFINED();
3024 QPainterPath textPath = r->d()->context()->createTextGlyphs(x, y, argv[0].toQStringNoThrow());
3025 r->d()->context()->buffer()->fill(textPath);
3026 }
3027
3028 RETURN_RESULT(*thisObject);
3029}
3030/*!
3031 \qmlmethod Context2D QtQuick::Context2D::strokeText(text, x, y)
3032
3033 Strokes the given \a text at a position specified by (\a x, \a y).
3034
3035 \sa font
3036 \sa textAlign
3037 \sa textBaseline
3038 \sa fillText
3039*/
3040QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3041{
3042 QV4::Scope scope(b);
3043 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3044 CHECK_CONTEXT(r)
3045
3046 if (argc >= 3)
3047 r->d()->context()->drawText(argv[0].toQStringNoThrow(), argv[1].toNumber(), argv[2].toNumber(), false);
3048
3049 RETURN_RESULT(*thisObject);
3050}
3051
3052/*!
3053 \qmlmethod var QtQuick::Context2D::measureText(text)
3054
3055 Returns an object with a \c width property, whose value is equivalent to
3056 calling QFontMetrics::horizontalAdvance() with the given \a text in the
3057 current font.
3058 */
3059QV4::ReturnedValue QQuickJSContext2DPrototype::method_measureText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3060{
3061 QV4::Scope scope(b);
3062 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3063 CHECK_CONTEXT(r)
3064
3065 if (argc >= 1) {
3066 QFontMetrics fm(r->d()->context()->state.font);
3067 uint width = fm.horizontalAdvance(argv[0].toQStringNoThrow());
3068 QV4::ScopedObject tm(scope, scope.engine->newObject());
3069 tm->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("width"))).getPointer(),
3070 QV4::ScopedValue(scope, QV4::Value::fromDouble(width)));
3071 RETURN_RESULT(*tm);
3072 }
3073 RETURN_UNDEFINED();
3074}
3075
3076// drawing images
3077/*!
3078 \qmlmethod void QtQuick::Context2D::drawImage(variant image, real dx, real dy)
3079 Draws the given \a image on the canvas at position (\a dx, \a dy).
3080 Note:
3081 The \a image type can be an Image item, an image url or a CanvasImageData object.
3082 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3083 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3084 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3085
3086 \sa CanvasImageData
3087 \sa Image
3088 \sa Canvas::loadImage
3089 \sa Canvas::isImageLoaded
3090 \sa Canvas::imageLoaded
3091
3092 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3093 */
3094/*!
3095 \qmlmethod void QtQuick::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
3096 This is an overloaded function.
3097 Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
3098 height \a dh.
3099
3100 Note:
3101 The \a image type can be an Image item, an image url or a CanvasImageData object.
3102 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3103 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3104 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3105
3106 \sa CanvasImageData
3107 \sa Image
3108 \sa Canvas::loadImage()
3109 \sa Canvas::isImageLoaded
3110 \sa Canvas::imageLoaded
3111
3112 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3113 */
3114/*!
3115 \qmlmethod void QtQuick::Context2D::drawImage(variant image, real sx, real sy, real sw, real sh, real dx, real dy, real dw, real dh)
3116 This is an overloaded function.
3117 Draws the given item as \a image from source point (\a sx, \a sy) and source width \a sw, source height \a sh
3118 onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
3119
3120
3121 Note:
3122 The \a image type can be an Image or Canvas item, an image url or a CanvasImageData object.
3123 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3124 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3125 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3126
3127 \sa CanvasImageData
3128 \sa Image
3129 \sa Canvas::loadImage()
3130 \sa Canvas::isImageLoaded
3131 \sa Canvas::imageLoaded
3132
3133 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3134*/
3135QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3136{
3137 QV4::Scope scope(b);
3138 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3139 CHECK_CONTEXT(r)
3140
3141 qreal sx, sy, sw, sh, dx, dy, dw, dh;
3142
3143 if (!argc)
3144 RETURN_UNDEFINED();
3145
3146 //FIXME:This function should be moved to QQuickContext2D::drawImage(...)
3147 if (!r->d()->context()->state.invertibleCTM)
3148 RETURN_UNDEFINED();
3149
3150 QQmlRefPointer<QQuickCanvasPixmap> pixmap;
3151
3152 QV4::ScopedValue arg(scope, argv[0]);
3153 if (arg->isString()) {
3154 QUrl url(arg->toQString());
3155 if (!url.isValid())
3156 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3157
3158 pixmap = r->d()->context()->createPixmap(url);
3159 } else if (arg->isObject()) {
3160 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, arg);
3161 if (!!qobjectWrapper) {
3162 if (QQuickImage *imageItem = qobject_cast<QQuickImage*>(qobjectWrapper->object())) {
3163 pixmap = r->d()->context()->createPixmap(imageItem->source());
3164 } else if (QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(qobjectWrapper->object())) {
3165 QImage img = canvas->toImage();
3166 if (!img.isNull())
3167 pixmap.adopt(new QQuickCanvasPixmap(img));
3168 } else {
3169 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3170 }
3171 } else {
3172 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg);
3173 if (!!imageData) {
3174 QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3175 if (pix && !pix->d()->image->isNull()) {
3176 pixmap.adopt(new QQuickCanvasPixmap(*pix->d()->image));
3177 } else {
3178 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3179 }
3180 } else {
3181 QUrl url(arg->toQStringNoThrow());
3182 if (url.isValid())
3183 pixmap = r->d()->context()->createPixmap(url);
3184 else
3185 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3186 }
3187 }
3188 } else {
3189 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3190 }
3191
3192 if (pixmap.isNull() || !pixmap->isValid())
3193 RETURN_UNDEFINED();
3194
3195 if (argc >= 9) {
3196 sx = argv[1].toNumber();
3197 sy = argv[2].toNumber();
3198 sw = argv[3].toNumber();
3199 sh = argv[4].toNumber();
3200 dx = argv[5].toNumber();
3201 dy = argv[6].toNumber();
3202 dw = argv[7].toNumber();
3203 dh = argv[8].toNumber();
3204 } else if (argc >= 5) {
3205 sx = 0;
3206 sy = 0;
3207 sw = pixmap->width();
3208 sh = pixmap->height();
3209 dx = argv[1].toNumber();
3210 dy = argv[2].toNumber();
3211 dw = argv[3].toNumber();
3212 dh = argv[4].toNumber();
3213 } else if (argc >= 3) {
3214 dx = argv[1].toNumber();
3215 dy = argv[2].toNumber();
3216 sx = 0;
3217 sy = 0;
3218 sw = pixmap->width();
3219 sh = pixmap->height();
3220 dw = sw;
3221 dh = sh;
3222 } else {
3223 RETURN_UNDEFINED();
3224 }
3225
3226 if (!qt_is_finite(sx)
3227 || !qt_is_finite(sy)
3228 || !qt_is_finite(sw)
3229 || !qt_is_finite(sh)
3230 || !qt_is_finite(dx)
3231 || !qt_is_finite(dy)
3232 || !qt_is_finite(dw)
3233 || !qt_is_finite(dh))
3234 RETURN_UNDEFINED();
3235
3236 if (sx < 0
3237 || sy < 0
3238 || sw == 0
3239 || sh == 0
3240 || sx + sw > pixmap->width()
3241 || sy + sh > pixmap->height()
3242 || sx + sw < 0 || sy + sh < 0) {
3243 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error");
3244 }
3245
3246 r->d()->context()->buffer()->drawPixmap(pixmap, QRectF(sx, sy, sw, sh), QRectF(dx, dy, dw, dh));
3247
3248 RETURN_RESULT(*thisObject);
3249}
3250
3251// pixel manipulation
3252/*!
3253 \qmltype CanvasImageData
3254 \inqmlmodule QtQuick
3255 \ingroup qtquick-canvas
3256 \brief Contains image pixel data in RGBA order.
3257
3258 The CanvasImageData object holds the image pixel data.
3259
3260 The CanvasImageData object has the actual dimensions of the data stored in
3261 this object and holds the one-dimensional array containing the data in RGBA order,
3262 as integers in the range 0 to 255.
3263
3264 \sa width
3265 \sa height
3266 \sa data
3267 \sa Context2D::createImageData()
3268 \sa Context2D::getImageData()
3269 \sa Context2D::putImageData()
3270 */
3271/*!
3272 \qmlproperty int QtQuick::CanvasImageData::width
3273 Holds the actual width dimension of the data in the ImageData object, in device pixels.
3274 */
3275QV4::ReturnedValue QQuickJSContext2DImageData::method_get_width(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3276{
3277 QV4::Scope scope(b);
3278 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3279 if (!imageData)
3280 THROW_TYPE_ERROR();
3281 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3282 int width = r ? r->d()->image->width() : 0;
3283 RETURN_RESULT(QV4::Encode(width));
3284}
3285
3286/*!
3287 \qmlproperty int QtQuick::CanvasImageData::height
3288 Holds the actual height dimension of the data in the ImageData object, in device pixels.
3289 */
3290QV4::ReturnedValue QQuickJSContext2DImageData::method_get_height(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3291{
3292 QV4::Scope scope(b);
3293 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3294 if (!imageData)
3295 THROW_TYPE_ERROR();
3296 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3297 int height = r ? r->d()->image->height() : 0;
3298 RETURN_RESULT(QV4::Encode(height));
3299}
3300
3301/*!
3302 \qmlproperty CanvasPixelArray QtQuick::CanvasImageData::data
3303 Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
3304 */
3305QV4::ReturnedValue QQuickJSContext2DImageData::method_get_data(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3306{
3307 QV4::Scope scope(b);
3308 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3309 if (!imageData)
3310 THROW_TYPE_ERROR();
3311 RETURN_RESULT(imageData->d()->pixelData);
3312}
3313
3314/*!
3315 \qmltype CanvasPixelArray
3316 \inqmlmodule QtQuick
3317 \ingroup qtquick-canvas
3318 \brief Provides ordered and indexed access to the components of each pixel in image data.
3319
3320 The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
3321 The CanvasPixelArray can be accessed as normal Javascript array.
3322 \sa CanvasImageData
3323 \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
3324 */
3325
3326/*!
3327 \qmlproperty int QtQuick::CanvasPixelArray::length
3328 The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
3329 The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
3330 This property is read only.
3331*/
3332QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3333{
3334 QV4::Scope scope(b);
3335 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, thisObject->as<QQuickJSContext2DPixelData>());
3336 if (!r || r->d()->image->isNull())
3337 RETURN_UNDEFINED();
3338
3339 RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4));
3340}
3341
3342QV4::ReturnedValue QQuickJSContext2DPixelData::virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
3343{
3344 if (!id.isArrayIndex())
3345 return QV4::Object::virtualGet(m, id, receiver, hasProperty);
3346
3347 uint index = id.asArrayIndex();
3348 Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
3349 QV4::ExecutionEngine *v4 = static_cast<const QQuickJSContext2DPixelData *>(m)->engine();
3350 QV4::Scope scope(v4);
3351 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<const QQuickJSContext2DPixelData *>(m));
3352
3353 if (index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4)) {
3354 if (hasProperty)
3355 *hasProperty = true;
3356 const quint32 w = r->d()->image->width();
3357 const quint32 row = (index / 4) / w;
3358 const quint32 col = (index / 4) % w;
3359 const QRgb* pixel = reinterpret_cast<const QRgb*>(r->d()->image->constScanLine(row));
3360 pixel += col;
3361 switch (index % 4) {
3362 case 0:
3363 return QV4::Encode(qRed(*pixel));
3364 case 1:
3365 return QV4::Encode(qGreen(*pixel));
3366 case 2:
3367 return QV4::Encode(qBlue(*pixel));
3368 case 3:
3369 return QV4::Encode(qAlpha(*pixel));
3370 }
3371 }
3372
3373 if (hasProperty)
3374 *hasProperty = false;
3375 return QV4::Encode::undefined();
3376}
3377
3378bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, QV4::Value *receiver)
3379{
3380 if (!id.isArrayIndex())
3381 return Object::virtualPut(m, id, value, receiver);
3382
3383 Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
3384 QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine();
3385 QV4::Scope scope(v4);
3386 if (scope.hasException())
3387 return false;
3388
3389 uint index = id.asArrayIndex();
3390 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
3391
3392 const int v = value.toInt32();
3393 if (r && index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4) && v >= 0 && v <= 255) {
3394 const quint32 w = r->d()->image->width();
3395 const quint32 row = (index / 4) / w;
3396 const quint32 col = (index / 4) % w;
3397
3398 QRgb* pixel = reinterpret_cast<QRgb*>(r->d()->image->scanLine(row));
3399 pixel += col;
3400 switch (index % 4) {
3401 case 0:
3402 *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel));
3403 break;
3404 case 1:
3405 *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel));
3406 break;
3407 case 2:
3408 *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel));
3409 break;
3410 case 3:
3411 *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v);
3412 break;
3413 }
3414 return true;
3415 }
3416
3417 return false;
3418}
3419/*!
3420 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(real sw, real sh)
3421
3422 Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
3423*/
3424/*!
3425 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(CanvasImageData imageData)
3426
3427 Creates a CanvasImageData object with the same dimensions as the \a imageData argument.
3428*/
3429/*!
3430 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(Url imageUrl)
3431
3432 Creates a CanvasImageData object with the given image loaded from \a imageUrl.
3433
3434 \note The \a imageUrl must be already loaded before this function call,
3435 otherwise an empty CanvasImageData obect will be returned.
3436
3437 \sa Canvas::loadImage(), QtQuick::Canvas::unloadImage(),
3438 QtQuick::Canvas::isImageLoaded
3439 */
3440QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3441{
3442 QV4::Scope scope(b);
3443 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3444 CHECK_CONTEXT(r)
3445
3446 if (argc == 1) {
3447 QV4::ScopedValue arg0(scope, argv[0]);
3448 QV4::Scoped<QQuickJSContext2DImageData> imgData(scope, arg0);
3449 if (!!imgData) {
3450 QV4::Scoped<QQuickJSContext2DPixelData> pa(scope, imgData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3451 if (pa) {
3452 qreal w = pa->d()->image->width();
3453 qreal h = pa->d()->image->height();
3454 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, QImage()));
3455 }
3456 } else if (arg0->isString()) {
3457 QImage image = r->d()->context()->createPixmap(QUrl(arg0->toQStringNoThrow()))->image();
3458 RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, std::move(image)));
3459 }
3460 } else if (argc == 2) {
3461 qreal w = argv[0].toNumber();
3462 qreal h = argv[1].toNumber();
3463
3464 if (!qt_is_finite(w) || !qt_is_finite(h))
3465 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments");
3466
3467 if (w > 0 && h > 0)
3468 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, QImage()));
3469 else
3470 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments");
3471 }
3472 RETURN_UNDEFINED();
3473}
3474
3475/*!
3476 \qmlmethod CanvasImageData QtQuick::Context2D::getImageData(real x, real y, real w, real h)
3477
3478 Returns an CanvasImageData object containing the image data for the canvas
3479 rectangle specified by (\a x, \a y, \a w, \a h).
3480 */
3481QV4::ReturnedValue QQuickJSContext2DPrototype::method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3482{
3483 QV4::Scope scope(b);
3484 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3485 CHECK_CONTEXT(r)
3486
3487 if (argc >= 4) {
3488 qreal x = argv[0].toNumber();
3489 qreal y = argv[1].toNumber();
3490 qreal w = argv[2].toNumber();
3491 qreal h = argv[3].toNumber();
3492 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3493 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments");
3494
3495 if (w <= 0 || h <= 0)
3496 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
3497
3498 QImage image = r->d()->context()->canvas()->toImage(QRectF(x, y, w, h));
3499 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, std::move(image)));
3500 }
3501 RETURN_RESULT(QV4::Encode::null());
3502}
3503
3504/*!
3505 \qmlmethod void QtQuick::Context2D::putImageData(CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
3506
3507 Paints the data from the given \a imageData object onto the canvas at
3508 (\a dx, \a dy).
3509
3510 If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight)
3511 is provided, only the pixels from that rectangle are painted.
3512 */
3513QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3514{
3515 QV4::Scope scope(b);
3516 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3517 CHECK_CONTEXT(r)
3518 if (argc < 7)
3519 RETURN_UNDEFINED();
3520
3521 QV4::ScopedValue arg0(scope, argv[0]);
3522 if (!arg0->isObject())
3523 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch");
3524
3525 qreal dx = argv[1].toNumber();
3526 qreal dy = argv[2].toNumber();
3527 qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
3528
3529 if (!qt_is_finite(dx) || !qt_is_finite(dy))
3530 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
3531
3532 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg0);
3533 if (!imageData)
3534 RETURN_UNDEFINED();
3535
3536 QV4::Scoped<QQuickJSContext2DPixelData> pixelArray(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3537 if (pixelArray) {
3538 w = pixelArray->d()->image->width();
3539 h = pixelArray->d()->image->height();
3540
3541 if (argc == 7) {
3542 dirtyX = argv[3].toNumber();
3543 dirtyY = argv[4].toNumber();
3544 dirtyWidth = argv[5].toNumber();
3545 dirtyHeight = argv[6].toNumber();
3546
3547 if (!qt_is_finite(dirtyX) || !qt_is_finite(dirtyY) || !qt_is_finite(dirtyWidth) || !qt_is_finite(dirtyHeight))
3548 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
3549
3550
3551 if (dirtyWidth < 0) {
3552 dirtyX = dirtyX+dirtyWidth;
3553 dirtyWidth = -dirtyWidth;
3554 }
3555
3556 if (dirtyHeight < 0) {
3557 dirtyY = dirtyY+dirtyHeight;
3558 dirtyHeight = -dirtyHeight;
3559 }
3560
3561 if (dirtyX < 0) {
3562 dirtyWidth = dirtyWidth+dirtyX;
3563 dirtyX = 0;
3564 }
3565
3566 if (dirtyY < 0) {
3567 dirtyHeight = dirtyHeight+dirtyY;
3568 dirtyY = 0;
3569 }
3570
3571 if (dirtyX+dirtyWidth > w) {
3572 dirtyWidth = w - dirtyX;
3573 }
3574
3575 if (dirtyY+dirtyHeight > h) {
3576 dirtyHeight = h - dirtyY;
3577 }
3578
3579 if (dirtyWidth <=0 || dirtyHeight <= 0)
3580 RETURN_UNDEFINED();
3581 } else {
3582 dirtyX = 0;
3583 dirtyY = 0;
3584 dirtyWidth = w;
3585 dirtyHeight = h;
3586 }
3587
3588 QImage image = pixelArray->d()->image->copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
3589 r->d()->context()->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
3590 }
3591
3592 RETURN_RESULT(*thisObject);
3593}
3594
3595/*!
3596 \qmltype CanvasGradient
3597 \inqmlmodule QtQuick
3598 \since 5.0
3599 \ingroup qtquick-canvas
3600 \brief Provides an opaque CanvasGradient interface.
3601 */
3602
3603/*!
3604 \qmlmethod CanvasGradient QtQuick::CanvasGradient::addColorStop(real offset, string color)
3605
3606 Adds a color stop with the given \a color to the gradient at the given \a offset.
3607 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
3608
3609 For example:
3610
3611 \code
3612 var gradient = ctx.createLinearGradient(0, 0, 100, 100);
3613 gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
3614 gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1)');
3615 \endcode
3616 */
3617QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3618{
3619 QV4::Scope scope(b);
3620 QV4::Scoped<QQuickContext2DStyle> style(scope, thisObject->as<QQuickContext2DStyle>());
3621 if (!style)
3622 THROW_GENERIC_ERROR("Not a CanvasGradient object");
3623
3624 if (argc == 2) {
3625
3626 if (!style->d()->brush->gradient())
3627 THROW_GENERIC_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
3628 QGradient gradient = *(style->d()->brush->gradient());
3629 qreal pos = argv[0].toNumber();
3630 QColor color;
3631
3632 if (argv[1].as<Object>()) {
3633 color = QV4::ExecutionEngine::toVariant(
3634 argv[1], QMetaType::fromType<QColor>()).value<QColor>();
3635 } else {
3636 color = qt_color_from_string(argv[1]);
3637 }
3638 if (pos < 0.0 || pos > 1.0 || !qt_is_finite(pos)) {
3639 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range");
3640 }
3641
3642 if (color.isValid()) {
3643 gradient.setColorAt(pos, color);
3644 } else {
3645 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
3646 }
3647 *style->d()->brush = gradient;
3648 }
3649
3650 return thisObject->asReturnedValue();
3651}
3652
3653void QQuickContext2D::scale(qreal x, qreal y)
3654{
3655 if (!state.invertibleCTM)
3656 return;
3657
3658 if (!qt_is_finite(x) || !qt_is_finite(y))
3659 return;
3660
3661 QTransform newTransform = state.matrix;
3662 newTransform.scale(x, y);
3663
3664 if (!newTransform.isInvertible()) {
3665 state.invertibleCTM = false;
3666 return;
3667 }
3668
3669 state.matrix = newTransform;
3670 buffer()->updateMatrix(state.matrix);
3671 m_path = QTransform().scale(1.0 / x, 1.0 / y).map(m_path);
3672}
3673
3674void QQuickContext2D::rotate(qreal angle)
3675{
3676 if (!state.invertibleCTM)
3677 return;
3678
3679 if (!qt_is_finite(angle))
3680 return;
3681
3682 QTransform newTransform =state.matrix;
3683 newTransform.rotate(qRadiansToDegrees(angle));
3684
3685 if (!newTransform.isInvertible()) {
3686 state.invertibleCTM = false;
3687 return;
3688 }
3689
3690 state.matrix = newTransform;
3691 buffer()->updateMatrix(state.matrix);
3692 m_path = QTransform().rotate(-qRadiansToDegrees(angle)).map(m_path);
3693}
3694
3695void QQuickContext2D::shear(qreal h, qreal v)
3696{
3697 if (!state.invertibleCTM)
3698 return;
3699
3700 if (!qt_is_finite(h) || !qt_is_finite(v))
3701 return ;
3702
3703 QTransform newTransform = state.matrix;
3704 newTransform.shear(h, v);
3705
3706 if (!newTransform.isInvertible()) {
3707 state.invertibleCTM = false;
3708 return;
3709 }
3710
3711 state.matrix = newTransform;
3712 buffer()->updateMatrix(state.matrix);
3713 m_path = QTransform().shear(-h, -v).map(m_path);
3714}
3715
3716void QQuickContext2D::translate(qreal x, qreal y)
3717{
3718 if (!state.invertibleCTM)
3719 return;
3720
3721 if (!qt_is_finite(x) || !qt_is_finite(y))
3722 return ;
3723
3724 QTransform newTransform = state.matrix;
3725 newTransform.translate(x, y);
3726
3727 if (!newTransform.isInvertible()) {
3728 state.invertibleCTM = false;
3729 return;
3730 }
3731
3732 state.matrix = newTransform;
3733 buffer()->updateMatrix(state.matrix);
3734 m_path = QTransform().translate(-x, -y).map(m_path);
3735}
3736
3737void QQuickContext2D::transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
3738{
3739 if (!state.invertibleCTM)
3740 return;
3741
3742 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))
3743 return;
3744
3745 QTransform transform(a, b, c, d, e, f);
3746 QTransform newTransform = state.matrix * transform;
3747
3748 if (!newTransform.isInvertible()) {
3749 state.invertibleCTM = false;
3750 return;
3751 }
3752 state.matrix = newTransform;
3753 buffer()->updateMatrix(state.matrix);
3754 m_path = transform.inverted().map(m_path);
3755}
3756
3757void QQuickContext2D::setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
3758{
3759 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))
3760 return;
3761
3762 QTransform ctm = state.matrix;
3763 if (!ctm.isInvertible())
3764 return;
3765
3766 state.matrix = ctm.inverted() * state.matrix;
3767 m_path = ctm.map(m_path);
3768 state.invertibleCTM = true;
3769 transform(a, b, c, d, e, f);
3770}
3771
3773{
3774 if (!state.invertibleCTM)
3775 return;
3776
3777 if (!m_path.elementCount())
3778 return;
3779
3780 m_path.setFillRule(state.fillRule);
3781 buffer()->fill(m_path);
3782}
3783
3785{
3786 if (!state.invertibleCTM)
3787 return;
3788
3789 QPainterPath clipPath = m_path;
3790 clipPath.closeSubpath();
3791 if (state.clip) {
3792 state.clipPath = clipPath.intersected(state.clipPath);
3793 } else {
3794 state.clip = true;
3795 state.clipPath = clipPath;
3796 }
3797 buffer()->clip(state.clip, state.clipPath);
3798}
3799
3801{
3802 if (!state.invertibleCTM)
3803 return;
3804
3805 if (!m_path.elementCount())
3806 return;
3807
3808 buffer()->stroke(m_path);
3809}
3810
3811void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
3812{
3813 if (!state.invertibleCTM)
3814 return;
3815
3816 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3817 return;
3818
3819 buffer()->fillRect(QRectF(x, y, w, h));
3820}
3821
3822void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
3823{
3824 if (!state.invertibleCTM)
3825 return;
3826
3827 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3828 return;
3829
3830 buffer()->strokeRect(QRectF(x, y, w, h));
3831}
3832
3833void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
3834{
3835 if (!state.invertibleCTM)
3836 return;
3837
3838 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3839 return;
3840
3841 buffer()->clearRect(QRectF(x, y, w, h));
3842}
3843
3844void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill)
3845{
3846 if (!state.invertibleCTM)
3847 return;
3848
3849 if (!qt_is_finite(x) || !qt_is_finite(y))
3850 return;
3851
3852 QPainterPath textPath = createTextGlyphs(x, y, text);
3853 if (fill)
3854 buffer()->fill(textPath);
3855 else
3856 buffer()->stroke(textPath);
3857}
3858
3859
3861{
3862 if (!m_path.elementCount())
3863 return;
3864 m_path = QPainterPath();
3865}
3866
3868{
3869 if (!m_path.elementCount())
3870 return;
3871
3872 QRectF boundRect = m_path.boundingRect();
3873 if (boundRect.width() || boundRect.height())
3874 m_path.closeSubpath();
3875 //FIXME:QPainterPath set the current point to (0,0) after close subpath
3876 //should be the first point of the previous subpath
3877}
3878
3879void QQuickContext2D::moveTo( qreal x, qreal y)
3880{
3881 if (!state.invertibleCTM)
3882 return;
3883
3884 //FIXME: moveTo should not close the previous subpath
3885 m_path.moveTo(QPointF(x, y));
3886}
3887
3888void QQuickContext2D::lineTo( qreal x, qreal y)
3889{
3890 if (!state.invertibleCTM)
3891 return;
3892
3893 QPointF pt(x, y);
3894
3895 if (!m_path.elementCount())
3896 m_path.moveTo(pt);
3897 else if (m_path.currentPosition() != pt)
3898 m_path.lineTo(pt);
3899}
3900
3901void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
3902 qreal x, qreal y)
3903{
3904 if (!state.invertibleCTM)
3905 return;
3906
3907 if (!m_path.elementCount())
3908 m_path.moveTo(QPointF(cpx, cpy));
3909
3910 QPointF pt(x, y);
3911 if (m_path.currentPosition() != pt)
3912 m_path.quadTo(QPointF(cpx, cpy), pt);
3913}
3914
3915void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
3916 qreal cp2x, qreal cp2y,
3917 qreal x, qreal y)
3918{
3919 if (!state.invertibleCTM)
3920 return;
3921
3922 if (!m_path.elementCount())
3923 m_path.moveTo(QPointF(cp1x, cp1y));
3924
3925 QPointF pt(x, y);
3926 if (m_path.currentPosition() != pt)
3927 m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y), pt);
3928}
3929
3930void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, qreal radius)
3931{
3932 QPointF p0(m_path.currentPosition());
3933
3934 QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
3935 QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
3936 qreal p1p0_length = std::hypot(p1p0.x(), p1p0.y());
3937 qreal p1p2_length = std::hypot(p1p2.x(), p1p2.y());
3938
3939 qreal cos_phi = QPointF::dotProduct(p1p0, p1p2) / (p1p0_length * p1p2_length);
3940
3941 // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
3942 // We could have used areCollinear() here, but since we're reusing
3943 // the variables computed above later on we keep this logic.
3944 if (qFuzzyCompare(std::abs(cos_phi), qreal(1.0))) {
3945 m_path.lineTo(p1);
3946 return;
3947 }
3948
3949 qreal tangent = radius / std::tan(std::acos(cos_phi) / 2);
3950 qreal factor_p1p0 = tangent / p1p0_length;
3951 QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
3952
3953 QPointF orth_p1p0(p1p0.y(), -p1p0.x());
3954 qreal orth_p1p0_length = std::hypot(orth_p1p0.x(), orth_p1p0.y());
3955 qreal factor_ra = radius / orth_p1p0_length;
3956
3957 // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
3958 qreal cos_alpha = QPointF::dotProduct(orth_p1p0, p1p2) / (orth_p1p0_length * p1p2_length);
3959 if (cos_alpha < 0.f)
3960 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3961
3962 QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
3963
3964 // calculate angles for addArc
3965 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3966 qreal sa = std::atan2(orth_p1p0.y(), orth_p1p0.x());
3967
3968 // anticlockwise logic
3969 bool anticlockwise = false;
3970
3971 qreal factor_p1p2 = tangent / p1p2_length;
3972 QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
3973 QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
3974 qreal ea = std::atan2(orth_p1p2.y(), orth_p1p2.x());
3975 if ((sa > ea) && ((sa - ea) < M_PI))
3976 anticlockwise = true;
3977 if ((sa < ea) && ((ea - sa) > M_PI))
3978 anticlockwise = true;
3979
3980 arc(p.x(), p.y(), radius, sa, ea, anticlockwise);
3981}
3982
3983void QQuickContext2D::arcTo(qreal x1, qreal y1,
3984 qreal x2, qreal y2,
3985 qreal radius)
3986{
3987 if (!state.invertibleCTM)
3988 return;
3989
3990 if (!qt_is_finite(x1) || !qt_is_finite(y1) || !qt_is_finite(x2) || !qt_is_finite(y2) || !qt_is_finite(radius))
3991 return;
3992
3993 QPointF st(x1, y1);
3994 QPointF end(x2, y2);
3995
3996 if (!m_path.elementCount())
3997 m_path.moveTo(st);
3998 else if (st == m_path.currentPosition() || st == end || !radius)
3999 lineTo(x1, y1);
4000 else
4001 addArcTo(st, end, radius);
4002 }
4003
4004void QQuickContext2D::rect(qreal x, qreal y, qreal w, qreal h)
4005{
4006 if (!state.invertibleCTM)
4007 return;
4008 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
4009 return;
4010
4011 if (!w && !h) {
4012 m_path.moveTo(x, y);
4013 return;
4014 }
4015 m_path.addRect(x, y, w, h);
4016}
4017
4018void QQuickContext2D::roundedRect(qreal x, qreal y,
4019 qreal w, qreal h,
4020 qreal xr, qreal yr)
4021{
4022 if (!state.invertibleCTM)
4023 return;
4024
4025 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))
4026 return;
4027
4028 if (!w && !h) {
4029 m_path.moveTo(x, y);
4030 return;
4031 }
4032 m_path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
4033}
4034
4035void QQuickContext2D::ellipse(qreal x, qreal y,
4036 qreal w, qreal h)
4037{
4038 if (!state.invertibleCTM)
4039 return;
4040
4041 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
4042 return;
4043
4044 if (!w && !h) {
4045 m_path.moveTo(x, y);
4046 return;
4047 }
4048
4049 m_path.addEllipse(x, y, w, h);
4050}
4051
4052void QQuickContext2D::text(const QString& str, qreal x, qreal y)
4053{
4054 if (!state.invertibleCTM)
4055 return;
4056
4057 QPainterPath path;
4058 path.addText(x, y, state.font, str);
4059 m_path.addPath(path);
4060}
4061
4062void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise)
4063{
4064 if (!state.invertibleCTM)
4065 return;
4066
4067 if (!qt_is_finite(xc) || !qt_is_finite(yc) || !qt_is_finite(sar) || !qt_is_finite(ear) || !qt_is_finite(radius))
4068 return;
4069
4070 if (sar == ear)
4071 return;
4072
4073
4074 //### HACK
4075
4076 // In Qt we don't switch the coordinate system for degrees
4077 // and still use the 0,0 as bottom left for degrees so we need
4078 // to switch
4079 sar = -sar;
4080 ear = -ear;
4081 antiClockWise = !antiClockWise;
4082 //end hack
4083
4084 float sa = qRadiansToDegrees(sar);
4085 float ea = qRadiansToDegrees(ear);
4086
4087 double span = 0;
4088
4089 double xs = xc - radius;
4090 double ys = yc - radius;
4091 double width = radius*2;
4092 double height = radius*2;
4093 if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360)))
4094 // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
4095 // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
4096 // circumference of this circle.
4097 span = 360;
4098 else {
4099 if (!antiClockWise && (ea < sa)) {
4100 span += 360;
4101 } else if (antiClockWise && (sa < ea)) {
4102 span -= 360;
4103 }
4104 //### this is also due to switched coordinate system
4105 // we would end up with a 0 span instead of 360
4106 if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
4107 qFuzzyCompare(qAbs(span), 360))) {
4108 span += ea - sa;
4109 }
4110 }
4111
4112 // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
4113 if (!m_path.elementCount())
4114 m_path.arcMoveTo(xs, ys, width, height, sa);
4115 else if (!radius) {
4116 m_path.lineTo(xc, yc);
4117 return;
4118 }
4119
4120 m_path.arcTo(xs, ys, width, height, sa, span);
4121}
4122
4123int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
4124{
4125 int offset = 0;
4126 switch (value) {
4127 case QQuickContext2D::Top:
4128 case QQuickContext2D::Hanging:
4129 break;
4130 case QQuickContext2D::Middle:
4131 offset = (metrics.ascent() >> 1) + metrics.height() - metrics.ascent();
4132 break;
4133 case QQuickContext2D::Alphabetic:
4134 offset = metrics.ascent();
4135 break;
4136 case QQuickContext2D::Bottom:
4137 offset = metrics.height();
4138 break;
4139 }
4140 return offset;
4141}
4142
4143static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
4144{
4145 int offset = 0;
4146 if (value == QQuickContext2D::Start)
4147 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Left : QQuickContext2D::Right;
4148 else if (value == QQuickContext2D::End)
4149 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Right: QQuickContext2D::Left;
4150 switch (value) {
4152 offset = metrics.horizontalAdvance(text) / 2;
4153 break;
4155 offset = metrics.horizontalAdvance(text);
4156 break;
4158 default:
4159 break;
4160 }
4161 return offset;
4162}
4163
4164void QQuickContext2D::setGrabbedImage(const QImage& grab)
4165{
4166 m_grabbedImage = grab;
4167 m_grabbed = true;
4168}
4169
4170QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url, QSizeF sourceSize)
4171{
4172 return m_canvas->loadedPixmap(url, sourceSize);
4173}
4174
4175QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
4176{
4177 const QFontMetrics metrics(state.font);
4178 int yoffset = baseLineOffset(static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
4179 int xoffset = textAlignOffset(static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
4180
4181 QPainterPath textPath;
4182
4183 textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
4184 return textPath;
4185}
4186
4187
4188static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
4189{
4190 // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
4191 return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x()));
4192}
4193
4194static inline bool withinRange(qreal p, qreal a, qreal b)
4195{
4196 return (p >= a && p <= b) || (p >= b && p <= a);
4197}
4198
4199bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
4200{
4201 if (!state.invertibleCTM)
4202 return false;
4203
4204 if (!m_path.elementCount())
4205 return false;
4206
4207 if (!qt_is_finite(x) || !qt_is_finite(y))
4208 return false;
4209
4210 QPointF point(x, y);
4211 QTransform ctm = state.matrix;
4212 QPointF p = ctm.inverted().map(point);
4213 if (!qt_is_finite(p.x()) || !qt_is_finite(p.y()))
4214 return false;
4215
4216 const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
4217
4218 bool contains = m_path.contains(p);
4219
4220 if (!contains) {
4221 // check whether the point is on the border
4222 QPolygonF border = m_path.toFillPolygon();
4223
4224 QPointF p1 = border.at(0);
4225 QPointF p2;
4226
4227 for (int i = 1; i < border.size(); ++i) {
4228 p2 = border.at(i);
4229 if (areCollinear(p, p1, p2)
4230 // Once we know that the points are collinear we
4231 // only need to check one of the coordinates
4232 && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ?
4233 withinRange(p.x(), p1.x(), p2.x()) :
4234 withinRange(p.y(), p1.y(), p2.y()))) {
4235 return true;
4236 }
4237 p1 = p2;
4238 }
4239 }
4240 return contains;
4241}
4242
4243QMutex QQuickContext2D::mutex;
4244
4248 , m_v4engine(nullptr)
4249 , m_surface(nullptr)
4250 , m_thread(nullptr)
4251 , m_grabbed(false)
4252{
4253}
4254
4256{
4257 mutex.lock();
4258 m_texture->setItem(nullptr);
4259 delete m_buffer;
4260 m_texture->deleteLater();
4261
4262 mutex.unlock();
4263}
4264
4266{
4267 return m_v4value.value();
4268}
4269
4271{
4272 return QStringList() << QStringLiteral("2d");
4273}
4274
4275void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args)
4276{
4277 Q_UNUSED(args);
4278
4279 m_canvas = canvasItem;
4280 m_renderTarget = canvasItem->renderTarget();
4281 m_renderStrategy = canvasItem->renderStrategy();
4282
4283 // Disable threaded background rendering if the platform has issues with it
4284 if (m_renderTarget == QQuickCanvasItem::FramebufferObject
4285 && m_renderStrategy == QQuickCanvasItem::Threaded
4286 && !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) {
4287 m_renderTarget = QQuickCanvasItem::Image;
4288 }
4289
4290 // Disable framebuffer object based rendering always in Qt 6. It
4291 // is not implemented in the new RHI-based graphics stack, but the
4292 // enum value is still present. Switch to Image instead.
4293 if (m_renderTarget == QQuickCanvasItem::FramebufferObject)
4294 m_renderTarget = QQuickCanvasItem::Image;
4295
4296 m_texture = new QQuickContext2DImageTexture;
4297
4298 m_texture->setItem(canvasItem);
4299 m_texture->setCanvasWindow(canvasItem->canvasWindow().toRect());
4300 m_texture->setTileSize(canvasItem->tileSize());
4301 m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
4302 m_texture->setSmooth(canvasItem->smooth());
4303 m_texture->setAntialiasing(canvasItem->antialiasing());
4304 m_texture->setOnCustomThread(m_renderStrategy == QQuickCanvasItem::Threaded);
4305 m_thread = QThread::currentThread();
4306
4307 QThread *renderThread = m_thread;
4308 if (m_renderStrategy == QQuickCanvasItem::Threaded)
4309 renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
4310 if (renderThread && renderThread != QThread::currentThread())
4311 m_texture->moveToThread(renderThread);
4312 connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
4313
4314 reset();
4315}
4316
4317void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing)
4318{
4319 if (m_texture->thread() == QThread::currentThread()) {
4320 m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth, antialiasing);
4321 } else {
4322 QEvent *e = new QQuickContext2DTexture::CanvasChangeEvent(canvasSize,
4323 tileSize,
4324 canvasWindow,
4325 dirtyRect,
4326 smooth,
4327 antialiasing);
4328 QCoreApplication::postEvent(m_texture, e);
4329 }
4330}
4331
4333{
4334 if (m_buffer) {
4335 if (m_texture->thread() == QThread::currentThread())
4336 m_texture->paint(m_buffer);
4337 else
4338 QCoreApplication::postEvent(m_texture, new QQuickContext2DTexture::PaintEvent(m_buffer));
4339 }
4341}
4342
4344{
4345 return m_texture;
4346}
4347
4348QImage QQuickContext2D::toImage(const QRectF& bounds)
4349{
4350 if (m_texture->thread() == QThread::currentThread()) {
4351 flush();
4352 m_texture->grabImage(bounds);
4353 } else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
4354 qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode";
4355 return QImage();
4356 } else {
4357 flush();
4358 QCoreApplication::postEvent(m_texture, new QEvent(QEvent::Type(QEvent::User + 10)));
4359 QMetaObject::invokeMethod(m_texture,
4360 "grabImage",
4361 Qt::BlockingQueuedConnection,
4362 Q_ARG(QRectF, bounds));
4363 }
4364 QImage img = m_grabbedImage;
4365 m_grabbedImage = QImage();
4366 m_grabbed = false;
4367 return img;
4368}
4369
4370
4372{
4373 QV4::Scope scope(v4);
4374
4375 QV4::ScopedObject proto(scope, QQuickJSContext2DPrototype::create(v4));
4376 proto->defineAccessorProperty(QStringLiteral("strokeStyle"), QQuickJSContext2D::method_get_strokeStyle, QQuickJSContext2D::method_set_strokeStyle);
4377 proto->defineAccessorProperty(QStringLiteral("font"), QQuickJSContext2D::method_get_font, QQuickJSContext2D::method_set_font);
4378 proto->defineAccessorProperty(QStringLiteral("fillRule"), QQuickJSContext2D::method_get_fillRule, QQuickJSContext2D::method_set_fillRule);
4379 proto->defineAccessorProperty(QStringLiteral("globalAlpha"), QQuickJSContext2D::method_get_globalAlpha, QQuickJSContext2D::method_set_globalAlpha);
4380 proto->defineAccessorProperty(QStringLiteral("lineCap"), QQuickJSContext2D::method_get_lineCap, QQuickJSContext2D::method_set_lineCap);
4381 proto->defineAccessorProperty(QStringLiteral("shadowOffsetX"), QQuickJSContext2D::method_get_shadowOffsetX, QQuickJSContext2D::method_set_shadowOffsetX);
4382 proto->defineAccessorProperty(QStringLiteral("shadowOffsetY"), QQuickJSContext2D::method_get_shadowOffsetY, QQuickJSContext2D::method_set_shadowOffsetY);
4383 proto->defineAccessorProperty(QStringLiteral("globalCompositeOperation"), QQuickJSContext2D::method_get_globalCompositeOperation, QQuickJSContext2D::method_set_globalCompositeOperation);
4384 proto->defineAccessorProperty(QStringLiteral("miterLimit"), QQuickJSContext2D::method_get_miterLimit, QQuickJSContext2D::method_set_miterLimit);
4385 proto->defineAccessorProperty(QStringLiteral("fillStyle"), QQuickJSContext2D::method_get_fillStyle, QQuickJSContext2D::method_set_fillStyle);
4386 proto->defineAccessorProperty(QStringLiteral("shadowColor"), QQuickJSContext2D::method_get_shadowColor, QQuickJSContext2D::method_set_shadowColor);
4387 proto->defineAccessorProperty(QStringLiteral("textBaseline"), QQuickJSContext2D::method_get_textBaseline, QQuickJSContext2D::method_set_textBaseline);
4388#if QT_CONFIG(quick_path)
4389 proto->defineAccessorProperty(QStringLiteral("path"), QQuickJSContext2D::method_get_path, QQuickJSContext2D::method_set_path);
4390#endif
4391 proto->defineAccessorProperty(QStringLiteral("lineJoin"), QQuickJSContext2D::method_get_lineJoin, QQuickJSContext2D::method_set_lineJoin);
4392 proto->defineAccessorProperty(QStringLiteral("lineWidth"), QQuickJSContext2D::method_get_lineWidth, QQuickJSContext2D::method_set_lineWidth);
4393 proto->defineAccessorProperty(QStringLiteral("textAlign"), QQuickJSContext2D::method_get_textAlign, QQuickJSContext2D::method_set_textAlign);
4394 proto->defineAccessorProperty(QStringLiteral("shadowBlur"), QQuickJSContext2D::method_get_shadowBlur, QQuickJSContext2D::method_set_shadowBlur);
4395 proto->defineAccessorProperty(QStringLiteral("lineDashOffset"), QQuickJSContext2D::method_get_lineDashOffset, QQuickJSContext2D::method_set_lineDashOffset);
4396 contextPrototype = proto;
4397
4398 proto = scope.engine->newObject();
4399 proto->defineDefaultProperty(QStringLiteral("addColorStop"), QQuickContext2DStyle::gradient_proto_addColorStop, 0);
4400 gradientProto = proto;
4401
4402 proto = scope.engine->newObject();
4403 proto->defineAccessorProperty(scope.engine->id_length(), QQuickJSContext2DPixelData::proto_get_length, nullptr);
4404 pixelArrayProto = proto;
4405}
4406
4410
4412{
4413 if (m_stateStack.isEmpty())
4414 return;
4415
4416 QQuickContext2D::State newState = m_stateStack.pop();
4417
4418 if (state.matrix != newState.matrix)
4419 buffer()->updateMatrix(newState.matrix);
4420
4421 if (newState.globalAlpha != state.globalAlpha)
4422 buffer()->setGlobalAlpha(newState.globalAlpha);
4423
4424 if (newState.globalCompositeOperation != state.globalCompositeOperation)
4425 buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
4426
4427 if (newState.fillStyle != state.fillStyle)
4428 buffer()->setFillStyle(newState.fillStyle);
4429
4430 if (newState.strokeStyle != state.strokeStyle)
4431 buffer()->setStrokeStyle(newState.strokeStyle);
4432
4433 if (newState.lineWidth != state.lineWidth)
4434 buffer()->setLineWidth(newState.lineWidth);
4435
4436 if (newState.lineCap != state.lineCap)
4437 buffer()->setLineCap(newState.lineCap);
4438
4439 if (newState.lineJoin != state.lineJoin)
4440 buffer()->setLineJoin(newState.lineJoin);
4441
4442 if (newState.miterLimit != state.miterLimit)
4443 buffer()->setMiterLimit(newState.miterLimit);
4444
4445 if (newState.clip != state.clip || newState.clipPath != state.clipPath)
4446 buffer()->clip(newState.clip, newState.clipPath);
4447
4448 if (newState.shadowBlur != state.shadowBlur)
4449 buffer()->setShadowBlur(newState.shadowBlur);
4450
4451 if (newState.shadowColor != state.shadowColor)
4452 buffer()->setShadowColor(newState.shadowColor);
4453
4454 if (newState.shadowOffsetX != state.shadowOffsetX)
4455 buffer()->setShadowOffsetX(newState.shadowOffsetX);
4456
4457 if (newState.shadowOffsetY != state.shadowOffsetY)
4458 buffer()->setShadowOffsetY(newState.shadowOffsetY);
4459
4460 if (newState.lineDash != state.lineDash)
4461 buffer()->setLineDash(newState.lineDash);
4462
4463 m_path = state.matrix.map(m_path);
4464 state = newState;
4465 m_path = state.matrix.inverted().map(m_path);
4466}
4468{
4469 m_stateStack.push(state);
4470}
4471
4473{
4474 QQuickContext2D::State newState;
4475
4476 m_path = QPainterPath();
4477
4478 newState.clipPath.setFillRule(Qt::WindingFill);
4479
4480 m_stateStack.clear();
4481 m_stateStack.push(newState);
4482 popState();
4483 m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
4484}
4485
4486QV4::ExecutionEngine *QQuickContext2D::v4Engine() const
4487{
4488 return m_v4engine;
4489}
4490
4491void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
4492{
4493 if (m_v4engine != engine) {
4494 m_v4engine = engine;
4495
4496 if (m_v4engine == nullptr)
4497 return;
4498
4499 QQuickContext2DEngineData *ed = engineData(engine);
4500 QV4::Scope scope(engine);
4501 QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocate<QQuickJSContext2D>());
4502 QV4::ScopedObject p(scope, ed->contextPrototype.value());
4503 wrapper->setPrototypeOf(p);
4504 wrapper->d()->setContext(this);
4505 m_v4value = wrapper;
4506 }
4507}
4508
4509QT_END_NAMESPACE
4510
4511#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)