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 QVector<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, QVector<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
1407// colors and styles
1408/*!
1409 \qmlproperty variant QtQuick::Context2D::fillStyle
1410 Holds the current style used for filling shapes.
1411 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored.
1412 This property accepts several color syntaxes:
1413 \list
1414 \li 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)'
1415 \li 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)'
1416 \li 'hsl(hue, saturation, lightness)'
1417 \li 'hsla(hue, saturation, lightness, alpha)'
1418 \li '#RRGGBB' - for example: '#00FFCC'
1419 \li Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0)
1420 \endlist
1421 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
1422 best performance, because it's already a valid QColor value, does not need to be parsed everytime.
1423
1424 The default value is '#000000'.
1425 \sa createLinearGradient()
1426 \sa createRadialGradient()
1427 \sa createPattern()
1428 \sa strokeStyle
1429 */
1430QV4::ReturnedValue QQuickJSContext2D::method_get_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1431{
1432 QV4::Scope scope(b);
1433 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1434 CHECK_CONTEXT(r)
1435
1436 const QColor color = r->d()->context()->state.fillStyle.color().toRgb();
1437 if (color.isValid()) {
1438 if (color.alpha() == 255)
1439 RETURN_RESULT(scope.engine->newString(color.name()));
1440 QString alphaString = QString::number(color.alphaF(), 'f');
1441 while (alphaString.endsWith(QLatin1Char('0')))
1442 alphaString.chop(1);
1443 if (alphaString.endsWith(QLatin1Char('.')))
1444 alphaString += QLatin1Char('0');
1445 QString str = QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString);
1446 RETURN_RESULT(scope.engine->newString(str));
1447 }
1448 RETURN_RESULT(r->d()->context()->m_fillStyle.value());
1449}
1450
1451QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1452{
1453 QV4::Scope scope(b);
1454 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1456
1457 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
1458
1459 if (value->as<Object>()) {
1460 QColor color = QV4::ExecutionEngine::toVariant(value, QMetaType::fromType<QColor>()).value<QColor>();
1461 if (color.isValid()) {
1462 r->d()->context()->state.fillStyle = color;
1463 r->d()->context()->buffer()->setFillStyle(color);
1464 r->d()->context()->m_fillStyle.set(scope.engine, value);
1465 } else {
1466 QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
1467 if (style && *style->d()->brush != r->d()->context()->state.fillStyle) {
1468 r->d()->context()->state.fillStyle = *style->d()->brush;
1469 r->d()->context()->buffer()->setFillStyle(*style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
1470 r->d()->context()->m_fillStyle.set(scope.engine, value);
1471 r->d()->context()->state.fillPatternRepeatX = style->d()->patternRepeatX;
1472 r->d()->context()->state.fillPatternRepeatY = style->d()->patternRepeatY;
1473 }
1474 }
1475 } else if (value->isString()) {
1476 QColor color = qt_color_from_string(value);
1477 if (color.isValid() && r->d()->context()->state.fillStyle != QBrush(color)) {
1478 r->d()->context()->state.fillStyle = QBrush(color);
1479 r->d()->context()->buffer()->setFillStyle(r->d()->context()->state.fillStyle);
1480 r->d()->context()->m_fillStyle.set(scope.engine, value);
1481 }
1482 }
1483 RETURN_UNDEFINED();
1484}
1485
1486/*!
1487 \qmlproperty enumeration QtQuick::Context2D::fillRule
1488 Holds the current fill rule used for filling shapes. The following fill rules are supported:
1489
1490 \value Qt.OddEvenFill Qt::OddEvenFill
1491 \value Qt.WindingFill (default) Qt::WindingFill
1492
1493 \note Unlike QPainterPath, the Canvas API uses the winding fill as the default fill rule.
1494 The fillRule property is part of the context rendering state.
1495
1496 \sa fillStyle
1497*/
1498QV4::ReturnedValue QQuickJSContext2D::method_get_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1499{
1500 QV4::Scope scope(b);
1501 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1502 CHECK_CONTEXT(r)
1503
1504 RETURN_RESULT(scope.engine->fromVariant(r->d()->context()->state.fillRule));
1505}
1506
1507QV4::ReturnedValue QQuickJSContext2D::method_set_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1508{
1509 QV4::Scope scope(b);
1510 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1512
1513 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
1514
1515 if ((value->isString() && value->toQString() == QLatin1String("WindingFill"))
1516 || (value->isInt32() && value->integerValue() == Qt::WindingFill)) {
1517 r->d()->context()->state.fillRule = Qt::WindingFill;
1518 } else if ((value->isString() && value->toQStringNoThrow() == QLatin1String("OddEvenFill"))
1519 || (value->isInt32() && value->integerValue() == Qt::OddEvenFill)) {
1520 r->d()->context()->state.fillRule = Qt::OddEvenFill;
1521 } else {
1522 //error
1523 }
1524 r->d()->context()->m_path.setFillRule(r->d()->context()->state.fillRule);
1525 RETURN_UNDEFINED();
1526}
1527/*!
1528 \qmlproperty variant QtQuick::Context2D::strokeStyle
1529 Holds the current color or style to use for the lines around shapes,
1530 The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object.
1531 Invalid values are ignored.
1532
1533 The default value is '#000000'.
1534
1535 \sa createLinearGradient()
1536 \sa createRadialGradient()
1537 \sa createPattern()
1538 \sa fillStyle
1539 */
1540QV4::ReturnedValue QQuickJSContext2D::method_get_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1541{
1542 QV4::Scope scope(b);
1543 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1544 CHECK_CONTEXT(r)
1545
1546 const QColor color = r->d()->context()->state.strokeStyle.color().toRgb();
1547 if (color.isValid()) {
1548 if (color.alpha() == 255)
1549 RETURN_RESULT(scope.engine->newString(color.name()));
1550 QString alphaString = QString::number(color.alphaF(), 'f');
1551 while (alphaString.endsWith(QLatin1Char('0')))
1552 alphaString.chop(1);
1553 if (alphaString.endsWith(QLatin1Char('.')))
1554 alphaString += QLatin1Char('0');
1555 QString str = QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString);
1556 RETURN_RESULT(scope.engine->newString(str));
1557 }
1558 RETURN_RESULT(r->d()->context()->m_strokeStyle.value());
1559}
1560
1561QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1562{
1563 QV4::Scope scope(b);
1564 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1566
1567 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
1568
1569 if (value->as<Object>()) {
1570 QColor color = QV4::ExecutionEngine::toVariant(value, QMetaType::fromType<QColor>()).value<QColor>();
1571 if (color.isValid()) {
1572 r->d()->context()->state.strokeStyle = color;
1573 r->d()->context()->buffer()->setStrokeStyle(color);
1574 r->d()->context()->m_strokeStyle.set(scope.engine, value);
1575 } else {
1576 QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
1577 if (style && *style->d()->brush != r->d()->context()->state.strokeStyle) {
1578 r->d()->context()->state.strokeStyle = *style->d()->brush;
1579 r->d()->context()->buffer()->setStrokeStyle(*style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
1580 r->d()->context()->m_strokeStyle.set(scope.engine, value);
1581 r->d()->context()->state.strokePatternRepeatX = style->d()->patternRepeatX;
1582 r->d()->context()->state.strokePatternRepeatY = style->d()->patternRepeatY;
1583 } else if (!style && r->d()->context()->state.strokeStyle != QBrush(QColor())) {
1584 // If there is no style object, then ensure that the strokeStyle is at least
1585 // QColor in case it was previously set
1586 r->d()->context()->state.strokeStyle = QBrush(QColor());
1587 r->d()->context()->buffer()->setStrokeStyle(r->d()->context()->state.strokeStyle);
1588 r->d()->context()->m_strokeStyle.set(scope.engine, value);
1589 }
1590 }
1591 } else if (value->isString()) {
1592 QColor color = qt_color_from_string(value);
1593 if (color.isValid() && r->d()->context()->state.strokeStyle != QBrush(color)) {
1594 r->d()->context()->state.strokeStyle = QBrush(color);
1595 r->d()->context()->buffer()->setStrokeStyle(r->d()->context()->state.strokeStyle);
1596 r->d()->context()->m_strokeStyle.set(scope.engine, value);
1597 }
1598 }
1599 RETURN_UNDEFINED();
1600}
1601
1602/*!
1603 \qmlmethod object QtQuick::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
1604 Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between
1605 the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
1606
1607 A gradient is a smooth transition between colors. There are two types of gradients: linear and radial.
1608 Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between
1609 to the gradient's starting and end points or circles.
1610
1611 \sa CanvasGradient::addColorStop()
1612 \sa createRadialGradient()
1613 \sa createConicalGradient()
1614 \sa createPattern()
1615 \sa fillStyle
1616 \sa strokeStyle
1617 */
1618
1619QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1620{
1621 QV4::Scope scope(b);
1622 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1623 CHECK_CONTEXT(r)
1624
1625 if (argc >= 4) {
1626 qreal x0 = argv[0].toNumber();
1627 qreal y0 = argv[1].toNumber();
1628 qreal x1 = argv[2].toNumber();
1629 qreal y1 = argv[3].toNumber();
1630
1631 if (!qt_is_finite(x0)
1632 || !qt_is_finite(y0)
1633 || !qt_is_finite(x1)
1634 || !qt_is_finite(y1)) {
1635 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
1636 }
1637 QQuickContext2DEngineData *ed = engineData(scope.engine);
1638
1639 QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1640 QV4::ScopedObject p(scope, ed->gradientProto.value());
1641 gradient->setPrototypeOf(p);
1642 *gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
1643 RETURN_RESULT(*gradient);
1644 }
1645
1646 RETURN_RESULT(*thisObject);
1647
1648}
1649
1650/*!
1651 \qmlmethod object QtQuick::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
1652
1653 Returns a CanvasGradient object that represents a radial gradient that
1654 paints along the cone given by the start circle with origin (\a x0, \a y0)
1655 and radius \a r0, and the end circle with origin (\a x1, \a y1) and radius
1656 \a r1.
1657
1658 \sa CanvasGradient::addColorStop()
1659 \sa createLinearGradient()
1660 \sa createConicalGradient()
1661 \sa createPattern()
1662 \sa fillStyle
1663 \sa strokeStyle
1664 */
1665
1666QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1667{
1668 QV4::Scope scope(b);
1669 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1670 CHECK_CONTEXT(r)
1671
1672 if (argc >= 6) {
1673 qreal x0 = argv[0].toNumber();
1674 qreal y0 = argv[1].toNumber();
1675 qreal r0 = argv[2].toNumber();
1676 qreal x1 = argv[3].toNumber();
1677 qreal y1 = argv[4].toNumber();
1678 qreal r1 = argv[5].toNumber();
1679
1680 if (!qt_is_finite(x0)
1681 || !qt_is_finite(y0)
1682 || !qt_is_finite(x1)
1683 || !qt_is_finite(r0)
1684 || !qt_is_finite(r1)
1685 || !qt_is_finite(y1)) {
1686 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
1687 }
1688
1689 if (r0 < 0 || r1 < 0)
1690 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
1691
1692 QQuickContext2DEngineData *ed = engineData(scope.engine);
1693
1694 QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1695 QV4::ScopedObject p(scope, ed->gradientProto.value());
1696 gradient->setPrototypeOf(p);
1697 *gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r1, QPointF(x0, y0), r0);
1698 RETURN_RESULT(*gradient);
1699 }
1700
1701 RETURN_RESULT(*thisObject);
1702
1703}
1704
1705/*!
1706 \qmlmethod object QtQuick::Context2D::createConicalGradient(real x, real y, real angle)
1707
1708 Returns a CanvasGradient object that represents a conical gradient that
1709 interpolates colors counter-clockwise around a center point (\a x, \a y)
1710 with a start angle \a angle in units of radians.
1711
1712 \sa CanvasGradient::addColorStop()
1713 \sa createLinearGradient()
1714 \sa createRadialGradient()
1715 \sa createPattern()
1716 \sa fillStyle
1717 \sa strokeStyle
1718 */
1719
1720QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1721{
1722 QV4::Scope scope(b);
1723 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
1724 CHECK_CONTEXT(r)
1725
1726 if (argc >= 3) {
1727 qreal x = argv[0].toNumber();
1728 qreal y = argv[1].toNumber();
1729 qreal angle = qRadiansToDegrees(argv[2].toNumber());
1730 if (!qt_is_finite(x) || !qt_is_finite(y)) {
1731 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
1732 }
1733
1734 if (!qt_is_finite(angle)) {
1735 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
1736 }
1737
1738 QQuickContext2DEngineData *ed = engineData(scope.engine);
1739
1740 QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1741 QV4::ScopedObject p(scope, ed->gradientProto.value());
1742 gradient->setPrototypeOf(p);
1743 *gradient->d()->brush = QConicalGradient(x, y, angle);
1744 RETURN_RESULT(*gradient);
1745 }
1746
1747 RETURN_RESULT(*thisObject);
1748
1749}
1750/*!
1751 \qmlmethod variant QtQuick::Context2D::createPattern(color color, enumeration patternMode)
1752 This is an overloaded function.
1753 Returns a CanvasPattern object that uses the given \a color and \a patternMode.
1754 The valid pattern modes are:
1755
1756 \value Qt.SolidPattern Qt::SolidPattern
1757 \value Qt.Dense1Pattern Qt::Dense1Pattern
1758 \value Qt.Dense2Pattern Qt::Dense2Pattern
1759 \value Qt.Dense3Pattern Qt::Dense3Pattern
1760 \value Qt.Dense4Pattern Qt::Dense4Pattern
1761 \value Qt.Dense5Pattern Qt::Dense5Pattern
1762 \value Qt.Dense6Pattern Qt::Dense6Pattern
1763 \value Qt.Dense7Pattern Qt::Dense7Pattern
1764 \value Qt.HorPattern Qt::HorPattern
1765 \value Qt.VerPattern Qt::VerPattern
1766 \value Qt.CrossPattern Qt::CrossPattern
1767 \value Qt.BDiagPattern Qt::BDiagPattern
1768 \value Qt.FDiagPattern Qt::FDiagPattern
1769 \value Qt.DiagCrossPattern Qt::DiagCrossPattern
1770
1771 \sa Qt::BrushStyle
1772*/
1773/*!
1774 \qmlmethod variant QtQuick::Context2D::createPattern(Image image, string repetition)
1775 Returns a CanvasPattern object that uses the given image and repeats in the
1776 direction(s) given by the repetition argument.
1777
1778 The \a image parameter must be a valid Image item, a valid CanvasImageData
1779 object or loaded image url. If there is no image data, thus function throws an
1780 INVALID_STATE_ERR exception.
1781
1782 The allowed values for \a repetition are:
1783
1784 \value "repeat" both directions
1785 \value "repeat-x horizontal only
1786 \value "repeat-y" vertical only
1787 \value "no-repeat" neither
1788
1789 If the repetition argument is empty or null, the value "repeat" is used.
1790
1791 \sa strokeStyle
1792 \sa fillStyle
1793*/
1794QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1795{
1796 QV4::Scope scope(b);
1797 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1798 CHECK_CONTEXT(r)
1799
1800 if (argc >= 2) {
1801 QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
1802
1803 QColor color = QV4::ExecutionEngine::toVariant(
1804 argv[0], QMetaType::fromType<QColor>()).value<QColor>();
1805 if (color.isValid()) {
1806 int patternMode = argv[1].toInt32();
1807 Qt::BrushStyle style = Qt::SolidPattern;
1808 if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
1809 style = static_cast<Qt::BrushStyle>(patternMode);
1810 }
1811 *pattern->d()->brush = QBrush(color, style);
1812 } else {
1813 QImage patternTexture;
1814
1815 if (const QV4::Object *o = argv[0].as<Object>()) {
1816 QV4::ScopedString s(scope, scope.engine->newString(QStringLiteral("data")));
1817 QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, o->get(s));
1818 if (!!pixelData) {
1819 patternTexture = *pixelData->d()->image;
1820 }
1821 } else {
1822 patternTexture = r->d()->context()->createPixmap(QUrl(argv[0].toQStringNoThrow()))->image();
1823 }
1824
1825 if (!patternTexture.isNull()) {
1826 pattern->d()->brush->setTextureImage(patternTexture);
1827
1828 QString repetition = argv[1].toQStringNoThrow();
1829 if (repetition == QLatin1String("repeat") || repetition.isEmpty()) {
1830 pattern->d()->patternRepeatX = true;
1831 pattern->d()->patternRepeatY = true;
1832 } else if (repetition == QLatin1String("repeat-x")) {
1833 pattern->d()->patternRepeatX = true;
1834 pattern->d()->patternRepeatY = false;
1835 } else if (repetition == QLatin1String("repeat-y")) {
1836 pattern->d()->patternRepeatX = false;
1837 pattern->d()->patternRepeatY = true;
1838 } else if (repetition == QLatin1String("no-repeat")) {
1839 pattern->d()->patternRepeatX = false;
1840 pattern->d()->patternRepeatY = false;
1841 } else {
1842 //TODO: exception: SYNTAX_ERR
1843 }
1844
1845 }
1846 }
1847
1848 RETURN_RESULT(*pattern);
1849
1850 }
1851 RETURN_UNDEFINED();
1852}
1853
1854// line styles
1855/*!
1856 \qmlproperty string QtQuick::Context2D::lineCap
1857 Holds the current line cap style.
1858 The possible line cap styles are:
1859
1860 \value "butt"
1861 (default) Qt::FlatCap the end of each line has a flat edge
1862 perpendicular to the direction of the line.
1863 \value "round"
1864 Qt::RoundCap a semi-circle with the diameter equal to the width of the
1865 line is added on to the end of the line.
1866 \value "square"
1867 Qt::SquareCap a rectangle with the length of the line width and the
1868 width of half the line width, placed flat against the edge
1869 perpendicular to the direction of the line.
1870
1871 Other values are ignored.
1872*/
1873QV4::ReturnedValue QQuickJSContext2D::method_get_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1874{
1875 QV4::Scope scope(b);
1876 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1877 CHECK_CONTEXT(r)
1878
1879 switch (r->d()->context()->state.lineCap) {
1880 case Qt::RoundCap:
1881 RETURN_RESULT(scope.engine->newString(QStringLiteral("round")));
1882 case Qt::SquareCap:
1883 RETURN_RESULT(scope.engine->newString(QStringLiteral("square")));
1884 case Qt::FlatCap:
1885 default:
1886 break;
1887 }
1888 RETURN_RESULT(scope.engine->newString(QStringLiteral("butt")));
1889}
1890
1891QV4::ReturnedValue QQuickJSContext2D::method_set_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1892{
1893 if (!argc)
1894 return QV4::Encode::undefined();
1895
1896 QV4::Scope scope(b);
1897 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1899
1900 QString lineCap = argv[0].toQString();
1901 Qt::PenCapStyle cap;
1902 if (lineCap == QLatin1String("round"))
1903 cap = Qt::RoundCap;
1904 else if (lineCap == QLatin1String("butt"))
1905 cap = Qt::FlatCap;
1906 else if (lineCap == QLatin1String("square"))
1907 cap = Qt::SquareCap;
1908 else
1909 RETURN_UNDEFINED();
1910
1911 if (cap != r->d()->context()->state.lineCap) {
1912 r->d()->context()->state.lineCap = cap;
1913 r->d()->context()->buffer()->setLineCap(cap);
1914 }
1915 RETURN_UNDEFINED();
1916}
1917
1918/*!
1919 \qmlproperty string QtQuick::Context2D::lineJoin
1920 Holds the current line join style. A join exists at any point in a subpath
1921 shared by two consecutive lines. When a subpath is closed, then a join also
1922 exists at its first point (equivalent to its last point) connecting the
1923 first and last lines in the subpath.
1924
1925 The possible line join styles are:
1926
1927 \value "bevel" Qt::BevelJoin The triangular notch between the two lines is filled.
1928 \value "round" Qt::RoundJoin A circular arc between the two lines is filled.
1929 \value "miter" (default) Qt::MiterJoin The outer edges of the lines are extended to
1930 meet at an angle, and this area is filled.
1931
1932 Other values are ignored.
1933*/
1934QV4::ReturnedValue QQuickJSContext2D::method_get_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1935{
1936 QV4::Scope scope(b);
1937 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1938 CHECK_CONTEXT(r)
1939
1940 switch (r->d()->context()->state.lineJoin) {
1941 case Qt::RoundJoin:
1942 RETURN_RESULT(scope.engine->newString(QStringLiteral("round")));
1943 case Qt::BevelJoin:
1944 RETURN_RESULT(scope.engine->newString(QStringLiteral("bevel")));
1945 case Qt::MiterJoin:
1946 default:
1947 break;
1948 }
1949 RETURN_RESULT(scope.engine->newString(QStringLiteral("miter")));
1950}
1951
1952QV4::ReturnedValue QQuickJSContext2D::method_set_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1953{
1954 QV4::Scope scope(b);
1955 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1957
1958 if (!argc)
1959 THROW_TYPE_ERROR();
1960
1961 QString lineJoin = argv[0].toQString();
1962 Qt::PenJoinStyle join;
1963 if (lineJoin == QLatin1String("round"))
1964 join = Qt::RoundJoin;
1965 else if (lineJoin == QLatin1String("bevel"))
1966 join = Qt::BevelJoin;
1967 else if (lineJoin == QLatin1String("miter"))
1968 join = Qt::SvgMiterJoin;
1969 else
1970 RETURN_UNDEFINED();
1971
1972 if (join != r->d()->context()->state.lineJoin) {
1973 r->d()->context()->state.lineJoin = join;
1974 r->d()->context()->buffer()->setLineJoin(join);
1975 }
1976 RETURN_UNDEFINED();
1977}
1978
1979/*!
1980 \qmlproperty real QtQuick::Context2D::lineWidth
1981 Holds the current line width. Values that are not finite values greater than zero are ignored.
1982 */
1983QV4::ReturnedValue QQuickJSContext2D::method_get_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
1984{
1985 QV4::Scope scope(b);
1986 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1987 CHECK_CONTEXT(r)
1988
1989 RETURN_RESULT(QV4::Encode(r->d()->context()->state.lineWidth));
1990}
1991
1992QV4::ReturnedValue QQuickJSContext2D::method_set_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
1993{
1994 QV4::Scope scope(b);
1995 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
1997
1998 qreal w = argc ? argv[0].toNumber() : -1;
1999
2000 if (w > 0 && qt_is_finite(w) && w != r->d()->context()->state.lineWidth) {
2001 r->d()->context()->state.lineWidth = w;
2002 r->d()->context()->buffer()->setLineWidth(w);
2003 }
2004 RETURN_UNDEFINED();
2005}
2006
2007/*!
2008 \qmlproperty real QtQuick::Context2D::miterLimit
2009 Holds the current miter limit ratio.
2010 The default miter limit value is 10.0.
2011 */
2012QV4::ReturnedValue QQuickJSContext2D::method_get_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2013{
2014 QV4::Scope scope(b);
2015 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2016 CHECK_CONTEXT(r)
2017
2018 RETURN_RESULT(QV4::Encode(r->d()->context()->state.miterLimit));
2019}
2020
2021QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2022{
2023 QV4::Scope scope(b);
2024 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2026
2027 qreal ml = argc ? argv[0].toNumber() : -1;
2028
2029 if (ml > 0 && qt_is_finite(ml) && ml != r->d()->context()->state.miterLimit) {
2030 r->d()->context()->state.miterLimit = ml;
2031 r->d()->context()->buffer()->setMiterLimit(ml);
2032 }
2033 RETURN_UNDEFINED();
2034}
2035
2036/*!
2037 \qmlmethod array QtQuick::Context2D::getLineDash()
2038 \since QtQuick 2.11
2039 Returns an array of qreals representing the dash pattern of the line.
2040
2041 \sa setLineDash(), lineDashOffset
2042 */
2043QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2044{
2045 QV4::Scope scope(b);
2046 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2047 CHECK_CONTEXT(r)
2048
2049 const QVector<qreal> pattern = r->d()->context()->state.lineDash;
2050 QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject(pattern.size()));
2051 array->arrayReserve(pattern.size());
2052 for (int i = 0; i < pattern.size(); i++)
2053 array->put(i, QV4::Value::fromDouble(pattern[i]));
2054
2055 array->setArrayLengthUnchecked(pattern.size());
2056
2057 RETURN_RESULT(*array);
2058}
2059
2060/*!
2061 \qmlmethod QtQuick::Context2D::setLineDash(array pattern)
2062 \since QtQuick 2.11
2063 Sets the dash pattern to the given pattern.
2064
2065 \a pattern a list of numbers that specifies distances to alternately draw a line and a gap.
2066
2067 If the number of elements in the array is odd, the elements of the array get copied
2068 and concatenated. For example, [5, 15, 25] will become [5, 15, 25, 5, 15, 25].
2069
2070 \table 100%
2071 \row
2072 \li \inlineimage qml-item-canvas-lineDash.png
2073 \li
2074 \code
2075 var space = 4
2076 ctx.setLineDash([1, space, 3, space, 9, space, 27, space, 9, space])
2077 ...
2078 ctx.stroke();
2079 \endcode
2080 \endtable
2081
2082 \sa getLineDash(), lineDashOffset
2083 */
2084QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2085{
2086 QV4::Scope scope(b);
2087 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2089
2090 if (!argc)
2091 RETURN_UNDEFINED();
2092
2093 QV4::ScopedArrayObject array(scope, argv[0]);
2094 if (!array)
2095 RETURN_UNDEFINED();
2096
2097 QV4::ScopedValue v(scope);
2098 const uint arrayLength = array->getLength();
2099 QVector<qreal> dashes;
2100 dashes.reserve(arrayLength);
2101 for (uint i = 0; i < arrayLength; ++i) {
2102 v = array->get(i);
2103 const double number = v->toNumber();
2104
2105 if (!qt_is_finite(number) || (number < 0))
2106 RETURN_UNDEFINED();
2107
2108 dashes.append(v->toNumber());
2109 }
2110 if (dashes.size() % 2 != 0) {
2111 dashes += dashes;
2112 }
2113
2114 r->d()->context()->state.lineDash = dashes;
2115 r->d()->context()->buffer()->setLineDash(dashes);
2116
2117 RETURN_UNDEFINED();
2118}
2119
2120/*!
2121 \qmlproperty real QtQuick::Context2D::lineDashOffset
2122 \since QtQuick 2.11
2123
2124 Holds the current line dash offset.
2125 The default line dash offset value is \c 0.
2126
2127 \sa getLineDash(), setLineDash()
2128 */
2129QV4::ReturnedValue QQuickJSContext2D::method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2130{
2131 QV4::Scope scope(b);
2132 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2133 CHECK_CONTEXT(r)
2134
2135 RETURN_RESULT(QV4::Encode(r->d()->context()->state.lineDashOffset));
2136}
2137
2138QV4::ReturnedValue QQuickJSContext2D::method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2139{
2140 QV4::Scope scope(b);
2141 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2143
2144 const qreal offset = argc ? argv[0].toNumber() : -1;
2145
2146 if (qt_is_finite(offset) && offset != r->d()->context()->state.lineDashOffset) {
2147 r->d()->context()->state.lineDashOffset = offset;
2148 r->d()->context()->buffer()->setLineDashOffset(offset);
2149 }
2150 RETURN_UNDEFINED();
2151}
2152
2153
2154// shadows
2155/*!
2156 \qmlproperty real QtQuick::Context2D::shadowBlur
2157 Holds the current level of blur applied to shadows
2158 */
2159QV4::ReturnedValue QQuickJSContext2D::method_get_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2160{
2161 QV4::Scope scope(b);
2162 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2163 CHECK_CONTEXT(r)
2164
2165 RETURN_RESULT(QV4::Encode(r->d()->context()->state.shadowBlur));
2166}
2167
2168QV4::ReturnedValue QQuickJSContext2D::method_set_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2169{
2170 QV4::Scope scope(b);
2171 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2173
2174 qreal blur = argc ? argv[0].toNumber() : -1;
2175
2176 if (blur > 0 && qt_is_finite(blur) && blur != r->d()->context()->state.shadowBlur) {
2177 r->d()->context()->state.shadowBlur = blur;
2178 r->d()->context()->buffer()->setShadowBlur(blur);
2179 }
2180 RETURN_UNDEFINED();
2181}
2182
2183/*!
2184 \qmlproperty string QtQuick::Context2D::shadowColor
2185 Holds the current shadow color.
2186 */
2187QV4::ReturnedValue QQuickJSContext2D::method_get_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2188{
2189 QV4::Scope scope(b);
2190 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2191 CHECK_CONTEXT(r)
2192
2193 RETURN_RESULT(scope.engine->newString(r->d()->context()->state.shadowColor.name()));
2194}
2195
2196QV4::ReturnedValue QQuickJSContext2D::method_set_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2197{
2198 QV4::Scope scope(b);
2199 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2201
2202 QColor color;
2203 if (argc)
2204 color = qt_color_from_string(argv[0]);
2205
2206 if (color.isValid() && color != r->d()->context()->state.shadowColor) {
2207 r->d()->context()->state.shadowColor = color;
2208 r->d()->context()->buffer()->setShadowColor(color);
2209 }
2210 RETURN_UNDEFINED();
2211}
2212
2213
2214/*!
2215 \qmlproperty real QtQuick::Context2D::shadowOffsetX
2216 Holds the current shadow offset in the positive horizontal distance.
2217
2218 \sa shadowOffsetY
2219 */
2220QV4::ReturnedValue QQuickJSContext2D::method_get_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2221{
2222 QV4::Scope scope(b);
2223 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2224 CHECK_CONTEXT(r)
2225
2226 RETURN_RESULT(QV4::Encode(r->d()->context()->state.shadowOffsetX));
2227}
2228
2229QV4::ReturnedValue QQuickJSContext2D::method_set_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2230{
2231 QV4::Scope scope(b);
2232 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2234
2235 qreal offsetX = argc ? argv[0].toNumber() : qt_qnan();
2236 if (qt_is_finite(offsetX) && offsetX != r->d()->context()->state.shadowOffsetX) {
2237 r->d()->context()->state.shadowOffsetX = offsetX;
2238 r->d()->context()->buffer()->setShadowOffsetX(offsetX);
2239 }
2240 RETURN_UNDEFINED();
2241}
2242/*!
2243 \qmlproperty real QtQuick::Context2D::shadowOffsetY
2244 Holds the current shadow offset in the positive vertical distance.
2245
2246 \sa shadowOffsetX
2247 */
2248QV4::ReturnedValue QQuickJSContext2D::method_get_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2249{
2250 QV4::Scope scope(b);
2251 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2252 CHECK_CONTEXT(r)
2253
2254 RETURN_RESULT(QV4::Encode(r->d()->context()->state.shadowOffsetY));
2255}
2256
2257QV4::ReturnedValue QQuickJSContext2D::method_set_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2258{
2259 QV4::Scope scope(b);
2260 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2262
2263 qreal offsetY = argc ? argv[0].toNumber() : qt_qnan();
2264 if (qt_is_finite(offsetY) && offsetY != r->d()->context()->state.shadowOffsetY) {
2265 r->d()->context()->state.shadowOffsetY = offsetY;
2266 r->d()->context()->buffer()->setShadowOffsetY(offsetY);
2267 }
2268 RETURN_UNDEFINED();
2269}
2270
2271#if QT_CONFIG(quick_path)
2272QV4::ReturnedValue QQuickJSContext2D::method_get_path(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2273{
2274 QV4::Scope scope(b);
2275 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2276 CHECK_CONTEXT(r)
2277
2278 RETURN_RESULT(r->d()->context()->m_v4path.value());
2279}
2280
2281QV4::ReturnedValue QQuickJSContext2D::method_set_path(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2282{
2283 QV4::Scope scope(b);
2284 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2285 CHECK_CONTEXT_SETTER(r)
2286
2287 QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
2288 r->d()->context()->beginPath();
2289 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, value);
2290 if (!!qobjectWrapper) {
2291 if (QQuickPath *path = qobject_cast<QQuickPath*>(qobjectWrapper->object()))
2292 r->d()->context()->m_path = path->path();
2293 } else {
2294 QString path =value->toQStringNoThrow();
2295 QQuickSvgParser::parsePathDataFast(path, r->d()->context()->m_path);
2296 }
2297 r->d()->context()->m_v4path.set(scope.engine, value);
2298 RETURN_UNDEFINED();
2299}
2300#endif // QT_CONFIG(quick_path)
2301
2302//rects
2303/*!
2304 \qmlmethod object QtQuick::Context2D::clearRect(real x, real y, real w, real h)
2305
2306 Clears all pixels on the canvas in the rectangle specified by
2307 (\a x, \a y, \a w, \a h) to transparent black.
2308 */
2309QV4::ReturnedValue QQuickJSContext2DPrototype::method_clearRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2310{
2311 QV4::Scope scope(b);
2312 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2313 CHECK_CONTEXT(r)
2314
2315
2316 if (argc >= 4)
2317 r->d()->context()->clearRect(argv[0].toNumber(),
2318 argv[1].toNumber(),
2319 argv[2].toNumber(),
2320 argv[3].toNumber());
2321
2322 RETURN_RESULT(*thisObject);
2323
2324}
2325/*!
2326 \qmlmethod object QtQuick::Context2D::fillRect(real x, real y, real w, real h)
2327
2328 Paints a rectangular area specified by (\a x, \a y, \a w, \a h) using fillStyle.
2329
2330 \sa fillStyle
2331 */
2332QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2333{
2334 QV4::Scope scope(b);
2335 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2336 CHECK_CONTEXT(r)
2337
2338 if (argc >= 4)
2339 r->d()->context()->fillRect(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2340 RETURN_RESULT(*thisObject);
2341
2342}
2343
2344/*!
2345 \qmlmethod object QtQuick::Context2D::strokeRect(real x, real y, real w, real h)
2346
2347 Strokes the path of the rectangle specified by (\a x, \a y, \a w, \a h) using
2348 strokeStyle, lineWidth, lineJoin, and (if appropriate) miterLimit attributes.
2349
2350 \sa strokeStyle, lineWidth, lineJoin, miterLimit
2351 */
2352QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2353{
2354 QV4::Scope scope(b);
2355 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2356 CHECK_CONTEXT(r)
2357
2358 if (argc >= 4)
2359 r->d()->context()->strokeRect(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2360
2361 RETURN_RESULT(*thisObject);
2362
2363}
2364
2365// Complex shapes (paths) API
2366/*!
2367 \qmlmethod object QtQuick::Context2D::arc(real x, real y, real radius,
2368 real startAngle, real endAngle, bool anticlockwise)
2369
2370 Adds an arc to the current subpath that lies on the circumference of the
2371 circle whose center is at the point (\a x, \a y) and whose radius is
2372 \a radius.
2373
2374 Both \a startAngle and \a endAngle are measured from the x-axis in radians.
2375
2376 \image qml-item-canvas-arc.png
2377
2378 \image qml-item-canvas-startAngle.png
2379
2380 The \a anticlockwise parameter is \c false for each arc in the figure above
2381 because they are all drawn in the clockwise direction.
2382
2383 \sa arcTo, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C's 2D
2384 Context Standard for arc()}
2385*/
2386QV4::ReturnedValue QQuickJSContext2DPrototype::method_arc(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2387{
2388 QV4::Scope scope(b);
2389 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2390 CHECK_CONTEXT(r)
2391
2392 if (argc >= 5) {
2393 bool antiClockwise = false;
2394
2395 if (argc == 6)
2396 antiClockwise = argv[5].toBoolean();
2397
2398 qreal radius = argv[2].toNumber();
2399
2400 if (qt_is_finite(radius) && radius < 0)
2401 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
2402
2403 r->d()->context()->arc(argv[0].toNumber(),
2404 argv[1].toNumber(),
2405 radius,
2406 argv[3].toNumber(),
2407 argv[4].toNumber(),
2408 antiClockwise);
2409 }
2410
2411 RETURN_RESULT(*thisObject);
2412
2413}
2414
2415/*!
2416 \qmlmethod object QtQuick::Context2D::arcTo(real x1, real y1, real x2,
2417 real y2, real radius)
2418
2419 Adds an arc with the given control points and radius to the current subpath,
2420 connected to the previous point by a straight line. To draw an arc, you
2421 begin with the same steps you followed to create a line:
2422
2423 \list
2424 \li Call the beginPath() method to set a new path.
2425 \li Call the moveTo(\c x, \c y) method to set your starting position on the
2426 canvas at the point (\c x, \c y).
2427 \li To draw an arc or circle, call the arcTo(\a x1, \a y1, \a x2, \a y2,
2428 \a radius) method. This adds an arc with starting point (\a x1, \a y1),
2429 ending point (\a x2, \a y2), and \a radius to the current subpath and
2430 connects it to the previous subpath by a straight line.
2431 \endlist
2432
2433 \image qml-item-canvas-arcTo.png
2434
2435 \sa arc, {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C's 2D
2436 Context Standard for arcTo()}
2437*/
2438QV4::ReturnedValue QQuickJSContext2DPrototype::method_arcTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2439{
2440 QV4::Scope scope(b);
2441 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2442 CHECK_CONTEXT(r)
2443
2444 if (argc >= 5) {
2445 qreal radius = argv[4].toNumber();
2446
2447 if (qt_is_finite(radius) && radius < 0)
2448 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
2449
2450 r->d()->context()->arcTo(argv[0].toNumber(),
2451 argv[1].toNumber(),
2452 argv[2].toNumber(),
2453 argv[3].toNumber(),
2454 radius);
2455 }
2456
2457 RETURN_RESULT(*thisObject);
2458
2459}
2460
2461/*!
2462 \qmlmethod object QtQuick::Context2D::beginPath()
2463
2464 Resets the current path to a new path.
2465 */
2466QV4::ReturnedValue QQuickJSContext2DPrototype::method_beginPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2467{
2468 QV4::Scope scope(b);
2469 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2470 CHECK_CONTEXT(r)
2471
2472 r->d()->context()->beginPath();
2473
2474 RETURN_RESULT(*thisObject);
2475
2476}
2477
2478/*!
2479 \qmlmethod object QtQuick::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
2480
2481 Adds a cubic bezier curve between the current position and the given endPoint using the control points specified by (\a {cp1x}, \a {cp1y}),
2482 and (\a {cp2x}, \a {cp2y}).
2483 After the curve is added, the current position is updated to be at the end point (\a {x}, \a {y}) of the curve.
2484 The following code produces the path shown below:
2485
2486 \code
2487 ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
2488 ctx.lineWidth = 1;
2489 ctx.beginPath();
2490 ctx.moveTo(20, 0);//start point
2491 ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
2492 ctx.stroke();
2493 \endcode
2494
2495 \image qml-item-canvas-bezierCurveTo.png
2496
2497 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
2498 \sa {https://web.archive.org/web/20130505222636if_/http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
2499 */
2500QV4::ReturnedValue QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2501{
2502 QV4::Scope scope(b);
2503 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2504 CHECK_CONTEXT(r)
2505
2506 if (argc >= 6) {
2507 qreal cp1x = argv[0].toNumber();
2508 qreal cp1y = argv[1].toNumber();
2509 qreal cp2x = argv[2].toNumber();
2510 qreal cp2y = argv[3].toNumber();
2511 qreal x = argv[4].toNumber();
2512 qreal y = argv[5].toNumber();
2513
2514 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))
2515 RETURN_UNDEFINED();
2516
2517 r->d()->context()->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
2518 }
2519 RETURN_RESULT(*thisObject);
2520}
2521
2522/*!
2523 \qmlmethod object QtQuick::Context2D::clip()
2524
2525 Creates the clipping region from the current path.
2526 Any parts of the shape outside the clipping path are not displayed.
2527 To create a complex shape using the \c clip() method:
2528
2529 \list 1
2530 \li Call the \c{context.beginPath()} method to set the clipping path.
2531 \li Define the clipping path by calling any combination of the \c{lineTo},
2532 \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
2533 \li Call the \c{context.clip()} method.
2534 \endlist
2535
2536 The new shape displays. The following shows how a clipping path can
2537 modify how an image displays:
2538
2539 \image qml-item-canvas-clip-complex.png
2540 \sa beginPath()
2541 \sa closePath()
2542 \sa stroke()
2543 \sa fill()
2544 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
2545 */
2546QV4::ReturnedValue QQuickJSContext2DPrototype::method_clip(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2547{
2548 QV4::Scope scope(b);
2549 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2550 CHECK_CONTEXT(r)
2551
2552 r->d()->context()->clip();
2553 RETURN_RESULT(*thisObject);
2554}
2555
2556/*!
2557 \qmlmethod object QtQuick::Context2D::closePath()
2558 Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
2559 The current point of the new path is the previous subpath's first point.
2560
2561 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
2562 */
2563QV4::ReturnedValue QQuickJSContext2DPrototype::method_closePath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2564{
2565 QV4::Scope scope(b);
2566 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2567 CHECK_CONTEXT(r)
2568
2569 r->d()->context()->closePath();
2570
2571 RETURN_RESULT(*thisObject);
2572}
2573
2574/*!
2575 \qmlmethod object QtQuick::Context2D::fill()
2576
2577 Fills the subpaths with the current fill style.
2578
2579 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
2580
2581 \sa fillStyle
2582 */
2583QV4::ReturnedValue QQuickJSContext2DPrototype::method_fill(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2584{
2585 QV4::Scope scope(b);
2586 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2587 CHECK_CONTEXT(r);
2588 r->d()->context()->fill();
2589 RETURN_RESULT(*thisObject);
2590}
2591
2592/*!
2593 \qmlmethod object QtQuick::Context2D::lineTo(real x, real y)
2594
2595 Draws a line from the current position to the point at (\a x, \a y).
2596 */
2597QV4::ReturnedValue QQuickJSContext2DPrototype::method_lineTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2598{
2599 QV4::Scope scope(b);
2600 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2601 CHECK_CONTEXT(r)
2602
2603 if (argc >= 2) {
2604 qreal x = argv[0].toNumber();
2605 qreal y = argv[1].toNumber();
2606
2607 if (!qt_is_finite(x) || !qt_is_finite(y))
2608 RETURN_UNDEFINED();
2609
2610 r->d()->context()->lineTo(x, y);
2611 }
2612
2613 RETURN_RESULT(*thisObject);
2614}
2615
2616/*!
2617 \qmlmethod object QtQuick::Context2D::moveTo(real x, real y)
2618
2619 Creates a new subpath with a point at (\a x, \a y).
2620 */
2621QV4::ReturnedValue QQuickJSContext2DPrototype::method_moveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2622{
2623 QV4::Scope scope(b);
2624 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2625 CHECK_CONTEXT(r)
2626
2627 if (argc >= 2) {
2628 qreal x = argv[0].toNumber();
2629 qreal y = argv[1].toNumber();
2630
2631 if (!qt_is_finite(x) || !qt_is_finite(y))
2632 RETURN_UNDEFINED();
2633 r->d()->context()->moveTo(x, y);
2634 }
2635
2636 RETURN_RESULT(*thisObject);
2637}
2638
2639/*!
2640 \qmlmethod object QtQuick::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
2641
2642 Adds a quadratic bezier curve between the current point and the endpoint
2643 (\a x, \a y) with the control point specified by (\a cpx, \a cpy).
2644
2645 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for quadraticCurveTo}
2646 */
2647QV4::ReturnedValue QQuickJSContext2DPrototype::method_quadraticCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2648{
2649 QV4::Scope scope(b);
2650 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2651 CHECK_CONTEXT(r)
2652
2653 if (argc >= 4) {
2654 qreal cpx = argv[0].toNumber();
2655 qreal cpy = argv[1].toNumber();
2656 qreal x = argv[2].toNumber();
2657 qreal y = argv[3].toNumber();
2658
2659 if (!qt_is_finite(cpx) || !qt_is_finite(cpy) || !qt_is_finite(x) || !qt_is_finite(y))
2660 RETURN_UNDEFINED();
2661
2662 r->d()->context()->quadraticCurveTo(cpx, cpy, x, y);
2663 }
2664
2665 RETURN_RESULT(*thisObject);
2666}
2667
2668/*!
2669 \qmlmethod object QtQuick::Context2D::rect(real x, real y, real w, real h)
2670
2671 Adds a rectangle at position (\a x, \a y), with the given width \a w and
2672 height \a h, as a closed subpath.
2673 */
2674QV4::ReturnedValue QQuickJSContext2DPrototype::method_rect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2675{
2676 QV4::Scope scope(b);
2677 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2678 CHECK_CONTEXT(r)
2679
2680 if (argc >= 4)
2681 r->d()->context()->rect(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2682 RETURN_RESULT(*thisObject);
2683
2684}
2685
2686/*!
2687 \qmlmethod object QtQuick::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius)
2688
2689 Adds a rounded-corner rectangle, specified by (\a x, \a y, \a w, \a h), to the path.
2690 The \a xRadius and \a yRadius arguments specify the radius of the
2691 ellipses defining the corners of the rounded rectangle.
2692 */
2693QV4::ReturnedValue QQuickJSContext2DPrototype::method_roundedRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2694{
2695 QV4::Scope scope(b);
2696 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2697 CHECK_CONTEXT(r)
2698
2699 if (argc >= 6)
2700 r->d()->context()->roundedRect(argv[0].toNumber()
2701 , argv[1].toNumber()
2702 , argv[2].toNumber()
2703 , argv[3].toNumber()
2704 , argv[4].toNumber()
2705 , argv[5].toNumber());
2706 RETURN_RESULT(*thisObject);
2707
2708}
2709
2710/*!
2711 \qmlmethod object QtQuick::Context2D::ellipse(real x, real y, real w, real h)
2712
2713 Creates an ellipse within the bounding rectangle defined by its top-left
2714 corner at (\a x, \a y), width \a w and height \a h, and adds it to the
2715 path as a closed subpath.
2716
2717 The ellipse is composed of a clockwise curve, starting and finishing at
2718 zero degrees (the 3 o'clock position).
2719 */
2720QV4::ReturnedValue QQuickJSContext2DPrototype::method_ellipse(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2721{
2722 QV4::Scope scope(b);
2723 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2724 CHECK_CONTEXT(r)
2725
2726 if (argc >= 4)
2727 r->d()->context()->ellipse(argv[0].toNumber(), argv[1].toNumber(), argv[2].toNumber(), argv[3].toNumber());
2728
2729 RETURN_RESULT(*thisObject);
2730
2731}
2732
2733/*!
2734 \qmlmethod object QtQuick::Context2D::text(string text, real x, real y)
2735
2736 Adds the given \a text to the path as a set of closed subpaths created
2737 from the current context font supplied.
2738
2739 The subpaths are positioned so that the left end of the text's baseline
2740 lies at the point specified by (\a x, \a y).
2741 */
2742QV4::ReturnedValue QQuickJSContext2DPrototype::method_text(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2743{
2744 QV4::Scope scope(b);
2745 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2746 CHECK_CONTEXT(r)
2747
2748 if (argc >= 3) {
2749 qreal x = argv[1].toNumber();
2750 qreal y = argv[2].toNumber();
2751
2752 if (!qt_is_finite(x) || !qt_is_finite(y))
2753 RETURN_UNDEFINED();
2754 r->d()->context()->text(argv[0].toQStringNoThrow(), x, y);
2755 }
2756
2757 RETURN_RESULT(*thisObject);
2758}
2759
2760/*!
2761 \qmlmethod object QtQuick::Context2D::stroke()
2762
2763 Strokes the subpaths with the current stroke style.
2764
2765 \sa strokeStyle, {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
2766 */
2767QV4::ReturnedValue QQuickJSContext2DPrototype::method_stroke(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2768{
2769 QV4::Scope scope(b);
2770 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2771 CHECK_CONTEXT(r)
2772
2773 r->d()->context()->stroke();
2774 RETURN_RESULT(*thisObject);
2775
2776}
2777
2778/*!
2779 \qmlmethod object QtQuick::Context2D::isPointInPath(real x, real y)
2780
2781 Returns \c true if the point (\a x, \a y) is in the current path.
2782
2783 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
2784 */
2785QV4::ReturnedValue QQuickJSContext2DPrototype::method_isPointInPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2786{
2787 QV4::Scope scope(b);
2788 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2789 CHECK_CONTEXT(r)
2790
2791 bool pointInPath = false;
2792 if (argc >= 2)
2793 pointInPath = r->d()->context()->isPointInPath(argv[0].toNumber(), argv[1].toNumber());
2794 RETURN_RESULT(QV4::Value::fromBoolean(pointInPath).asReturnedValue());
2795}
2796
2797QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawFocusRing(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2798{
2799 QV4::Scope scope(b);
2800 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
2801}
2802
2803QV4::ReturnedValue QQuickJSContext2DPrototype::method_setCaretSelectionRect(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2804{
2805 QV4::Scope scope(b);
2806 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
2807}
2808
2809QV4::ReturnedValue QQuickJSContext2DPrototype::method_caretBlinkRate(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
2810{
2811 QV4::Scope scope(b);
2812 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
2813}
2814
2815/*!
2816 \qmlproperty string QtQuick::Context2D::font
2817 Holds the current font settings.
2818
2819 A subset of the
2820 \l {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
2821 is supported:
2822
2823 \list
2824 \li font-style (optional):
2825 normal | italic | oblique
2826 \li font-variant (optional): normal | small-caps
2827 \li font-weight (optional): normal | bold | 1 ... 1000
2828 \li font-size: Npx | Npt (where N is a positive number)
2829 \li font-family: See \l {http://www.w3.org/TR/CSS2/fonts.html#propdef-font-family}
2830 \endlist
2831
2832 \note The font-size and font-family properties are mandatory and must be in
2833 the order they are shown in above. In addition, a font family with spaces in
2834 its name must be quoted.
2835
2836 The default font value is "10px sans-serif".
2837 */
2838QV4::ReturnedValue QQuickJSContext2D::method_get_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2839{
2840 QV4::Scope scope(b);
2841 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2842 CHECK_CONTEXT(r)
2843
2844 RETURN_RESULT(scope.engine->newString(r->d()->context()->state.font.toString()));
2845}
2846
2847QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2848{
2849 QV4::Scope scope(b);
2850 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2852
2853 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2854 if (scope.hasException())
2855 RETURN_UNDEFINED();
2856 QFont font = qt_font_from_string(s->toQString(), r->d()->context()->state.font);
2857 if (font != r->d()->context()->state.font) {
2858 r->d()->context()->state.font = font;
2859 }
2860 RETURN_UNDEFINED();
2861}
2862
2863/*!
2864 \qmlproperty string QtQuick::Context2D::textAlign
2865
2866 Holds the current text alignment settings. The possible values are:
2867
2868 \value "start" (default) Align to the start edge of the text (left side in
2869 left-to-right text, right side in right-to-left text).
2870 \value "end" Align to the end edge of the text (right side in left-to-right
2871 text, left side in right-to-left text).
2872 \value "left" Qt::AlignLeft
2873 \value "right" Qt::AlignRight
2874 \value "center" Qt::AlignHCenter
2875
2876 Other values are ignored.
2877*/
2878QV4::ReturnedValue QQuickJSContext2D::method_get_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2879{
2880 QV4::Scope scope(b);
2881 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2882 CHECK_CONTEXT(r)
2883
2884 switch (r->d()->context()->state.textAlign) {
2885 case QQuickContext2D::End:
2886 RETURN_RESULT(scope.engine->newString(QStringLiteral("end")));
2887 case QQuickContext2D::Left:
2888 RETURN_RESULT(scope.engine->newString(QStringLiteral("left")));
2889 case QQuickContext2D::Right:
2890 RETURN_RESULT(scope.engine->newString(QStringLiteral("right")));
2891 case QQuickContext2D::Center:
2892 RETURN_RESULT(scope.engine->newString(QStringLiteral("center")));
2894 default:
2895 break;
2896 }
2897 RETURN_RESULT(scope.engine->newString(QStringLiteral("start")));
2898}
2899
2900QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2901{
2902 QV4::Scope scope(b);
2903 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2905
2906 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2907 if (scope.hasException())
2908 RETURN_UNDEFINED();
2909 QString textAlign = s->toQString();
2910
2912 if (textAlign == QLatin1String("start"))
2914 else if (textAlign == QLatin1String("end"))
2916 else if (textAlign == QLatin1String("left"))
2918 else if (textAlign == QLatin1String("right"))
2920 else if (textAlign == QLatin1String("center"))
2922 else
2923 RETURN_UNDEFINED();
2924
2925 if (ta != r->d()->context()->state.textAlign)
2926 r->d()->context()->state.textAlign = ta;
2927
2928 RETURN_UNDEFINED();
2929}
2930
2931/*!
2932 \qmlproperty string QtQuick::Context2D::textBaseline
2933
2934 Holds the current baseline alignment settings. The possible values are:
2935
2936 \value "top" The top of the em square
2937 \value "hanging" The hanging baseline
2938 \value "middle" The middle of the em square
2939 \value "alphabetic" (default) The alphabetic baseline
2940 \value "ideographic" The ideographic-under baseline
2941 \value "bottom" The bottom of the em square
2942
2943 Other values are ignored. The default value is "alphabetic".
2944*/
2945QV4::ReturnedValue QQuickJSContext2D::method_get_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
2946{
2947 QV4::Scope scope(b);
2948 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2949 CHECK_CONTEXT(r)
2950
2951 switch (r->d()->context()->state.textBaseline) {
2952 case QQuickContext2D::Hanging:
2953 RETURN_RESULT(scope.engine->newString(QStringLiteral("hanging")));
2954 case QQuickContext2D::Top:
2955 RETURN_RESULT(scope.engine->newString(QStringLiteral("top")));
2956 case QQuickContext2D::Bottom:
2957 RETURN_RESULT(scope.engine->newString(QStringLiteral("bottom")));
2958 case QQuickContext2D::Middle:
2959 RETURN_RESULT(scope.engine->newString(QStringLiteral("middle")));
2960 case QQuickContext2D::Alphabetic:
2961 default:
2962 break;
2963 }
2964 RETURN_RESULT(scope.engine->newString(QStringLiteral("alphabetic")));
2965}
2966
2967QV4::ReturnedValue QQuickJSContext2D::method_set_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
2968{
2969 QV4::Scope scope(b);
2970 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
2972 QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
2973 if (scope.hasException())
2974 RETURN_UNDEFINED();
2975 QString textBaseline = s->toQString();
2976
2977 QQuickContext2D::TextBaseLineType tb;
2978 if (textBaseline == QLatin1String("alphabetic"))
2979 tb = QQuickContext2D::Alphabetic;
2980 else if (textBaseline == QLatin1String("hanging"))
2981 tb = QQuickContext2D::Hanging;
2982 else if (textBaseline == QLatin1String("top"))
2983 tb = QQuickContext2D::Top;
2984 else if (textBaseline == QLatin1String("bottom"))
2985 tb = QQuickContext2D::Bottom;
2986 else if (textBaseline == QLatin1String("middle"))
2987 tb = QQuickContext2D::Middle;
2988 else
2989 RETURN_UNDEFINED();
2990
2991 if (tb != r->d()->context()->state.textBaseline)
2992 r->d()->context()->state.textBaseline = tb;
2993
2994 RETURN_UNDEFINED();
2995}
2996
2997/*!
2998 \qmlmethod object QtQuick::Context2D::fillText(text, x, y)
2999
3000 Fills the specified \a text at the given position (\a x, \a y).
3001
3002 \sa font
3003 \sa textAlign
3004 \sa textBaseline
3005 \sa strokeText
3006 */
3007QV4::ReturnedValue QQuickJSContext2DPrototype::method_fillText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3008{
3009 QV4::Scope scope(b);
3010 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3011 CHECK_CONTEXT(r)
3012
3013 if (argc >= 3) {
3014 qreal x = argv[1].toNumber();
3015 qreal y = argv[2].toNumber();
3016 if (!qt_is_finite(x) || !qt_is_finite(y))
3017 RETURN_UNDEFINED();
3018 QPainterPath textPath = r->d()->context()->createTextGlyphs(x, y, argv[0].toQStringNoThrow());
3019 r->d()->context()->buffer()->fill(textPath);
3020 }
3021
3022 RETURN_RESULT(*thisObject);
3023}
3024/*!
3025 \qmlmethod object QtQuick::Context2D::strokeText(text, x, y)
3026
3027 Strokes the given \a text at a position specified by (\a x, \a y).
3028
3029 \sa font
3030 \sa textAlign
3031 \sa textBaseline
3032 \sa fillText
3033*/
3034QV4::ReturnedValue QQuickJSContext2DPrototype::method_strokeText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3035{
3036 QV4::Scope scope(b);
3037 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3038 CHECK_CONTEXT(r)
3039
3040 if (argc >= 3)
3041 r->d()->context()->drawText(argv[0].toQStringNoThrow(), argv[1].toNumber(), argv[2].toNumber(), false);
3042
3043 RETURN_RESULT(*thisObject);
3044}
3045
3046/*!
3047 \qmlmethod object QtQuick::Context2D::measureText(text)
3048
3049 Returns an object with a \c width property, whose value is equivalent to
3050 calling QFontMetrics::horizontalAdvance() with the given \a text in the
3051 current font.
3052 */
3053QV4::ReturnedValue QQuickJSContext2DPrototype::method_measureText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3054{
3055 QV4::Scope scope(b);
3056 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3057 CHECK_CONTEXT(r)
3058
3059 if (argc >= 1) {
3060 QFontMetrics fm(r->d()->context()->state.font);
3061 uint width = fm.horizontalAdvance(argv[0].toQStringNoThrow());
3062 QV4::ScopedObject tm(scope, scope.engine->newObject());
3063 tm->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("width"))).getPointer(),
3064 QV4::ScopedValue(scope, QV4::Value::fromDouble(width)));
3065 RETURN_RESULT(*tm);
3066 }
3067 RETURN_UNDEFINED();
3068}
3069
3070// drawing images
3071/*!
3072 \qmlmethod QtQuick::Context2D::drawImage(variant image, real dx, real dy)
3073 Draws the given \a image on the canvas at position (\a dx, \a dy).
3074 Note:
3075 The \a image type can be an Image item, an image url or a CanvasImageData object.
3076 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3077 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3078 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3079
3080 \sa CanvasImageData
3081 \sa Image
3082 \sa Canvas::loadImage
3083 \sa Canvas::isImageLoaded
3084 \sa Canvas::imageLoaded
3085
3086 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3087 */
3088/*!
3089 \qmlmethod QtQuick::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
3090 This is an overloaded function.
3091 Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
3092 height \a dh.
3093
3094 Note:
3095 The \a image type can be an Image item, an image url or a CanvasImageData object.
3096 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3097 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3098 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3099
3100 \sa CanvasImageData
3101 \sa Image
3102 \sa Canvas::loadImage()
3103 \sa Canvas::isImageLoaded
3104 \sa Canvas::imageLoaded
3105
3106 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3107 */
3108/*!
3109 \qmlmethod QtQuick::Context2D::drawImage(variant image, real sx, real sy, real sw, real sh, real dx, real dy, real dw, real dh)
3110 This is an overloaded function.
3111 Draws the given item as \a image from source point (\a sx, \a sy) and source width \a sw, source height \a sh
3112 onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
3113
3114
3115 Note:
3116 The \a image type can be an Image or Canvas item, an image url or a CanvasImageData object.
3117 When given as Image item, if the image isn't fully loaded, this method draws nothing.
3118 When given as url string, the image should be loaded by calling Canvas item's Canvas::loadImage() method first.
3119 This image been drawing is subject to the current context clip path, even the given \c image is a CanvasImageData object.
3120
3121 \sa CanvasImageData
3122 \sa Image
3123 \sa Canvas::loadImage()
3124 \sa Canvas::isImageLoaded
3125 \sa Canvas::imageLoaded
3126
3127 \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
3128*/
3129QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3130{
3131 QV4::Scope scope(b);
3132 QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
3133 CHECK_CONTEXT(r)
3134
3135 qreal sx, sy, sw, sh, dx, dy, dw, dh;
3136
3137 if (!argc)
3138 RETURN_UNDEFINED();
3139
3140 //FIXME:This function should be moved to QQuickContext2D::drawImage(...)
3141 if (!r->d()->context()->state.invertibleCTM)
3142 RETURN_UNDEFINED();
3143
3144 QQmlRefPointer<QQuickCanvasPixmap> pixmap;
3145
3146 QV4::ScopedValue arg(scope, argv[0]);
3147 if (arg->isString()) {
3148 QUrl url(arg->toQString());
3149 if (!url.isValid())
3150 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3151
3152 pixmap = r->d()->context()->createPixmap(url);
3153 } else if (arg->isObject()) {
3154 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, arg);
3155 if (!!qobjectWrapper) {
3156 if (QQuickImage *imageItem = qobject_cast<QQuickImage*>(qobjectWrapper->object())) {
3157 pixmap = r->d()->context()->createPixmap(imageItem->source());
3158 } else if (QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(qobjectWrapper->object())) {
3159 QImage img = canvas->toImage();
3160 if (!img.isNull())
3161 pixmap.adopt(new QQuickCanvasPixmap(img));
3162 } else {
3163 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3164 }
3165 } else {
3166 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg);
3167 if (!!imageData) {
3168 QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3169 if (pix && !pix->d()->image->isNull()) {
3170 pixmap.adopt(new QQuickCanvasPixmap(*pix->d()->image));
3171 } else {
3172 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3173 }
3174 } else {
3175 QUrl url(arg->toQStringNoThrow());
3176 if (url.isValid())
3177 pixmap = r->d()->context()->createPixmap(url);
3178 else
3179 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3180 }
3181 }
3182 } else {
3183 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
3184 }
3185
3186 if (pixmap.isNull() || !pixmap->isValid())
3187 RETURN_UNDEFINED();
3188
3189 if (argc >= 9) {
3190 sx = argv[1].toNumber();
3191 sy = argv[2].toNumber();
3192 sw = argv[3].toNumber();
3193 sh = argv[4].toNumber();
3194 dx = argv[5].toNumber();
3195 dy = argv[6].toNumber();
3196 dw = argv[7].toNumber();
3197 dh = argv[8].toNumber();
3198 } else if (argc >= 5) {
3199 sx = 0;
3200 sy = 0;
3201 sw = pixmap->width();
3202 sh = pixmap->height();
3203 dx = argv[1].toNumber();
3204 dy = argv[2].toNumber();
3205 dw = argv[3].toNumber();
3206 dh = argv[4].toNumber();
3207 } else if (argc >= 3) {
3208 dx = argv[1].toNumber();
3209 dy = argv[2].toNumber();
3210 sx = 0;
3211 sy = 0;
3212 sw = pixmap->width();
3213 sh = pixmap->height();
3214 dw = sw;
3215 dh = sh;
3216 } else {
3217 RETURN_UNDEFINED();
3218 }
3219
3220 if (!qt_is_finite(sx)
3221 || !qt_is_finite(sy)
3222 || !qt_is_finite(sw)
3223 || !qt_is_finite(sh)
3224 || !qt_is_finite(dx)
3225 || !qt_is_finite(dy)
3226 || !qt_is_finite(dw)
3227 || !qt_is_finite(dh))
3228 RETURN_UNDEFINED();
3229
3230 if (sx < 0
3231 || sy < 0
3232 || sw == 0
3233 || sh == 0
3234 || sx + sw > pixmap->width()
3235 || sy + sh > pixmap->height()
3236 || sx + sw < 0 || sy + sh < 0) {
3237 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error");
3238 }
3239
3240 r->d()->context()->buffer()->drawPixmap(pixmap, QRectF(sx, sy, sw, sh), QRectF(dx, dy, dw, dh));
3241
3242 RETURN_RESULT(*thisObject);
3243}
3244
3245// pixel manipulation
3246/*!
3247 \qmltype CanvasImageData
3248 \inqmlmodule QtQuick
3249 \ingroup qtquick-canvas
3250 \brief Contains image pixel data in RGBA order.
3251
3252 The CanvasImageData object holds the image pixel data.
3253
3254 The CanvasImageData object has the actual dimensions of the data stored in
3255 this object and holds the one-dimensional array containing the data in RGBA order,
3256 as integers in the range 0 to 255.
3257
3258 \sa width
3259 \sa height
3260 \sa data
3261 \sa Context2D::createImageData()
3262 \sa Context2D::getImageData()
3263 \sa Context2D::putImageData()
3264 */
3265/*!
3266 \qmlproperty int QtQuick::CanvasImageData::width
3267 Holds the actual width dimension of the data in the ImageData object, in device pixels.
3268 */
3269QV4::ReturnedValue QQuickJSContext2DImageData::method_get_width(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3270{
3271 QV4::Scope scope(b);
3272 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3273 if (!imageData)
3274 THROW_TYPE_ERROR();
3275 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3276 int width = r ? r->d()->image->width() : 0;
3277 RETURN_RESULT(QV4::Encode(width));
3278}
3279
3280/*!
3281 \qmlproperty int QtQuick::CanvasImageData::height
3282 Holds the actual height dimension of the data in the ImageData object, in device pixels.
3283 */
3284QV4::ReturnedValue QQuickJSContext2DImageData::method_get_height(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3285{
3286 QV4::Scope scope(b);
3287 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3288 if (!imageData)
3289 THROW_TYPE_ERROR();
3290 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3291 int height = r ? r->d()->image->height() : 0;
3292 RETURN_RESULT(QV4::Encode(height));
3293}
3294
3295/*!
3296 \qmlproperty object QtQuick::CanvasImageData::data
3297 Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
3298 */
3299QV4::ReturnedValue QQuickJSContext2DImageData::method_get_data(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3300{
3301 QV4::Scope scope(b);
3302 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, *thisObject);
3303 if (!imageData)
3304 THROW_TYPE_ERROR();
3305 RETURN_RESULT(imageData->d()->pixelData);
3306}
3307
3308/*!
3309 \qmltype CanvasPixelArray
3310 \inqmlmodule QtQuick
3311 \ingroup qtquick-canvas
3312 \brief Provides ordered and indexed access to the components of each pixel in image data.
3313
3314 The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
3315 The CanvasPixelArray can be accessed as normal Javascript array.
3316 \sa CanvasImageData
3317 \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
3318 */
3319
3320/*!
3321 \qmlproperty int QtQuick::CanvasPixelArray::length
3322 The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
3323 The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
3324 This property is read only.
3325*/
3326QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
3327{
3328 QV4::Scope scope(b);
3329 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, thisObject->as<QQuickJSContext2DPixelData>());
3330 if (!r || r->d()->image->isNull())
3331 RETURN_UNDEFINED();
3332
3333 RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4));
3334}
3335
3336QV4::ReturnedValue QQuickJSContext2DPixelData::virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
3337{
3338 if (!id.isArrayIndex())
3339 return QV4::Object::virtualGet(m, id, receiver, hasProperty);
3340
3341 uint index = id.asArrayIndex();
3342 Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
3343 QV4::ExecutionEngine *v4 = static_cast<const QQuickJSContext2DPixelData *>(m)->engine();
3344 QV4::Scope scope(v4);
3345 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<const QQuickJSContext2DPixelData *>(m));
3346
3347 if (index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4)) {
3348 if (hasProperty)
3349 *hasProperty = true;
3350 const quint32 w = r->d()->image->width();
3351 const quint32 row = (index / 4) / w;
3352 const quint32 col = (index / 4) % w;
3353 const QRgb* pixel = reinterpret_cast<const QRgb*>(r->d()->image->constScanLine(row));
3354 pixel += col;
3355 switch (index % 4) {
3356 case 0:
3357 return QV4::Encode(qRed(*pixel));
3358 case 1:
3359 return QV4::Encode(qGreen(*pixel));
3360 case 2:
3361 return QV4::Encode(qBlue(*pixel));
3362 case 3:
3363 return QV4::Encode(qAlpha(*pixel));
3364 }
3365 }
3366
3367 if (hasProperty)
3368 *hasProperty = false;
3369 return QV4::Encode::undefined();
3370}
3371
3372bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, QV4::Value *receiver)
3373{
3374 if (!id.isArrayIndex())
3375 return Object::virtualPut(m, id, value, receiver);
3376
3377 Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
3378 QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine();
3379 QV4::Scope scope(v4);
3380 if (scope.hasException())
3381 return false;
3382
3383 uint index = id.asArrayIndex();
3384 QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
3385
3386 const int v = value.toInt32();
3387 if (r && index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4) && v >= 0 && v <= 255) {
3388 const quint32 w = r->d()->image->width();
3389 const quint32 row = (index / 4) / w;
3390 const quint32 col = (index / 4) % w;
3391
3392 QRgb* pixel = reinterpret_cast<QRgb*>(r->d()->image->scanLine(row));
3393 pixel += col;
3394 switch (index % 4) {
3395 case 0:
3396 *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel));
3397 break;
3398 case 1:
3399 *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel));
3400 break;
3401 case 2:
3402 *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel));
3403 break;
3404 case 3:
3405 *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v);
3406 break;
3407 }
3408 return true;
3409 }
3410
3411 return false;
3412}
3413/*!
3414 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(real sw, real sh)
3415
3416 Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
3417*/
3418/*!
3419 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(CanvasImageData imageData)
3420
3421 Creates a CanvasImageData object with the same dimensions as the \a imageData argument.
3422*/
3423/*!
3424 \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(Url imageUrl)
3425
3426 Creates a CanvasImageData object with the given image loaded from \a imageUrl.
3427
3428 \note The \a imageUrl must be already loaded before this function call,
3429 otherwise an empty CanvasImageData obect will be returned.
3430
3431 \sa Canvas::loadImage(), QtQuick::Canvas::unloadImage(),
3432 QtQuick::Canvas::isImageLoaded
3433 */
3434QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3435{
3436 QV4::Scope scope(b);
3437 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3438 CHECK_CONTEXT(r)
3439
3440 if (argc == 1) {
3441 QV4::ScopedValue arg0(scope, argv[0]);
3442 QV4::Scoped<QQuickJSContext2DImageData> imgData(scope, arg0);
3443 if (!!imgData) {
3444 QV4::Scoped<QQuickJSContext2DPixelData> pa(scope, imgData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3445 if (pa) {
3446 qreal w = pa->d()->image->width();
3447 qreal h = pa->d()->image->height();
3448 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, QImage()));
3449 }
3450 } else if (arg0->isString()) {
3451 QImage image = r->d()->context()->createPixmap(QUrl(arg0->toQStringNoThrow()))->image();
3452 RETURN_RESULT(qt_create_image_data(image.width(), image.height(), scope.engine, std::move(image)));
3453 }
3454 } else if (argc == 2) {
3455 qreal w = argv[0].toNumber();
3456 qreal h = argv[1].toNumber();
3457
3458 if (!qt_is_finite(w) || !qt_is_finite(h))
3459 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments");
3460
3461 if (w > 0 && h > 0)
3462 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, QImage()));
3463 else
3464 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments");
3465 }
3466 RETURN_UNDEFINED();
3467}
3468
3469/*!
3470 \qmlmethod CanvasImageData QtQuick::Context2D::getImageData(real x, real y, real w, real h)
3471
3472 Returns an CanvasImageData object containing the image data for the canvas
3473 rectangle specified by (\a x, \a y, \a w, \a h).
3474 */
3475QV4::ReturnedValue QQuickJSContext2DPrototype::method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3476{
3477 QV4::Scope scope(b);
3478 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3479 CHECK_CONTEXT(r)
3480
3481 if (argc >= 4) {
3482 qreal x = argv[0].toNumber();
3483 qreal y = argv[1].toNumber();
3484 qreal w = argv[2].toNumber();
3485 qreal h = argv[3].toNumber();
3486 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3487 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments");
3488
3489 if (w <= 0 || h <= 0)
3490 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
3491
3492 QImage image = r->d()->context()->canvas()->toImage(QRectF(x, y, w, h));
3493 RETURN_RESULT(qt_create_image_data(w, h, scope.engine, std::move(image)));
3494 }
3495 RETURN_RESULT(QV4::Encode::null());
3496}
3497
3498/*!
3499 \qmlmethod object QtQuick::Context2D::putImageData(CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
3500
3501 Paints the data from the given \a imageData object onto the canvas at
3502 (\a dx, \a dy).
3503
3504 If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight)
3505 is provided, only the pixels from that rectangle are painted.
3506 */
3507QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3508{
3509 QV4::Scope scope(b);
3510 QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
3511 CHECK_CONTEXT(r)
3512 if (argc < 7)
3513 RETURN_UNDEFINED();
3514
3515 QV4::ScopedValue arg0(scope, argv[0]);
3516 if (!arg0->isObject())
3517 THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch");
3518
3519 qreal dx = argv[1].toNumber();
3520 qreal dy = argv[2].toNumber();
3521 qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
3522
3523 if (!qt_is_finite(dx) || !qt_is_finite(dy))
3524 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
3525
3526 QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg0);
3527 if (!imageData)
3528 RETURN_UNDEFINED();
3529
3530 QV4::Scoped<QQuickJSContext2DPixelData> pixelArray(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
3531 if (pixelArray) {
3532 w = pixelArray->d()->image->width();
3533 h = pixelArray->d()->image->height();
3534
3535 if (argc == 7) {
3536 dirtyX = argv[3].toNumber();
3537 dirtyY = argv[4].toNumber();
3538 dirtyWidth = argv[5].toNumber();
3539 dirtyHeight = argv[6].toNumber();
3540
3541 if (!qt_is_finite(dirtyX) || !qt_is_finite(dirtyY) || !qt_is_finite(dirtyWidth) || !qt_is_finite(dirtyHeight))
3542 THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
3543
3544
3545 if (dirtyWidth < 0) {
3546 dirtyX = dirtyX+dirtyWidth;
3547 dirtyWidth = -dirtyWidth;
3548 }
3549
3550 if (dirtyHeight < 0) {
3551 dirtyY = dirtyY+dirtyHeight;
3552 dirtyHeight = -dirtyHeight;
3553 }
3554
3555 if (dirtyX < 0) {
3556 dirtyWidth = dirtyWidth+dirtyX;
3557 dirtyX = 0;
3558 }
3559
3560 if (dirtyY < 0) {
3561 dirtyHeight = dirtyHeight+dirtyY;
3562 dirtyY = 0;
3563 }
3564
3565 if (dirtyX+dirtyWidth > w) {
3566 dirtyWidth = w - dirtyX;
3567 }
3568
3569 if (dirtyY+dirtyHeight > h) {
3570 dirtyHeight = h - dirtyY;
3571 }
3572
3573 if (dirtyWidth <=0 || dirtyHeight <= 0)
3574 RETURN_UNDEFINED();
3575 } else {
3576 dirtyX = 0;
3577 dirtyY = 0;
3578 dirtyWidth = w;
3579 dirtyHeight = h;
3580 }
3581
3582 QImage image = pixelArray->d()->image->copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
3583 r->d()->context()->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
3584 }
3585
3586 RETURN_RESULT(*thisObject);
3587}
3588
3589/*!
3590 \qmltype CanvasGradient
3591 \inqmlmodule QtQuick
3592 \since 5.0
3593 \ingroup qtquick-canvas
3594 \brief Provides an opaque CanvasGradient interface.
3595 */
3596
3597/*!
3598 \qmlmethod CanvasGradient QtQuick::CanvasGradient::addColorStop(real offset, string color)
3599
3600 Adds a color stop with the given \a color to the gradient at the given \a offset.
3601 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
3602
3603 For example:
3604
3605 \code
3606 var gradient = ctx.createLinearGradient(0, 0, 100, 100);
3607 gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
3608 gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1)');
3609 \endcode
3610 */
3611QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
3612{
3613 QV4::Scope scope(b);
3614 QV4::Scoped<QQuickContext2DStyle> style(scope, thisObject->as<QQuickContext2DStyle>());
3615 if (!style)
3616 THROW_GENERIC_ERROR("Not a CanvasGradient object");
3617
3618 if (argc == 2) {
3619
3620 if (!style->d()->brush->gradient())
3621 THROW_GENERIC_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
3622 QGradient gradient = *(style->d()->brush->gradient());
3623 qreal pos = argv[0].toNumber();
3624 QColor color;
3625
3626 if (argv[1].as<Object>()) {
3627 color = QV4::ExecutionEngine::toVariant(
3628 argv[1], QMetaType::fromType<QColor>()).value<QColor>();
3629 } else {
3630 color = qt_color_from_string(argv[1]);
3631 }
3632 if (pos < 0.0 || pos > 1.0 || !qt_is_finite(pos)) {
3633 THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range");
3634 }
3635
3636 if (color.isValid()) {
3637 gradient.setColorAt(pos, color);
3638 } else {
3639 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
3640 }
3641 *style->d()->brush = gradient;
3642 }
3643
3644 return thisObject->asReturnedValue();
3645}
3646
3647void QQuickContext2D::scale(qreal x, qreal y)
3648{
3649 if (!state.invertibleCTM)
3650 return;
3651
3652 if (!qt_is_finite(x) || !qt_is_finite(y))
3653 return;
3654
3655 QTransform newTransform = state.matrix;
3656 newTransform.scale(x, y);
3657
3658 if (!newTransform.isInvertible()) {
3659 state.invertibleCTM = false;
3660 return;
3661 }
3662
3663 state.matrix = newTransform;
3664 buffer()->updateMatrix(state.matrix);
3665 m_path = QTransform().scale(1.0 / x, 1.0 / y).map(m_path);
3666}
3667
3668void QQuickContext2D::rotate(qreal angle)
3669{
3670 if (!state.invertibleCTM)
3671 return;
3672
3673 if (!qt_is_finite(angle))
3674 return;
3675
3676 QTransform newTransform =state.matrix;
3677 newTransform.rotate(qRadiansToDegrees(angle));
3678
3679 if (!newTransform.isInvertible()) {
3680 state.invertibleCTM = false;
3681 return;
3682 }
3683
3684 state.matrix = newTransform;
3685 buffer()->updateMatrix(state.matrix);
3686 m_path = QTransform().rotate(-qRadiansToDegrees(angle)).map(m_path);
3687}
3688
3689void QQuickContext2D::shear(qreal h, qreal v)
3690{
3691 if (!state.invertibleCTM)
3692 return;
3693
3694 if (!qt_is_finite(h) || !qt_is_finite(v))
3695 return ;
3696
3697 QTransform newTransform = state.matrix;
3698 newTransform.shear(h, v);
3699
3700 if (!newTransform.isInvertible()) {
3701 state.invertibleCTM = false;
3702 return;
3703 }
3704
3705 state.matrix = newTransform;
3706 buffer()->updateMatrix(state.matrix);
3707 m_path = QTransform().shear(-h, -v).map(m_path);
3708}
3709
3710void QQuickContext2D::translate(qreal x, qreal y)
3711{
3712 if (!state.invertibleCTM)
3713 return;
3714
3715 if (!qt_is_finite(x) || !qt_is_finite(y))
3716 return ;
3717
3718 QTransform newTransform = state.matrix;
3719 newTransform.translate(x, y);
3720
3721 if (!newTransform.isInvertible()) {
3722 state.invertibleCTM = false;
3723 return;
3724 }
3725
3726 state.matrix = newTransform;
3727 buffer()->updateMatrix(state.matrix);
3728 m_path = QTransform().translate(-x, -y).map(m_path);
3729}
3730
3731void QQuickContext2D::transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
3732{
3733 if (!state.invertibleCTM)
3734 return;
3735
3736 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))
3737 return;
3738
3739 QTransform transform(a, b, c, d, e, f);
3740 QTransform newTransform = state.matrix * transform;
3741
3742 if (!newTransform.isInvertible()) {
3743 state.invertibleCTM = false;
3744 return;
3745 }
3746 state.matrix = newTransform;
3747 buffer()->updateMatrix(state.matrix);
3748 m_path = transform.inverted().map(m_path);
3749}
3750
3751void QQuickContext2D::setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
3752{
3753 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))
3754 return;
3755
3756 QTransform ctm = state.matrix;
3757 if (!ctm.isInvertible())
3758 return;
3759
3760 state.matrix = ctm.inverted() * state.matrix;
3761 m_path = ctm.map(m_path);
3762 state.invertibleCTM = true;
3763 transform(a, b, c, d, e, f);
3764}
3765
3767{
3768 if (!state.invertibleCTM)
3769 return;
3770
3771 if (!m_path.elementCount())
3772 return;
3773
3774 m_path.setFillRule(state.fillRule);
3775 buffer()->fill(m_path);
3776}
3777
3779{
3780 if (!state.invertibleCTM)
3781 return;
3782
3783 QPainterPath clipPath = m_path;
3784 clipPath.closeSubpath();
3785 if (state.clip) {
3786 state.clipPath = clipPath.intersected(state.clipPath);
3787 } else {
3788 state.clip = true;
3789 state.clipPath = clipPath;
3790 }
3791 buffer()->clip(state.clip, state.clipPath);
3792}
3793
3795{
3796 if (!state.invertibleCTM)
3797 return;
3798
3799 if (!m_path.elementCount())
3800 return;
3801
3802 buffer()->stroke(m_path);
3803}
3804
3805void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
3806{
3807 if (!state.invertibleCTM)
3808 return;
3809
3810 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3811 return;
3812
3813 buffer()->fillRect(QRectF(x, y, w, h));
3814}
3815
3816void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
3817{
3818 if (!state.invertibleCTM)
3819 return;
3820
3821 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3822 return;
3823
3824 buffer()->strokeRect(QRectF(x, y, w, h));
3825}
3826
3827void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
3828{
3829 if (!state.invertibleCTM)
3830 return;
3831
3832 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
3833 return;
3834
3835 buffer()->clearRect(QRectF(x, y, w, h));
3836}
3837
3838void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill)
3839{
3840 if (!state.invertibleCTM)
3841 return;
3842
3843 if (!qt_is_finite(x) || !qt_is_finite(y))
3844 return;
3845
3846 QPainterPath textPath = createTextGlyphs(x, y, text);
3847 if (fill)
3848 buffer()->fill(textPath);
3849 else
3850 buffer()->stroke(textPath);
3851}
3852
3853
3855{
3856 if (!m_path.elementCount())
3857 return;
3858 m_path = QPainterPath();
3859}
3860
3862{
3863 if (!m_path.elementCount())
3864 return;
3865
3866 QRectF boundRect = m_path.boundingRect();
3867 if (boundRect.width() || boundRect.height())
3868 m_path.closeSubpath();
3869 //FIXME:QPainterPath set the current point to (0,0) after close subpath
3870 //should be the first point of the previous subpath
3871}
3872
3873void QQuickContext2D::moveTo( qreal x, qreal y)
3874{
3875 if (!state.invertibleCTM)
3876 return;
3877
3878 //FIXME: moveTo should not close the previous subpath
3879 m_path.moveTo(QPointF(x, y));
3880}
3881
3882void QQuickContext2D::lineTo( qreal x, qreal y)
3883{
3884 if (!state.invertibleCTM)
3885 return;
3886
3887 QPointF pt(x, y);
3888
3889 if (!m_path.elementCount())
3890 m_path.moveTo(pt);
3891 else if (m_path.currentPosition() != pt)
3892 m_path.lineTo(pt);
3893}
3894
3895void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
3896 qreal x, qreal y)
3897{
3898 if (!state.invertibleCTM)
3899 return;
3900
3901 if (!m_path.elementCount())
3902 m_path.moveTo(QPointF(cpx, cpy));
3903
3904 QPointF pt(x, y);
3905 if (m_path.currentPosition() != pt)
3906 m_path.quadTo(QPointF(cpx, cpy), pt);
3907}
3908
3909void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
3910 qreal cp2x, qreal cp2y,
3911 qreal x, qreal y)
3912{
3913 if (!state.invertibleCTM)
3914 return;
3915
3916 if (!m_path.elementCount())
3917 m_path.moveTo(QPointF(cp1x, cp1y));
3918
3919 QPointF pt(x, y);
3920 if (m_path.currentPosition() != pt)
3921 m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y), pt);
3922}
3923
3924void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, qreal radius)
3925{
3926 QPointF p0(m_path.currentPosition());
3927
3928 QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
3929 QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
3930 qreal p1p0_length = std::hypot(p1p0.x(), p1p0.y());
3931 qreal p1p2_length = std::hypot(p1p2.x(), p1p2.y());
3932
3933 qreal cos_phi = QPointF::dotProduct(p1p0, p1p2) / (p1p0_length * p1p2_length);
3934
3935 // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
3936 // We could have used areCollinear() here, but since we're reusing
3937 // the variables computed above later on we keep this logic.
3938 if (qFuzzyCompare(std::abs(cos_phi), qreal(1.0))) {
3939 m_path.lineTo(p1);
3940 return;
3941 }
3942
3943 qreal tangent = radius / std::tan(std::acos(cos_phi) / 2);
3944 qreal factor_p1p0 = tangent / p1p0_length;
3945 QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
3946
3947 QPointF orth_p1p0(p1p0.y(), -p1p0.x());
3948 qreal orth_p1p0_length = std::hypot(orth_p1p0.x(), orth_p1p0.y());
3949 qreal factor_ra = radius / orth_p1p0_length;
3950
3951 // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
3952 qreal cos_alpha = QPointF::dotProduct(orth_p1p0, p1p2) / (orth_p1p0_length * p1p2_length);
3953 if (cos_alpha < 0.f)
3954 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3955
3956 QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
3957
3958 // calculate angles for addArc
3959 orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
3960 qreal sa = std::atan2(orth_p1p0.y(), orth_p1p0.x());
3961
3962 // anticlockwise logic
3963 bool anticlockwise = false;
3964
3965 qreal factor_p1p2 = tangent / p1p2_length;
3966 QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
3967 QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
3968 qreal ea = std::atan2(orth_p1p2.y(), orth_p1p2.x());
3969 if ((sa > ea) && ((sa - ea) < M_PI))
3970 anticlockwise = true;
3971 if ((sa < ea) && ((ea - sa) > M_PI))
3972 anticlockwise = true;
3973
3974 arc(p.x(), p.y(), radius, sa, ea, anticlockwise);
3975}
3976
3977void QQuickContext2D::arcTo(qreal x1, qreal y1,
3978 qreal x2, qreal y2,
3979 qreal radius)
3980{
3981 if (!state.invertibleCTM)
3982 return;
3983
3984 if (!qt_is_finite(x1) || !qt_is_finite(y1) || !qt_is_finite(x2) || !qt_is_finite(y2) || !qt_is_finite(radius))
3985 return;
3986
3987 QPointF st(x1, y1);
3988 QPointF end(x2, y2);
3989
3990 if (!m_path.elementCount())
3991 m_path.moveTo(st);
3992 else if (st == m_path.currentPosition() || st == end || !radius)
3993 lineTo(x1, y1);
3994 else
3995 addArcTo(st, end, radius);
3996 }
3997
3998void QQuickContext2D::rect(qreal x, qreal y, qreal w, qreal h)
3999{
4000 if (!state.invertibleCTM)
4001 return;
4002 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
4003 return;
4004
4005 if (!w && !h) {
4006 m_path.moveTo(x, y);
4007 return;
4008 }
4009 m_path.addRect(x, y, w, h);
4010}
4011
4012void QQuickContext2D::roundedRect(qreal x, qreal y,
4013 qreal w, qreal h,
4014 qreal xr, qreal yr)
4015{
4016 if (!state.invertibleCTM)
4017 return;
4018
4019 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))
4020 return;
4021
4022 if (!w && !h) {
4023 m_path.moveTo(x, y);
4024 return;
4025 }
4026 m_path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
4027}
4028
4029void QQuickContext2D::ellipse(qreal x, qreal y,
4030 qreal w, qreal h)
4031{
4032 if (!state.invertibleCTM)
4033 return;
4034
4035 if (!qt_is_finite(x) || !qt_is_finite(y) || !qt_is_finite(w) || !qt_is_finite(h))
4036 return;
4037
4038 if (!w && !h) {
4039 m_path.moveTo(x, y);
4040 return;
4041 }
4042
4043 m_path.addEllipse(x, y, w, h);
4044}
4045
4046void QQuickContext2D::text(const QString& str, qreal x, qreal y)
4047{
4048 if (!state.invertibleCTM)
4049 return;
4050
4051 QPainterPath path;
4052 path.addText(x, y, state.font, str);
4053 m_path.addPath(path);
4054}
4055
4056void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise)
4057{
4058 if (!state.invertibleCTM)
4059 return;
4060
4061 if (!qt_is_finite(xc) || !qt_is_finite(yc) || !qt_is_finite(sar) || !qt_is_finite(ear) || !qt_is_finite(radius))
4062 return;
4063
4064 if (sar == ear)
4065 return;
4066
4067
4068 //### HACK
4069
4070 // In Qt we don't switch the coordinate system for degrees
4071 // and still use the 0,0 as bottom left for degrees so we need
4072 // to switch
4073 sar = -sar;
4074 ear = -ear;
4075 antiClockWise = !antiClockWise;
4076 //end hack
4077
4078 float sa = qRadiansToDegrees(sar);
4079 float ea = qRadiansToDegrees(ear);
4080
4081 double span = 0;
4082
4083 double xs = xc - radius;
4084 double ys = yc - radius;
4085 double width = radius*2;
4086 double height = radius*2;
4087 if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360)))
4088 // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
4089 // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
4090 // circumference of this circle.
4091 span = 360;
4092 else {
4093 if (!antiClockWise && (ea < sa)) {
4094 span += 360;
4095 } else if (antiClockWise && (sa < ea)) {
4096 span -= 360;
4097 }
4098 //### this is also due to switched coordinate system
4099 // we would end up with a 0 span instead of 360
4100 if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
4101 qFuzzyCompare(qAbs(span), 360))) {
4102 span += ea - sa;
4103 }
4104 }
4105
4106 // If the path is empty, move to where the arc will start to avoid painting a line from (0,0)
4107 if (!m_path.elementCount())
4108 m_path.arcMoveTo(xs, ys, width, height, sa);
4109 else if (!radius) {
4110 m_path.lineTo(xc, yc);
4111 return;
4112 }
4113
4114 m_path.arcTo(xs, ys, width, height, sa, span);
4115}
4116
4117int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
4118{
4119 int offset = 0;
4120 switch (value) {
4121 case QQuickContext2D::Top:
4122 case QQuickContext2D::Hanging:
4123 break;
4124 case QQuickContext2D::Middle:
4125 offset = (metrics.ascent() >> 1) + metrics.height() - metrics.ascent();
4126 break;
4127 case QQuickContext2D::Alphabetic:
4128 offset = metrics.ascent();
4129 break;
4130 case QQuickContext2D::Bottom:
4131 offset = metrics.height();
4132 break;
4133 }
4134 return offset;
4135}
4136
4137static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
4138{
4139 int offset = 0;
4140 if (value == QQuickContext2D::Start)
4141 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Left : QQuickContext2D::Right;
4142 else if (value == QQuickContext2D::End)
4143 value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Right: QQuickContext2D::Left;
4144 switch (value) {
4146 offset = metrics.horizontalAdvance(text) / 2;
4147 break;
4149 offset = metrics.horizontalAdvance(text);
4150 break;
4152 default:
4153 break;
4154 }
4155 return offset;
4156}
4157
4158void QQuickContext2D::setGrabbedImage(const QImage& grab)
4159{
4160 m_grabbedImage = grab;
4161 m_grabbed = true;
4162}
4163
4164QQmlRefPointer<QQuickCanvasPixmap> QQuickContext2D::createPixmap(const QUrl& url, QSizeF sourceSize)
4165{
4166 return m_canvas->loadedPixmap(url, sourceSize);
4167}
4168
4169QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
4170{
4171 const QFontMetrics metrics(state.font);
4172 int yoffset = baseLineOffset(static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
4173 int xoffset = textAlignOffset(static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
4174
4175 QPainterPath textPath;
4176
4177 textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
4178 return textPath;
4179}
4180
4181
4182static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c)
4183{
4184 // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx)
4185 return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x()));
4186}
4187
4188static inline bool withinRange(qreal p, qreal a, qreal b)
4189{
4190 return (p >= a && p <= b) || (p >= b && p <= a);
4191}
4192
4193bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
4194{
4195 if (!state.invertibleCTM)
4196 return false;
4197
4198 if (!m_path.elementCount())
4199 return false;
4200
4201 if (!qt_is_finite(x) || !qt_is_finite(y))
4202 return false;
4203
4204 QPointF point(x, y);
4205 QTransform ctm = state.matrix;
4206 QPointF p = ctm.inverted().map(point);
4207 if (!qt_is_finite(p.x()) || !qt_is_finite(p.y()))
4208 return false;
4209
4210 const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule);
4211
4212 bool contains = m_path.contains(p);
4213
4214 if (!contains) {
4215 // check whether the point is on the border
4216 QPolygonF border = m_path.toFillPolygon();
4217
4218 QPointF p1 = border.at(0);
4219 QPointF p2;
4220
4221 for (int i = 1; i < border.size(); ++i) {
4222 p2 = border.at(i);
4223 if (areCollinear(p, p1, p2)
4224 // Once we know that the points are collinear we
4225 // only need to check one of the coordinates
4226 && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ?
4227 withinRange(p.x(), p1.x(), p2.x()) :
4228 withinRange(p.y(), p1.y(), p2.y()))) {
4229 return true;
4230 }
4231 p1 = p2;
4232 }
4233 }
4234 return contains;
4235}
4236
4237QMutex QQuickContext2D::mutex;
4238
4242 , m_v4engine(nullptr)
4243 , m_surface(nullptr)
4244 , m_thread(nullptr)
4245 , m_grabbed(false)
4246{
4247}
4248
4250{
4251 mutex.lock();
4252 m_texture->setItem(nullptr);
4253 delete m_buffer;
4254 m_texture->deleteLater();
4255
4256 mutex.unlock();
4257}
4258
4260{
4261 return m_v4value.value();
4262}
4263
4265{
4266 return QStringList() << QStringLiteral("2d");
4267}
4268
4269void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args)
4270{
4271 Q_UNUSED(args);
4272
4273 m_canvas = canvasItem;
4274 m_renderTarget = canvasItem->renderTarget();
4275 m_renderStrategy = canvasItem->renderStrategy();
4276
4277 // Disable threaded background rendering if the platform has issues with it
4278 if (m_renderTarget == QQuickCanvasItem::FramebufferObject
4279 && m_renderStrategy == QQuickCanvasItem::Threaded
4280 && !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) {
4281 m_renderTarget = QQuickCanvasItem::Image;
4282 }
4283
4284 // Disable framebuffer object based rendering always in Qt 6. It
4285 // is not implemented in the new RHI-based graphics stack, but the
4286 // enum value is still present. Switch to Image instead.
4287 if (m_renderTarget == QQuickCanvasItem::FramebufferObject)
4288 m_renderTarget = QQuickCanvasItem::Image;
4289
4290 m_texture = new QQuickContext2DImageTexture;
4291
4292 m_texture->setItem(canvasItem);
4293 m_texture->setCanvasWindow(canvasItem->canvasWindow().toRect());
4294 m_texture->setTileSize(canvasItem->tileSize());
4295 m_texture->setCanvasSize(canvasItem->canvasSize().toSize());
4296 m_texture->setSmooth(canvasItem->smooth());
4297 m_texture->setAntialiasing(canvasItem->antialiasing());
4298 m_texture->setOnCustomThread(m_renderStrategy == QQuickCanvasItem::Threaded);
4299 m_thread = QThread::currentThread();
4300
4301 QThread *renderThread = m_thread;
4302 if (m_renderStrategy == QQuickCanvasItem::Threaded)
4303 renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
4304 if (renderThread && renderThread != QThread::currentThread())
4305 m_texture->moveToThread(renderThread);
4306 connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
4307
4308 reset();
4309}
4310
4311void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing)
4312{
4313 if (m_texture->thread() == QThread::currentThread()) {
4314 m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth, antialiasing);
4315 } else {
4316 QEvent *e = new QQuickContext2DTexture::CanvasChangeEvent(canvasSize,
4317 tileSize,
4318 canvasWindow,
4319 dirtyRect,
4320 smooth,
4321 antialiasing);
4322 QCoreApplication::postEvent(m_texture, e);
4323 }
4324}
4325
4327{
4328 if (m_buffer) {
4329 if (m_texture->thread() == QThread::currentThread())
4330 m_texture->paint(m_buffer);
4331 else
4332 QCoreApplication::postEvent(m_texture, new QQuickContext2DTexture::PaintEvent(m_buffer));
4333 }
4335}
4336
4338{
4339 return m_texture;
4340}
4341
4342QImage QQuickContext2D::toImage(const QRectF& bounds)
4343{
4344 if (m_texture->thread() == QThread::currentThread()) {
4345 flush();
4346 m_texture->grabImage(bounds);
4347 } else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
4348 qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode";
4349 return QImage();
4350 } else {
4351 flush();
4352 QCoreApplication::postEvent(m_texture, new QEvent(QEvent::Type(QEvent::User + 10)));
4353 QMetaObject::invokeMethod(m_texture,
4354 "grabImage",
4355 Qt::BlockingQueuedConnection,
4356 Q_ARG(QRectF, bounds));
4357 }
4358 QImage img = m_grabbedImage;
4359 m_grabbedImage = QImage();
4360 m_grabbed = false;
4361 return img;
4362}
4363
4364
4366{
4367 QV4::Scope scope(v4);
4368
4369 QV4::ScopedObject proto(scope, QQuickJSContext2DPrototype::create(v4));
4370 proto->defineAccessorProperty(QStringLiteral("strokeStyle"), QQuickJSContext2D::method_get_strokeStyle, QQuickJSContext2D::method_set_strokeStyle);
4371 proto->defineAccessorProperty(QStringLiteral("font"), QQuickJSContext2D::method_get_font, QQuickJSContext2D::method_set_font);
4372 proto->defineAccessorProperty(QStringLiteral("fillRule"), QQuickJSContext2D::method_get_fillRule, QQuickJSContext2D::method_set_fillRule);
4373 proto->defineAccessorProperty(QStringLiteral("globalAlpha"), QQuickJSContext2D::method_get_globalAlpha, QQuickJSContext2D::method_set_globalAlpha);
4374 proto->defineAccessorProperty(QStringLiteral("lineCap"), QQuickJSContext2D::method_get_lineCap, QQuickJSContext2D::method_set_lineCap);
4375 proto->defineAccessorProperty(QStringLiteral("shadowOffsetX"), QQuickJSContext2D::method_get_shadowOffsetX, QQuickJSContext2D::method_set_shadowOffsetX);
4376 proto->defineAccessorProperty(QStringLiteral("shadowOffsetY"), QQuickJSContext2D::method_get_shadowOffsetY, QQuickJSContext2D::method_set_shadowOffsetY);
4377 proto->defineAccessorProperty(QStringLiteral("globalCompositeOperation"), QQuickJSContext2D::method_get_globalCompositeOperation, QQuickJSContext2D::method_set_globalCompositeOperation);
4378 proto->defineAccessorProperty(QStringLiteral("miterLimit"), QQuickJSContext2D::method_get_miterLimit, QQuickJSContext2D::method_set_miterLimit);
4379 proto->defineAccessorProperty(QStringLiteral("fillStyle"), QQuickJSContext2D::method_get_fillStyle, QQuickJSContext2D::method_set_fillStyle);
4380 proto->defineAccessorProperty(QStringLiteral("shadowColor"), QQuickJSContext2D::method_get_shadowColor, QQuickJSContext2D::method_set_shadowColor);
4381 proto->defineAccessorProperty(QStringLiteral("textBaseline"), QQuickJSContext2D::method_get_textBaseline, QQuickJSContext2D::method_set_textBaseline);
4382#if QT_CONFIG(quick_path)
4383 proto->defineAccessorProperty(QStringLiteral("path"), QQuickJSContext2D::method_get_path, QQuickJSContext2D::method_set_path);
4384#endif
4385 proto->defineAccessorProperty(QStringLiteral("lineJoin"), QQuickJSContext2D::method_get_lineJoin, QQuickJSContext2D::method_set_lineJoin);
4386 proto->defineAccessorProperty(QStringLiteral("lineWidth"), QQuickJSContext2D::method_get_lineWidth, QQuickJSContext2D::method_set_lineWidth);
4387 proto->defineAccessorProperty(QStringLiteral("textAlign"), QQuickJSContext2D::method_get_textAlign, QQuickJSContext2D::method_set_textAlign);
4388 proto->defineAccessorProperty(QStringLiteral("shadowBlur"), QQuickJSContext2D::method_get_shadowBlur, QQuickJSContext2D::method_set_shadowBlur);
4389 proto->defineAccessorProperty(QStringLiteral("lineDashOffset"), QQuickJSContext2D::method_get_lineDashOffset, QQuickJSContext2D::method_set_lineDashOffset);
4390 contextPrototype = proto;
4391
4392 proto = scope.engine->newObject();
4393 proto->defineDefaultProperty(QStringLiteral("addColorStop"), QQuickContext2DStyle::gradient_proto_addColorStop, 0);
4394 gradientProto = proto;
4395
4396 proto = scope.engine->newObject();
4397 proto->defineAccessorProperty(scope.engine->id_length(), QQuickJSContext2DPixelData::proto_get_length, nullptr);
4398 pixelArrayProto = proto;
4399}
4400
4404
4406{
4407 if (m_stateStack.isEmpty())
4408 return;
4409
4410 QQuickContext2D::State newState = m_stateStack.pop();
4411
4412 if (state.matrix != newState.matrix)
4413 buffer()->updateMatrix(newState.matrix);
4414
4415 if (newState.globalAlpha != state.globalAlpha)
4416 buffer()->setGlobalAlpha(newState.globalAlpha);
4417
4418 if (newState.globalCompositeOperation != state.globalCompositeOperation)
4419 buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
4420
4421 if (newState.fillStyle != state.fillStyle)
4422 buffer()->setFillStyle(newState.fillStyle);
4423
4424 if (newState.strokeStyle != state.strokeStyle)
4425 buffer()->setStrokeStyle(newState.strokeStyle);
4426
4427 if (newState.lineWidth != state.lineWidth)
4428 buffer()->setLineWidth(newState.lineWidth);
4429
4430 if (newState.lineCap != state.lineCap)
4431 buffer()->setLineCap(newState.lineCap);
4432
4433 if (newState.lineJoin != state.lineJoin)
4434 buffer()->setLineJoin(newState.lineJoin);
4435
4436 if (newState.miterLimit != state.miterLimit)
4437 buffer()->setMiterLimit(newState.miterLimit);
4438
4439 if (newState.clip != state.clip || newState.clipPath != state.clipPath)
4440 buffer()->clip(newState.clip, newState.clipPath);
4441
4442 if (newState.shadowBlur != state.shadowBlur)
4443 buffer()->setShadowBlur(newState.shadowBlur);
4444
4445 if (newState.shadowColor != state.shadowColor)
4446 buffer()->setShadowColor(newState.shadowColor);
4447
4448 if (newState.shadowOffsetX != state.shadowOffsetX)
4449 buffer()->setShadowOffsetX(newState.shadowOffsetX);
4450
4451 if (newState.shadowOffsetY != state.shadowOffsetY)
4452 buffer()->setShadowOffsetY(newState.shadowOffsetY);
4453
4454 if (newState.lineDash != state.lineDash)
4455 buffer()->setLineDash(newState.lineDash);
4456
4457 m_path = state.matrix.map(m_path);
4458 state = newState;
4459 m_path = state.matrix.inverted().map(m_path);
4460}
4462{
4463 m_stateStack.push(state);
4464}
4465
4467{
4468 QQuickContext2D::State newState;
4469
4470 m_path = QPainterPath();
4471
4472 newState.clipPath.setFillRule(Qt::WindingFill);
4473
4474 m_stateStack.clear();
4475 m_stateStack.push(newState);
4476 popState();
4477 m_buffer->clearRect(QRectF(0, 0, m_canvas->width(), m_canvas->height()));
4478}
4479
4480QV4::ExecutionEngine *QQuickContext2D::v4Engine() const
4481{
4482 return m_v4engine;
4483}
4484
4485void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
4486{
4487 if (m_v4engine != engine) {
4488 m_v4engine = engine;
4489
4490 if (m_v4engine == nullptr)
4491 return;
4492
4493 QQuickContext2DEngineData *ed = engineData(engine);
4494 QV4::Scope scope(engine);
4495 QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocate<QQuickJSContext2D>());
4496 QV4::ScopedObject p(scope, ed->contextPrototype.value());
4497 wrapper->setPrototypeOf(p);
4498 wrapper->d()->setContext(this);
4499 m_v4value = wrapper;
4500 }
4501}
4502
4503QT_END_NAMESPACE
4504
4505#include "moc_qquickcontext2d_p.cpp"
QV4::PersistentValue gradientProto
QQuickContext2DEngineData(QV4::ExecutionEngine *engine)
QV4::PersistentValue contextPrototype
QV4::PersistentValue pixelArrayProto
static QQuickContext2DRenderThread * instance(QQmlEngine *engine)
QPainterPath createTextGlyphs(qreal x, qreal y, const QString &text)
bool isPointInPath(qreal x, qreal y) const
void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
void translate(qreal x, qreal y)
QQuickContext2DTexture * texture() const
QV4::ExecutionEngine * v4Engine() const override
void strokeRect(qreal x, qreal y, qreal w, qreal h)
void text(const QString &str, qreal x, qreal y)
void ellipse(qreal x, qreal y, qreal w, qreal h)
QV4::ExecutionEngine * m_v4engine
void flush() override
QV4::ReturnedValue v4value() const override
void setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
QImage toImage(const QRectF &bounds) override
void arc(qreal x, qreal y, qreal radius, qreal startAngle, qreal endAngle, bool anticlockwise)
QStringList contextNames() const override
void setGrabbedImage(const QImage &grab)
void lineTo(qreal x, qreal y)
QQuickContext2D(QObject *parent=nullptr)
QQuickContext2DCommandBuffer * buffer() const
void scale(qreal x, qreal y)
void bezierCurveTo(qreal cp1x, qreal cp1y, qreal cp2x, qreal cp2y, qreal x, qreal y)
void prepare(const QSize &canvasSize, const QSize &tileSize, const QRect &canvasWindow, const QRect &dirtyRect, bool smooth, bool antialiasing) override
QQuickContext2DTexture * m_texture
void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
void rotate(qreal angle)
void drawText(const QString &text, qreal x, qreal y, bool fill)
void roundedRect(qreal x, qreal y, qreal w, qreal h, qreal xr, qreal yr)
static QMutex mutex
void setV4Engine(QV4::ExecutionEngine *eng) override
void shear(qreal h, qreal v)
QQuickContext2DCommandBuffer * m_buffer
void clearRect(qreal x, qreal y, qreal w, qreal h)
void addArcTo(const QPointF &p1, const QPointF &p2, qreal radius)
void transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f)
void rect(qreal x, qreal y, qreal w, qreal h)
void moveTo(qreal x, qreal y)
void fillRect(qreal x, qreal y, qreal w, qreal h)
Definition qjsvalue.h:23
#define M_PI
Definition qmath.h:200
static bool withinRange(qreal p, qreal a, qreal b)
#define qClamp(val, min, max)
static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
DEFINE_OBJECT_VTABLE(QQuickJSContext2DPrototype)
static QFont qt_font_from_string(const QString &fontString, const QFont &currentFont)
DEFINE_OBJECT_VTABLE(QQuickContext2DStyle)
QImage qt_image_convolute_filter(const QImage &src, const QVector< qreal > &weights, int radius=0)
@ NoTokens
@ FontStyle
@ FontWeight
@ FontVariant
static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
#define CHECK_CONTEXT(r)
\qmltype Context2D \nativetype QQuickContext2D \inqmlmodule QtQuick
#define Q_TRY_SET_TOKEN(token, value, setStatement)
int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
void qt_image_boxblur(QImage &image, int radius, bool quality)
static bool qSetFontFamilyFromTokens(QFont &font, const QStringList &fontFamilyTokens)
DEFINE_OBJECT_VTABLE(QQuickJSContext2DImageData)
static bool areCollinear(const QPointF &a, const QPointF &b, const QPointF &c)
#define CHECK_CONTEXT_SETTER(r)
static bool qSetFontSizeFromToken(QFont &font, QStringView fontSizeToken)
static int qParseFontSizeFromToken(QStringView fontSizeToken, bool &ok)
static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionEngine *v4, QImage &&image)
DEFINE_OBJECT_VTABLE(QQuickJSContext2DPixelData)
static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
static QStringList qExtractFontFamiliesFromString(QStringView fontFamiliesString)
DEFINE_OBJECT_VTABLE(QQuickJSContext2D)
QDebug Q_QUICK_EXPORT operator<<(QDebug debug, const QQuickWindow *item)
static QV4::ReturnedValue method_get_data(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty object QtQuick::CanvasImageData::data Holds the one-dimensional array containing the dat...
static QV4::ReturnedValue method_get_height(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty int QtQuick::CanvasImageData::height Holds the actual height dimension of the data in th...
static bool virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, Value *receiver)
static QV4::ReturnedValue proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmltype CanvasPixelArray \inqmlmodule QtQuick
static QV4::ReturnedValue method_isPointInPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::isPointInPath(real x, real y)
static QV4::ReturnedValue method_measureText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::measureText(text)
static QV4::ReturnedValue method_moveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::moveTo(real x, real y)
static QV4::ReturnedValue method_bezierCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x,...
static QV4::ReturnedValue method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod CanvasImageData QtQuick::Context2D::getImageData(real x, real y, real w,...
static QV4::ReturnedValue method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod QtQuick::Context2D::setLineDash(array pattern)
static QV4::ReturnedValue method_quadraticCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::quadraticCurveTo(real cpx, real cpy, real x,...
static QV4::ReturnedValue method_drawImage(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod QtQuick::Context2D::drawImage(variant image, real dx, real dy) Draws the given image on th...
static QV4::ReturnedValue method_scale(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::scale(real x, real y)
static QV4::ReturnedValue method_stroke(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::stroke()
static QV4::ReturnedValue method_closePath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::closePath() Closes the current subpath by drawing a line to the...
static QV4::ReturnedValue method_strokeRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::strokeRect(real x, real y, real w, real h)
static QV4::ReturnedValue method_transform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::transform(real a, real b, real c, real d, real e,...
static QV4::ReturnedValue method_translate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::translate(real x, real y)
static QV4::ReturnedValue method_ellipse(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::ellipse(real x, real y, real w, real h)
static QV4::ReturnedValue method_resetTransform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::resetTransform()
static QV4::ReturnedValue method_createRadialGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::createRadialGradient(real x0, real y0, real r0,...
static QV4::ReturnedValue method_clip(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::clip()
static QV4::ReturnedValue method_createLinearGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::createLinearGradient(real x0, real y0, real x1,...
static QV4::ReturnedValue method_reset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::reset() Resets the context state and properties to the default ...
static QV4::ReturnedValue method_fillText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::fillText(text, x, y)
static QV4::ReturnedValue method_arc(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::arc(real x, real y, real radius, real startAngle,...
static QV4::ReturnedValue method_save(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::save() Pushes the current state onto the state stack.
static QV4::ReturnedValue method_drawFocusRing(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_shear(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::shear(real sh, real sv)
static QV4::ReturnedValue method_lineTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::lineTo(real x, real y)
static QV4::ReturnedValue method_setTransform(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::setTransform(real a, real b, real c, real d,...
static QV4::ReturnedValue method_createConicalGradient(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::createConicalGradient(real x, real y, real angle)
static QV4::ReturnedValue method_strokeText(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::strokeText(text, x, y)
static QV4::ReturnedValue method_restore(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::restore() Pops the top state on the stack, restoring the contex...
static QV4::ReturnedValue method_rect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::rect(real x, real y, real w, real h)
static QV4::ReturnedValue method_beginPath(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::beginPath()
static QV4::ReturnedValue method_rotate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::rotate(real angle) Rotate the canvas around the current origin ...
static QV4::ReturnedValue method_fillRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::fillRect(real x, real y, real w, real h)
static QV4::ReturnedValue method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod array QtQuick::Context2D::getLineDash()
static QV4::ReturnedValue method_arcTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::arcTo(real x1, real y1, real x2, real y2,...
static QV4::ReturnedValue method_fill(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::fill()
static QV4::ReturnedValue method_setCaretSelectionRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod CanvasImageData QtQuick::Context2D::createImageData(real sw, real sh)
static QV4::ReturnedValue method_createPattern(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod variant QtQuick::Context2D::createPattern(color color, enumeration patternMode) This is an...
static QV4::ReturnedValue method_roundedRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::roundedRect(real x, real y, real w, real h, real xRadius,...
static QV4::ReturnedValue method_caretBlinkRate(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::putImageData(CanvasImageData imageData, real dx,...
static QV4::ReturnedValue method_text(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::text(string text, real x, real y)
static QV4::ReturnedValue method_clearRect(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlmethod object QtQuick::Context2D::clearRect(real x, real y, real w, real h)
static QV4::ReturnedValue method_set_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty string QtQuick::Context2D::lineJoin Holds the current line join style.
static QV4::ReturnedValue method_get_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty string QtQuick::Context2D::globalCompositeOperation Holds the current the current compos...
static QV4::ReturnedValue method_set_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_fillRule(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty enumeration QtQuick::Context2D::fillRule Holds the current fill rule used for filling sh...
static QV4::ReturnedValue method_set_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_lineCap(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty string QtQuick::Context2D::lineCap Holds the current line cap style.
static QV4::ReturnedValue method_set_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty real QtQuick::Context2D::lineDashOffset
static QV4::ReturnedValue method_get_textAlign(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty string QtQuick::Context2D::textAlign
static QV4::ReturnedValue method_set_globalAlpha(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty real QtQuick::Context2D::lineWidth Holds the current line width.
static QV4::ReturnedValue method_set_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_font(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty string QtQuick::Context2D::font Holds the current font settings.
static QV4::ReturnedValue method_get_fillStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty variant QtQuick::Context2D::fillStyle Holds the current style used for filling shapes.
static QV4::ReturnedValue method_get_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty real QtQuick::Context2D::shadowBlur Holds the current level of blur applied to shadows
static QV4::ReturnedValue method_get_shadowOffsetY(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty real QtQuick::Context2D::shadowOffsetY Holds the current shadow offset in the positive v...
static QV4::ReturnedValue method_get_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty real QtQuick::Context2D::miterLimit Holds the current miter limit ratio.
static QV4::ReturnedValue method_set_globalCompositeOperation(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty string QtQuick::Context2D::shadowColor Holds the current shadow color.
static QV4::ReturnedValue method_set_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_shadowColor(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_set_lineJoin(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_strokeStyle(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty variant QtQuick::Context2D::strokeStyle Holds the current color or style to use for the ...
static QV4::ReturnedValue method_set_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
static QV4::ReturnedValue method_get_textBaseline(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty string QtQuick::Context2D::textBaseline
static QV4::ReturnedValue method_get_shadowOffsetX(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
\qmlproperty real QtQuick::Context2D::shadowOffsetX Holds the current shadow offset in the positive h...
static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
void setContext(QQuickContext2D *context)