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