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