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