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