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

source code of qtdeclarative/src/quick/items/context2d/qquickcontext2d.cpp