| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtQuick module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include <private/qsgadaptationlayer_p.h> | 
| 41 | #include "qquickcanvasitem_p.h" | 
| 42 | #include <private/qquickitem_p.h> | 
| 43 | #include <private/qquickcanvascontext_p.h> | 
| 44 | #include <private/qquickcontext2d_p.h> | 
| 45 | #include <private/qquickcontext2dtexture_p.h> | 
| 46 | #include <private/qsgadaptationlayer_p.h> | 
| 47 | #include <qsgtextureprovider.h> | 
| 48 | #include <QtQuick/private/qquickpixmapcache_p.h> | 
| 49 | #include <QtGui/QGuiApplication> | 
| 50 | #include <qsgtextureprovider.h> | 
| 51 |  | 
| 52 | #include <qqmlinfo.h> | 
| 53 | #include <private/qqmlengine_p.h> | 
| 54 | #include <QtCore/QBuffer> | 
| 55 | #include <QtCore/qdatetime.h> | 
| 56 |  | 
| 57 | #include <private/qv4value_p.h> | 
| 58 | #include <private/qv4functionobject_p.h> | 
| 59 | #include <private/qv4scopedvalue_p.h> | 
| 60 | #include <private/qv4jscall_p.h> | 
| 61 | #include <private/qv4qobjectwrapper_p.h> | 
| 62 |  | 
| 63 | QT_BEGIN_NAMESPACE | 
| 64 |  | 
| 65 | class QQuickCanvasTextureProvider : public QSGTextureProvider | 
| 66 | { | 
| 67 | public: | 
| 68 |     QSGTexture *tex; | 
| 69 |     QSGTexture *texture() const override { return tex; } | 
| 70 |     void fireTextureChanged() { emit textureChanged(); } | 
| 71 | }; | 
| 72 |  | 
| 73 | QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image) | 
| 74 |     : m_pixmap(nullptr) | 
| 75 |     , m_image(image) | 
| 76 | { | 
| 77 |  | 
| 78 | } | 
| 79 |  | 
| 80 | QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap) | 
| 81 |     : m_pixmap(pixmap) | 
| 82 | { | 
| 83 |  | 
| 84 | } | 
| 85 |  | 
| 86 | QQuickCanvasPixmap::~QQuickCanvasPixmap() | 
| 87 | { | 
| 88 |     delete m_pixmap; | 
| 89 | } | 
| 90 |  | 
| 91 | qreal QQuickCanvasPixmap::width() const | 
| 92 | { | 
| 93 |     if (m_pixmap) | 
| 94 |         return m_pixmap->width(); | 
| 95 |  | 
| 96 |     return m_image.width(); | 
| 97 | } | 
| 98 |  | 
| 99 | qreal QQuickCanvasPixmap::height() const | 
| 100 | { | 
| 101 |     if (m_pixmap) | 
| 102 |         return m_pixmap->height(); | 
| 103 |  | 
| 104 |     return m_image.height(); | 
| 105 | } | 
| 106 |  | 
| 107 | bool QQuickCanvasPixmap::isValid() const | 
| 108 | { | 
| 109 |     if (m_pixmap) | 
| 110 |         return m_pixmap->isReady(); | 
| 111 |     return !m_image.isNull(); | 
| 112 | } | 
| 113 |  | 
| 114 | QImage QQuickCanvasPixmap::image() | 
| 115 | { | 
| 116 |     if (m_image.isNull() && m_pixmap) | 
| 117 |         m_image = m_pixmap->image(); | 
| 118 |  | 
| 119 |     return m_image; | 
| 120 | } | 
| 121 |  | 
| 122 | QHash<QQmlEngine *,QQuickContext2DRenderThread*> QQuickContext2DRenderThread::renderThreads; | 
| 123 | QMutex QQuickContext2DRenderThread::renderThreadsMutex; | 
| 124 |  | 
| 125 | QQuickContext2DRenderThread::QQuickContext2DRenderThread(QQmlEngine *eng) | 
| 126 |     : QThread(eng), m_engine(eng), m_eventLoopQuitHack(nullptr) | 
| 127 | { | 
| 128 |     Q_ASSERT(eng); | 
| 129 |     m_eventLoopQuitHack = new QObject; | 
| 130 |     m_eventLoopQuitHack->moveToThread(thread: this); | 
| 131 |     connect(asender: m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), atype: Qt::DirectConnection); | 
| 132 |     start(QThread::IdlePriority); | 
| 133 | } | 
| 134 |  | 
| 135 | QQuickContext2DRenderThread::~QQuickContext2DRenderThread() | 
| 136 | { | 
| 137 |     renderThreadsMutex.lock(); | 
| 138 |     renderThreads.remove(key: m_engine); | 
| 139 |     renderThreadsMutex.unlock(); | 
| 140 |  | 
| 141 |     m_eventLoopQuitHack->deleteLater(); | 
| 142 |     wait(); | 
| 143 | } | 
| 144 |  | 
| 145 | QQuickContext2DRenderThread *QQuickContext2DRenderThread::instance(QQmlEngine *engine) | 
| 146 | { | 
| 147 |     QQuickContext2DRenderThread *thread = nullptr; | 
| 148 |     renderThreadsMutex.lock(); | 
| 149 |     if (renderThreads.contains(key: engine)) | 
| 150 |         thread = renderThreads.value(key: engine); | 
| 151 |     else { | 
| 152 |         thread = new QQuickContext2DRenderThread(engine); | 
| 153 |         renderThreads.insert(key: engine, value: thread); | 
| 154 |     } | 
| 155 |     renderThreadsMutex.unlock(); | 
| 156 |     return thread; | 
| 157 | } | 
| 158 |  | 
| 159 | class QQuickCanvasItemPrivate : public QQuickItemPrivate | 
| 160 | { | 
| 161 | public: | 
| 162 |     QQuickCanvasItemPrivate(); | 
| 163 |     ~QQuickCanvasItemPrivate(); | 
| 164 |     QQuickCanvasContext *context; | 
| 165 |     QSizeF canvasSize; | 
| 166 |     QSize tileSize; | 
| 167 |     QRectF canvasWindow; | 
| 168 |     QRectF dirtyRect; | 
| 169 |     uint hasCanvasSize :1; | 
| 170 |     uint hasTileSize :1; | 
| 171 |     uint hasCanvasWindow :1; | 
| 172 |     uint available :1; | 
| 173 |     QQuickCanvasItem::RenderTarget renderTarget; | 
| 174 |     QQuickCanvasItem::RenderStrategy renderStrategy; | 
| 175 |     QString contextType; | 
| 176 |     QHash<QUrl, QQmlRefPointer<QQuickCanvasPixmap> > pixmaps; | 
| 177 |     QUrl baseUrl; | 
| 178 |     QMap<int, QV4::PersistentValue> animationCallbacks; | 
| 179 |     mutable QQuickCanvasTextureProvider *textureProvider; | 
| 180 |     QSGInternalImageNode *node; | 
| 181 |     QSGTexture *nodeTexture; | 
| 182 | }; | 
| 183 |  | 
| 184 | QQuickCanvasItemPrivate::QQuickCanvasItemPrivate() | 
| 185 |     : QQuickItemPrivate() | 
| 186 |     , context(nullptr) | 
| 187 |     , canvasSize(1, 1) | 
| 188 |     , tileSize(1, 1) | 
| 189 |     , hasCanvasSize(false) | 
| 190 |     , hasTileSize(false) | 
| 191 |     , hasCanvasWindow(false) | 
| 192 |     , available(false) | 
| 193 |     , renderTarget(QQuickCanvasItem::Image) | 
| 194 |     , renderStrategy(QQuickCanvasItem::Immediate) | 
| 195 |     , textureProvider(nullptr) | 
| 196 |     , node(nullptr) | 
| 197 |     , nodeTexture(nullptr) | 
| 198 | { | 
| 199 |     implicitAntialiasing = true; | 
| 200 | } | 
| 201 |  | 
| 202 | QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate() | 
| 203 | { | 
| 204 |     pixmaps.clear(); | 
| 205 | } | 
| 206 |  | 
| 207 |  | 
| 208 | /*! | 
| 209 |     \qmltype Canvas | 
| 210 |     \instantiates QQuickCanvasItem | 
| 211 |     \inqmlmodule QtQuick | 
| 212 |     \since 5.0 | 
| 213 |     \inherits Item | 
| 214 |     \ingroup qtquick-canvas | 
| 215 |     \ingroup qtquick-visual | 
| 216 |     \brief Provides a 2D canvas item enabling drawing via JavaScript. | 
| 217 |  | 
| 218 |     The Canvas item allows drawing of straight and curved lines, simple and | 
| 219 |     complex shapes, graphs, and referenced graphic images.  It can also add | 
| 220 |     text, colors, shadows, gradients, and patterns, and do low level pixel | 
| 221 |     operations. The Canvas output may be saved as an image file or serialized | 
| 222 |     to a URL. | 
| 223 |  | 
| 224 |     Rendering to the Canvas is done using a Context2D object, usually as a | 
| 225 |     result of the \l paint signal. | 
| 226 |  | 
| 227 |     To define a drawing area in the Canvas item set the \c width and \c height | 
| 228 |     properties.  For example, the following code creates a Canvas item which | 
| 229 |     has a drawing area with a height of 100 pixels and width of 200 pixels: | 
| 230 |     \qml | 
| 231 |     import QtQuick 2.0 | 
| 232 |     Canvas { | 
| 233 |         id: mycanvas | 
| 234 |         width: 100 | 
| 235 |         height: 200 | 
| 236 |         onPaint: { | 
| 237 |             var ctx = getContext("2d"); | 
| 238 |             ctx.fillStyle = Qt.rgba(1, 0, 0, 1); | 
| 239 |             ctx.fillRect(0, 0, width, height); | 
| 240 |         } | 
| 241 |     } | 
| 242 |     \endqml | 
| 243 |  | 
| 244 |     Currently the Canvas item only supports the two-dimensional rendering context. | 
| 245 |  | 
| 246 |     \section1 Threaded Rendering and Render Target | 
| 247 |  | 
| 248 |     The Canvas item supports two render targets: \c Canvas.Image and | 
| 249 |     \c Canvas.FramebufferObject. | 
| 250 |  | 
| 251 |     The \c Canvas.Image render target is a \a QImage object. This render target | 
| 252 |     supports background thread rendering, allowing complex or long running | 
| 253 |     painting to be executed without blocking the UI. This is the only render | 
| 254 |     target that is supported by all Qt Quick backends. | 
| 255 |  | 
| 256 |     The Canvas.FramebufferObject render target utilizes OpenGL hardware | 
| 257 |     acceleration rather than rendering into system memory, which in many cases | 
| 258 |     results in faster rendering. Canvas.FramebufferObject relies on the OpenGL | 
| 259 |     extensions \c GL_EXT_framebuffer_multisample and \c GL_EXT_framebuffer_blit | 
| 260 |     for antialiasing. It will also use more graphics memory when rendering | 
| 261 |     strategy is anything other than Canvas.Cooperative. Framebuffer objects may | 
| 262 |     not be available with Qt Quick backends other than OpenGL. | 
| 263 |  | 
| 264 |     The default render target is Canvas.Image and the default renderStrategy is | 
| 265 |     Canvas.Immediate. | 
| 266 |  | 
| 267 |     \section1 Pixel Operations | 
| 268 |     All HTML5 2D context pixel operations are supported. In order to ensure | 
| 269 |     improved pixel reading/writing performance the \a Canvas.Image render | 
| 270 |     target should be chosen. The \a Canvas.FramebufferObject render target | 
| 271 |     requires the pixel data to be exchanged between the system memory and the | 
| 272 |     graphic card, which is significantly more expensive.  Rendering may also be | 
| 273 |     synchronized with the V-sync signal (to avoid | 
| 274 |     \l{http://en.wikipedia.org/wiki/Screen_tearing}{screen tearing}) which will further | 
| 275 |     impact pixel operations with \c Canvas.FrambufferObject render target. | 
| 276 |  | 
| 277 |     \section1 Tips for Porting Existing HTML5 Canvas Applications | 
| 278 |  | 
| 279 |     Although the Canvas item provides an HTML5-like API, HTML5 canvas | 
| 280 |     applications need to be modified to run in the Canvas item: | 
| 281 |     \list | 
| 282 |     \li Replace all DOM API calls with QML property bindings or Canvas item methods. | 
| 283 |     \li Replace all HTML event handlers with the MouseArea item. | 
| 284 |     \li Change setInterval/setTimeout function calls with the \l Timer item or | 
| 285 |        the use of requestAnimationFrame(). | 
| 286 |     \li Place painting code into the \c onPaint handler and trigger | 
| 287 |        painting by calling the markDirty() or requestPaint() methods. | 
| 288 |     \li To draw images, load them by calling the Canvas's loadImage() method and then request to paint | 
| 289 |        them in the \c onImageLoaded handler. | 
| 290 |     \endlist | 
| 291 |  | 
| 292 |     Starting Qt 5.4, the Canvas is a | 
| 293 |     \l{QSGTextureProvider}{texture provider} | 
| 294 |     and can be used directly in \l {ShaderEffect}{ShaderEffects} and other | 
| 295 |     classes that consume texture providers. | 
| 296 |  | 
| 297 |     \note In general large canvases, frequent updates, and animation should be | 
| 298 |     avoided with the Canvas.Image render target. This is because with | 
| 299 |     accelerated graphics APIs each update will lead to a texture upload. Also, | 
| 300 |     if possible, prefer QQuickPaintedItem and implement drawing in C++ via | 
| 301 |     QPainter instead of the more expensive and likely less performing | 
| 302 |     JavaScript and Context2D approach. | 
| 303 |  | 
| 304 |     \sa Context2D QQuickPaintedItem | 
| 305 | */ | 
| 306 |  | 
| 307 | QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent) | 
| 308 |     : QQuickItem(*(new QQuickCanvasItemPrivate), parent) | 
| 309 | { | 
| 310 |     setFlag(flag: ItemHasContents); | 
| 311 | } | 
| 312 |  | 
| 313 | QQuickCanvasItem::~QQuickCanvasItem() | 
| 314 | { | 
| 315 |     Q_D(QQuickCanvasItem); | 
| 316 |     delete d->context; | 
| 317 |     if (d->textureProvider) | 
| 318 |         QQuickWindowQObjectCleanupJob::schedule(window: window(), object: d->textureProvider); | 
| 319 | } | 
| 320 |  | 
| 321 | /*! | 
| 322 |     \qmlproperty bool QtQuick::Canvas::available | 
| 323 |  | 
| 324 |     Indicates when Canvas is able to provide a drawing context to operate on. | 
| 325 | */ | 
| 326 |  | 
| 327 | bool QQuickCanvasItem::isAvailable() const | 
| 328 | { | 
| 329 |     return d_func()->available; | 
| 330 | } | 
| 331 |  | 
| 332 | /*! | 
| 333 |     \qmlproperty string QtQuick::Canvas::contextType | 
| 334 |     The type of drawing context to use. | 
| 335 |  | 
| 336 |     This property is set to the name of the active context type. | 
| 337 |  | 
| 338 |     If set explicitly the canvas will attempt to create a context of the | 
| 339 |     named type after becoming available. | 
| 340 |  | 
| 341 |     The type name is the same as used in the getContext() call, for the 2d | 
| 342 |     canvas the value will be "2d". | 
| 343 |  | 
| 344 |     \sa getContext(), available | 
| 345 | */ | 
| 346 |  | 
| 347 | QString QQuickCanvasItem::contextType() const | 
| 348 | { | 
| 349 |     return d_func()->contextType; | 
| 350 | } | 
| 351 |  | 
| 352 | void QQuickCanvasItem::setContextType(const QString &contextType) | 
| 353 | { | 
| 354 |     Q_D(QQuickCanvasItem); | 
| 355 |  | 
| 356 |     if (contextType.compare(s: d->contextType, cs: Qt::CaseInsensitive) == 0) | 
| 357 |         return; | 
| 358 |  | 
| 359 |     if (d->context) { | 
| 360 |         qmlWarning(me: this) << "Canvas already initialized with a different context type" ; | 
| 361 |         return; | 
| 362 |     } | 
| 363 |  | 
| 364 |     d->contextType = contextType; | 
| 365 |  | 
| 366 |     if (d->available) | 
| 367 |         createContext(contextType); | 
| 368 |  | 
| 369 |     emit contextTypeChanged(); | 
| 370 | } | 
| 371 |  | 
| 372 | /*! | 
| 373 |     \qmlproperty object QtQuick::Canvas::context | 
| 374 |     Holds the active drawing context. | 
| 375 |  | 
| 376 |     If the canvas is ready and there has been a successful call to getContext() | 
| 377 |     or the contextType property has been set with a supported context type, | 
| 378 |     this property will contain the current drawing context, otherwise null. | 
| 379 | */ | 
| 380 |  | 
| 381 | QJSValue QQuickCanvasItem::context() const | 
| 382 | { | 
| 383 |     Q_D(const QQuickCanvasItem); | 
| 384 |     return d->context ? QJSValue(d->context->v4Engine(), d->context->v4value()) : QJSValue(); | 
| 385 | } | 
| 386 |  | 
| 387 | /*! | 
| 388 |     \qmlproperty size QtQuick::Canvas::canvasSize | 
| 389 |     Holds the logical canvas size that the context paints on. | 
| 390 |  | 
| 391 |     By default, the canvas size is the same size as the current canvas item | 
| 392 |     size. | 
| 393 |  | 
| 394 |     By setting the canvasSize, tileSize and canvasWindow, the Canvas item can | 
| 395 |     act as a large virtual canvas with many separately rendered tile rectangles. | 
| 396 |     Only those tiles within the current canvas window are painted by the Canvas | 
| 397 |     render engine. | 
| 398 |  | 
| 399 |     \sa tileSize, canvasWindow | 
| 400 | */ | 
| 401 | QSizeF QQuickCanvasItem::canvasSize() const | 
| 402 | { | 
| 403 |     Q_D(const QQuickCanvasItem); | 
| 404 |     return d->canvasSize; | 
| 405 | } | 
| 406 |  | 
| 407 | void QQuickCanvasItem::setCanvasSize(const QSizeF & size) | 
| 408 | { | 
| 409 |     Q_D(QQuickCanvasItem); | 
| 410 |     if (d->canvasSize != size) { | 
| 411 |         d->hasCanvasSize = true; | 
| 412 |         d->canvasSize = size; | 
| 413 |         emit canvasSizeChanged(); | 
| 414 |  | 
| 415 |         if (d->context) | 
| 416 |             polish(); | 
| 417 |     } | 
| 418 | } | 
| 419 |  | 
| 420 | /*! | 
| 421 |     \qmlproperty size QtQuick::Canvas::tileSize | 
| 422 |     Holds the canvas rendering tile size. | 
| 423 |  | 
| 424 |     The Canvas item enters tiled mode by setting canvasSize, tileSize and the | 
| 425 |     canvasWindow. This can improve rendering performance by rendering and | 
| 426 |     caching tiles instead of rendering the whole canvas every time. | 
| 427 |  | 
| 428 |     Memory will be consumed only by those tiles within the current visible | 
| 429 |     region. | 
| 430 |  | 
| 431 |     By default the tileSize is the same as the canvasSize. | 
| 432 |  | 
| 433 |     \obsolete This feature is incomplete. For details, see QTBUG-33129. | 
| 434 |  | 
| 435 |     \sa canvasSize, canvasWindow | 
| 436 | */ | 
| 437 | QSize QQuickCanvasItem::tileSize() const | 
| 438 | { | 
| 439 |     Q_D(const QQuickCanvasItem); | 
| 440 |     return d->tileSize; | 
| 441 | } | 
| 442 |  | 
| 443 | void QQuickCanvasItem::setTileSize(const QSize & size) | 
| 444 | { | 
| 445 |     Q_D(QQuickCanvasItem); | 
| 446 |     if (d->tileSize != size) { | 
| 447 |         d->hasTileSize = true; | 
| 448 |         d->tileSize = size; | 
| 449 |  | 
| 450 |         emit tileSizeChanged(); | 
| 451 |  | 
| 452 |         if (d->context) | 
| 453 |             polish(); | 
| 454 |     } | 
| 455 | } | 
| 456 |  | 
| 457 | /*! | 
| 458 |     \qmlproperty rect QtQuick::Canvas::canvasWindow | 
| 459 |      Holds the current canvas visible window. | 
| 460 |  | 
| 461 |      By default the canvasWindow size is the same as the Canvas item size with | 
| 462 |      the top-left point as (0, 0). | 
| 463 |  | 
| 464 |      If the canvasSize is different to the Canvas item size, the Canvas item | 
| 465 |      can display different visible areas by changing the canvas windowSize | 
| 466 |      and/or position. | 
| 467 |  | 
| 468 |     \obsolete This feature is incomplete. For details, see QTBUG-33129 | 
| 469 |  | 
| 470 |     \sa canvasSize, tileSize | 
| 471 | */ | 
| 472 | QRectF QQuickCanvasItem::canvasWindow() const | 
| 473 | { | 
| 474 |     Q_D(const QQuickCanvasItem); | 
| 475 |     return d->canvasWindow; | 
| 476 | } | 
| 477 |  | 
| 478 | void QQuickCanvasItem::setCanvasWindow(const QRectF& rect) | 
| 479 | { | 
| 480 |     Q_D(QQuickCanvasItem); | 
| 481 |     if (d->canvasWindow != rect) { | 
| 482 |         d->canvasWindow = rect; | 
| 483 |  | 
| 484 |         d->hasCanvasWindow = true; | 
| 485 |         emit canvasWindowChanged(); | 
| 486 |  | 
| 487 |         if (d->context) | 
| 488 |             polish(); | 
| 489 |     } | 
| 490 | } | 
| 491 |  | 
| 492 | /*! | 
| 493 |     \qmlproperty enumeration QtQuick::Canvas::renderTarget | 
| 494 |     Holds the current canvas render target. | 
| 495 |  | 
| 496 |     \list | 
| 497 |     \li Canvas.Image  - render to an in memory image buffer. | 
| 498 |     \li Canvas.FramebufferObject - render to an OpenGL frame buffer | 
| 499 |     \endlist | 
| 500 |  | 
| 501 |     This hint is supplied along with renderStrategy to the graphics context to | 
| 502 |     determine the method of rendering. A renderStrategy, renderTarget or a | 
| 503 |     combination may not be supported by a graphics context, in which case the | 
| 504 |     context will choose appropriate options and Canvas will signal the change | 
| 505 |     to the properties. | 
| 506 |  | 
| 507 |     The default render target is \c Canvas.Image. | 
| 508 | */ | 
| 509 | QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const | 
| 510 | { | 
| 511 |     Q_D(const QQuickCanvasItem); | 
| 512 |     return d->renderTarget; | 
| 513 | } | 
| 514 |  | 
| 515 | void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target) | 
| 516 | { | 
| 517 |     Q_D(QQuickCanvasItem); | 
| 518 |     if (d->renderTarget != target) { | 
| 519 |         if (d->context) { | 
| 520 |             qmlWarning(me: this) << "Canvas:renderTarget not changeble once context is active." ; | 
| 521 |             return; | 
| 522 |         } | 
| 523 |  | 
| 524 |         d->renderTarget = target; | 
| 525 |         emit renderTargetChanged(); | 
| 526 |     } | 
| 527 | } | 
| 528 |  | 
| 529 | /*! | 
| 530 |     \qmlproperty enumeration QtQuick::Canvas::renderStrategy | 
| 531 |     Holds the current canvas rendering strategy. | 
| 532 |  | 
| 533 |     \list | 
| 534 |     \li Canvas.Immediate - context will perform graphics commands immediately in the main UI thread. | 
| 535 |     \li Canvas.Threaded - context will defer graphics commands to a private rendering thread. | 
| 536 |     \li Canvas.Cooperative - context will defer graphics commands to the applications global render thread. | 
| 537 |     \endlist | 
| 538 |  | 
| 539 |     This hint is supplied along with renderTarget to the graphics context to | 
| 540 |     determine the method of rendering. A renderStrategy, renderTarget or a | 
| 541 |     combination may not be supported by a graphics context, in which case the | 
| 542 |     context will choose appropriate options and Canvas will signal the change | 
| 543 |     to the properties. | 
| 544 |  | 
| 545 |     Configuration or runtime tests may cause the QML Scene Graph to render in | 
| 546 |     the GUI thread.  Selecting \c Canvas.Cooperative, does not guarantee | 
| 547 |     rendering will occur on a thread separate from the GUI thread. | 
| 548 |  | 
| 549 |     The default value is \c Canvas.Immediate. | 
| 550 |  | 
| 551 |     \sa renderTarget | 
| 552 | */ | 
| 553 |  | 
| 554 | QQuickCanvasItem::RenderStrategy QQuickCanvasItem::renderStrategy() const | 
| 555 | { | 
| 556 |     return d_func()->renderStrategy; | 
| 557 | } | 
| 558 |  | 
| 559 | void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strategy) | 
| 560 | { | 
| 561 |     Q_D(QQuickCanvasItem); | 
| 562 |     if (d->renderStrategy != strategy) { | 
| 563 |         if (d->context) { | 
| 564 |             qmlWarning(me: this) << "Canvas:renderStrategy not changeable once context is active." ; | 
| 565 |             return; | 
| 566 |         } | 
| 567 |         d->renderStrategy = strategy; | 
| 568 |         emit renderStrategyChanged(); | 
| 569 |     } | 
| 570 | } | 
| 571 |  | 
| 572 | QQuickCanvasContext* QQuickCanvasItem::rawContext() const | 
| 573 | { | 
| 574 |     return d_func()->context; | 
| 575 | } | 
| 576 |  | 
| 577 | bool QQuickCanvasItem::isPaintConnected() | 
| 578 | { | 
| 579 |     IS_SIGNAL_CONNECTED(this, QQuickCanvasItem, paint, (const QRect &)); | 
| 580 | } | 
| 581 |  | 
| 582 | void QQuickCanvasItem::sceneGraphInitialized() | 
| 583 | { | 
| 584 |     Q_D(QQuickCanvasItem); | 
| 585 |  | 
| 586 |     d->available = true; | 
| 587 |     connect(asender: this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks())); | 
| 588 |     QMetaObject::invokeMethod(obj: this, member: "availableChanged" , type: Qt::QueuedConnection); | 
| 589 |  | 
| 590 |     if (!d->contextType.isNull()) | 
| 591 |         QMetaObject::invokeMethod(obj: this, member: "delayedCreate" , type: Qt::QueuedConnection); | 
| 592 |     else if (isPaintConnected()) | 
| 593 |         QMetaObject::invokeMethod(obj: this, member: "requestPaint" , type: Qt::QueuedConnection); | 
| 594 | } | 
| 595 |  | 
| 596 | void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) | 
| 597 | { | 
| 598 |     Q_D(QQuickCanvasItem); | 
| 599 |  | 
| 600 |     QQuickItem::geometryChanged(newGeometry, oldGeometry); | 
| 601 |  | 
| 602 |     // Due to indirect recursion, newGeometry may be outdated | 
| 603 |     // after this call, so we use width and height instead. | 
| 604 |     QSizeF newSize = QSizeF(width(), height()); | 
| 605 |     if (!d->hasCanvasSize && d->canvasSize != newSize) { | 
| 606 |         d->canvasSize = newSize; | 
| 607 |         emit canvasSizeChanged(); | 
| 608 |     } | 
| 609 |  | 
| 610 |     if (!d->hasTileSize && d->tileSize != newSize) { | 
| 611 |         d->tileSize = newSize.toSize(); | 
| 612 |         emit tileSizeChanged(); | 
| 613 |     } | 
| 614 |  | 
| 615 |     const QRectF rect = QRectF(QPointF(0, 0), newSize); | 
| 616 |  | 
| 617 |     if (!d->hasCanvasWindow && d->canvasWindow != rect) { | 
| 618 |         d->canvasWindow = rect; | 
| 619 |         emit canvasWindowChanged(); | 
| 620 |     } | 
| 621 |  | 
| 622 |     if (d->available && newSize != oldGeometry.size()) { | 
| 623 |         if (isVisible() || (d->extra.isAllocated() && d->extra->effectRefCount > 0)) | 
| 624 |             requestPaint(); | 
| 625 |     } | 
| 626 | } | 
| 627 |  | 
| 628 | void QQuickCanvasItem::releaseResources() | 
| 629 | { | 
| 630 |     Q_D(QQuickCanvasItem); | 
| 631 |  | 
| 632 |     if (d->context) { | 
| 633 |         delete d->context; | 
| 634 |         d->context = nullptr; | 
| 635 |     } | 
| 636 |     d->node = nullptr; // managed by the scene graph, just reset the pointer | 
| 637 |     if (d->textureProvider) { | 
| 638 |         QQuickWindowQObjectCleanupJob::schedule(window: window(), object: d->textureProvider); | 
| 639 |         d->textureProvider = nullptr; | 
| 640 |     } | 
| 641 |     if (d->nodeTexture) { | 
| 642 |         QQuickWindowQObjectCleanupJob::schedule(window: window(), object: d->nodeTexture); | 
| 643 |         d->nodeTexture = nullptr; | 
| 644 |     } | 
| 645 | } | 
| 646 |  | 
| 647 | bool QQuickCanvasItem::event(QEvent *event) | 
| 648 | { | 
| 649 |     switch (event->type()) { | 
| 650 |     case QEvent::PolishRequest: | 
| 651 |         polish(); | 
| 652 |         return true; | 
| 653 |     default: | 
| 654 |         return QQuickItem::event(event); | 
| 655 |     } | 
| 656 | } | 
| 657 |  | 
| 658 | void QQuickCanvasItem::invalidateSceneGraph() | 
| 659 | { | 
| 660 |     Q_D(QQuickCanvasItem); | 
| 661 |     if (d->context) | 
| 662 |         d->context->deleteLater(); | 
| 663 |     d->context = nullptr; | 
| 664 |     d->node = nullptr; // managed by the scene graph, just reset the pointer | 
| 665 |     delete d->textureProvider; | 
| 666 |     d->textureProvider = nullptr; | 
| 667 |     delete d->nodeTexture; | 
| 668 |     d->nodeTexture = nullptr; | 
| 669 | } | 
| 670 |  | 
| 671 | void QQuickCanvasItem::schedulePolish() | 
| 672 | { | 
| 673 |     auto polishRequestEvent = new QEvent(QEvent::PolishRequest); | 
| 674 |     QCoreApplication::postEvent(receiver: this, event: polishRequestEvent); | 
| 675 | } | 
| 676 |  | 
| 677 | void QQuickCanvasItem::componentComplete() | 
| 678 | { | 
| 679 |     QQuickItem::componentComplete(); | 
| 680 |  | 
| 681 |     Q_D(QQuickCanvasItem); | 
| 682 |     d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl(); | 
| 683 | } | 
| 684 |  | 
| 685 | void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) | 
| 686 | { | 
| 687 |     QQuickItem::itemChange(change, value); | 
| 688 |     if (change != QQuickItem::ItemSceneChange) | 
| 689 |         return; | 
| 690 |  | 
| 691 |     Q_D(QQuickCanvasItem); | 
| 692 |     if (d->available) { | 
| 693 |         if (d->dirtyAttributes & QQuickItemPrivate::ContentUpdateMask) | 
| 694 |             requestPaint(); | 
| 695 |         return; | 
| 696 |     } | 
| 697 |  | 
| 698 |     if (value.window== nullptr) | 
| 699 |         return; | 
| 700 |  | 
| 701 |     d->window = value.window; | 
| 702 |     QSGRenderContext *context = QQuickWindowPrivate::get(c: d->window)->context; | 
| 703 |  | 
| 704 |     // Rendering to FramebufferObject needs a valid OpenGL context. | 
| 705 |     if (context != nullptr && (d->renderTarget != FramebufferObject || context->isValid())) { | 
| 706 |         // Defer the call. In some (arguably incorrect) cases we get here due | 
| 707 |         // to ItemSceneChange with the user-supplied property values not yet | 
| 708 |         // set. Work this around by a deferred invoke. (QTBUG-49692) | 
| 709 |         QMetaObject::invokeMethod(obj: this, member: "sceneGraphInitialized" , type: Qt::QueuedConnection); | 
| 710 |     } else { | 
| 711 |         connect(asender: d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized())); | 
| 712 |     } | 
| 713 | } | 
| 714 |  | 
| 715 | void QQuickCanvasItem::updatePolish() | 
| 716 | { | 
| 717 |     QQuickItem::updatePolish(); | 
| 718 |  | 
| 719 |     Q_D(QQuickCanvasItem); | 
| 720 |     if (d->context && d->renderStrategy != QQuickCanvasItem::Cooperative) | 
| 721 |         d->context->prepare(canvasSize: d->canvasSize.toSize(), tileSize: d->tileSize, canvasWindow: d->canvasWindow.toRect(), dirtyRect: d->dirtyRect.toRect(), smooth: d->smooth, antialiasing: antialiasing()); | 
| 722 |  | 
| 723 |     if (d->animationCallbacks.size() > 0 && isVisible()) { | 
| 724 |         QMap<int, QV4::PersistentValue> animationCallbacks = d->animationCallbacks; | 
| 725 |         d->animationCallbacks.clear(); | 
| 726 |  | 
| 727 |         QV4::ExecutionEngine *v4 = qmlEngine(this)->handle(); | 
| 728 |         QV4::Scope scope(v4); | 
| 729 |         QV4::ScopedFunctionObject function(scope); | 
| 730 |         QV4::JSCallData jsCall(scope, 1); | 
| 731 |         *jsCall->thisObject = QV4::QObjectWrapper::wrap(engine: v4, object: this); | 
| 732 |  | 
| 733 |         for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) { | 
| 734 |             function = it.value().value(); | 
| 735 |             jsCall->args[0] = QV4::Value::fromUInt32(i: QDateTime::currentMSecsSinceEpoch()); | 
| 736 |             function->call(data: jsCall); | 
| 737 |         } | 
| 738 |     } | 
| 739 |     else { | 
| 740 |         if (d->dirtyRect.isValid()) { | 
| 741 |             if (d->hasTileSize && d->hasCanvasWindow) | 
| 742 |                 emit paint(region: tiledRect(window: d->canvasWindow.intersected(r: d->dirtyRect.toAlignedRect()), tileSize: d->tileSize)); | 
| 743 |             else | 
| 744 |                 emit paint(region: d->dirtyRect.toRect()); | 
| 745 |             d->dirtyRect = QRectF(); | 
| 746 |         } | 
| 747 |     } | 
| 748 |  | 
| 749 |     if (d->context) { | 
| 750 |         if (d->renderStrategy == QQuickCanvasItem::Cooperative) | 
| 751 |             update(); | 
| 752 |         else | 
| 753 |             d->context->flush(); | 
| 754 |     } | 
| 755 | } | 
| 756 |  | 
| 757 | QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) | 
| 758 | { | 
| 759 |     Q_D(QQuickCanvasItem); | 
| 760 |  | 
| 761 |     if (!d->context || d->canvasWindow.size().isEmpty()) { | 
| 762 |         if (d->textureProvider) { | 
| 763 |             d->textureProvider->tex = nullptr; | 
| 764 |             d->textureProvider->fireTextureChanged(); | 
| 765 |         } | 
| 766 |         delete oldNode; | 
| 767 |         return nullptr; | 
| 768 |     } | 
| 769 |  | 
| 770 |     QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode); | 
| 771 |     if (!node) { | 
| 772 |         QSGRenderContext *rc = QQuickWindowPrivate::get(c: window())->context; | 
| 773 |         node = rc->sceneGraphContext()->createInternalImageNode(renderContext: rc); | 
| 774 |         d->node = node; | 
| 775 |     } | 
| 776 |  | 
| 777 |  | 
| 778 |     if (d->smooth) | 
| 779 |         node->setFiltering(QSGTexture::Linear); | 
| 780 |     else | 
| 781 |         node->setFiltering(QSGTexture::Nearest); | 
| 782 |  | 
| 783 |     if (d->renderStrategy == QQuickCanvasItem::Cooperative) { | 
| 784 |         d->context->prepare(canvasSize: d->canvasSize.toSize(), tileSize: d->tileSize, canvasWindow: d->canvasWindow.toRect(), dirtyRect: d->dirtyRect.toRect(), smooth: d->smooth, antialiasing: antialiasing()); | 
| 785 |         d->context->flush(); | 
| 786 |     } | 
| 787 |  | 
| 788 |     QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(object: d->context); | 
| 789 |     QQuickContext2DTexture *factory = ctx->texture(); | 
| 790 |     QSGTexture *texture = factory->textureForNextFrame(lastFrame: d->nodeTexture, window: window()); | 
| 791 |     if (!texture) { | 
| 792 |         delete node; | 
| 793 |         d->node = nullptr; | 
| 794 |         d->nodeTexture = nullptr; | 
| 795 |         if (d->textureProvider) { | 
| 796 |             d->textureProvider->tex = nullptr; | 
| 797 |             d->textureProvider->fireTextureChanged(); | 
| 798 |         } | 
| 799 |         return nullptr; | 
| 800 |     } | 
| 801 |  | 
| 802 |     d->nodeTexture = texture; | 
| 803 |     node->setTexture(texture); | 
| 804 |     node->setTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size())); | 
| 805 |     node->setInnerTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size())); | 
| 806 |     node->update(); | 
| 807 |  | 
| 808 |     if (d->textureProvider) { | 
| 809 |         d->textureProvider->tex = d->nodeTexture; | 
| 810 |         d->textureProvider->fireTextureChanged(); | 
| 811 |     } | 
| 812 |     return node; | 
| 813 | } | 
| 814 |  | 
| 815 | bool QQuickCanvasItem::isTextureProvider() const | 
| 816 | { | 
| 817 |     return true; | 
| 818 | } | 
| 819 |  | 
| 820 | QSGTextureProvider *QQuickCanvasItem::textureProvider() const | 
| 821 | { | 
| 822 |     // When Item::layer::enabled == true, QQuickItem will be a texture | 
| 823 |     // provider. In this case we should prefer to return the layer rather | 
| 824 |     // than the canvas itself. | 
| 825 |     if (QQuickItem::isTextureProvider()) | 
| 826 |         return QQuickItem::textureProvider(); | 
| 827 |  | 
| 828 |     Q_D(const QQuickCanvasItem); | 
| 829 | #if QT_CONFIG(opengl) | 
| 830 |     QQuickWindow *w = window(); | 
| 831 |     if (!w || !w->isSceneGraphInitialized() | 
| 832 |             || QThread::currentThread() != QQuickWindowPrivate::get(c: w)->context->thread()) { | 
| 833 |         qWarning(msg: "QQuickCanvasItem::textureProvider: can only be queried on the rendering thread of an exposed window" ); | 
| 834 |         return nullptr; | 
| 835 |     } | 
| 836 | #endif | 
| 837 |     if (!d->textureProvider) | 
| 838 |         d->textureProvider = new QQuickCanvasTextureProvider; | 
| 839 |     d->textureProvider->tex = d->nodeTexture; | 
| 840 |     return d->textureProvider; | 
| 841 | } | 
| 842 |  | 
| 843 | /*! | 
| 844 |     \qmlmethod object QtQuick::Canvas::getContext(string contextId, ... args) | 
| 845 |  | 
| 846 |     Returns a drawing context, or \c null if no context is available. | 
| 847 |  | 
| 848 |     The \a contextId parameter names the required context. The Canvas item | 
| 849 |     will return a context that implements the required drawing mode. After the | 
| 850 |     first call to getContext, any subsequent call to getContext with the same | 
| 851 |     contextId will return the same context object. Any additional arguments | 
| 852 |     (\a args) are currently ignored. | 
| 853 |  | 
| 854 |     If the context type is not supported or the canvas has previously been | 
| 855 |     requested to provide a different and incompatible context type, \c null | 
| 856 |     will be returned. | 
| 857 |  | 
| 858 |     Canvas only supports a 2d context. | 
| 859 |  | 
| 860 | */ | 
| 861 |  | 
| 862 | void QQuickCanvasItem::getContext(QQmlV4Function *args) | 
| 863 | { | 
| 864 |     Q_D(QQuickCanvasItem); | 
| 865 |  | 
| 866 |     QV4::Scope scope(args->v4engine()); | 
| 867 |     QV4::ScopedString str(scope, (*args)[0]); | 
| 868 |     if (!str) { | 
| 869 |         qmlWarning(me: this) << "getContext should be called with a string naming the required context type" ; | 
| 870 |         args->setReturnValue(QV4::Encode::null()); | 
| 871 |         return; | 
| 872 |     } | 
| 873 |  | 
| 874 |     if (!d->available) { | 
| 875 |         qmlWarning(me: this) << "Unable to use getContext() at this time, please wait for available: true" ; | 
| 876 |         args->setReturnValue(QV4::Encode::null()); | 
| 877 |         return; | 
| 878 |     } | 
| 879 |  | 
| 880 |     QString contextId = str->toQString(); | 
| 881 |  | 
| 882 |     if (d->context != nullptr) { | 
| 883 |         if (d->context->contextNames().contains(str: contextId, cs: Qt::CaseInsensitive)) { | 
| 884 |             args->setReturnValue(d->context->v4value()); | 
| 885 |             return; | 
| 886 |         } | 
| 887 |  | 
| 888 |         qmlWarning(me: this) << "Canvas already initialized with a different context type" ; | 
| 889 |         args->setReturnValue(QV4::Encode::null()); | 
| 890 |         return; | 
| 891 |     } | 
| 892 |  | 
| 893 |     if (createContext(contextType: contextId)) | 
| 894 |         args->setReturnValue(d->context->v4value()); | 
| 895 |     else | 
| 896 |         args->setReturnValue(QV4::Encode::null()); | 
| 897 | } | 
| 898 |  | 
| 899 | /*! | 
| 900 |     \qmlmethod int QtQuick::Canvas::requestAnimationFrame(callback) | 
| 901 |  | 
| 902 |     This function schedules \a callback to be invoked before composing the Qt Quick | 
| 903 |     scene. | 
| 904 | */ | 
| 905 |  | 
| 906 | void QQuickCanvasItem::requestAnimationFrame(QQmlV4Function *args) | 
| 907 | { | 
| 908 |     QV4::Scope scope(args->v4engine()); | 
| 909 |     QV4::ScopedFunctionObject f(scope, (*args)[0]); | 
| 910 |     if (!f) { | 
| 911 |         qmlWarning(me: this) << "requestAnimationFrame should be called with an animation callback function" ; | 
| 912 |         args->setReturnValue(QV4::Encode::null()); | 
| 913 |         return; | 
| 914 |     } | 
| 915 |  | 
| 916 |     Q_D(QQuickCanvasItem); | 
| 917 |  | 
| 918 |     static int id = 0; | 
| 919 |  | 
| 920 |     d->animationCallbacks.insert(key: ++id, value: QV4::PersistentValue(scope.engine, f->asReturnedValue())); | 
| 921 |  | 
| 922 |     // QTBUG-55778: Calling polish directly here can lead to a polish loop | 
| 923 |     if (isVisible()) | 
| 924 |         schedulePolish(); | 
| 925 |  | 
| 926 |     args->setReturnValue(QV4::Encode(id)); | 
| 927 | } | 
| 928 |  | 
| 929 | /*! | 
| 930 |     \qmlmethod QtQuick::Canvas::cancelRequestAnimationFrame(int handle) | 
| 931 |  | 
| 932 |     This function will cancel the animation callback referenced by \a handle. | 
| 933 | */ | 
| 934 |  | 
| 935 | void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV4Function *args) | 
| 936 | { | 
| 937 |     QV4::Scope scope(args->v4engine()); | 
| 938 |     QV4::ScopedValue v(scope, (*args)[0]); | 
| 939 |     if (!v->isInteger()) { | 
| 940 |         qmlWarning(me: this) << "cancelRequestAnimationFrame should be called with an animation callback id" ; | 
| 941 |         args->setReturnValue(QV4::Encode::null()); | 
| 942 |         return; | 
| 943 |     } | 
| 944 |  | 
| 945 |     d_func()->animationCallbacks.remove(key: v->integerValue()); | 
| 946 | } | 
| 947 |  | 
| 948 |  | 
| 949 | /*! | 
| 950 |     \qmlmethod QtQuick::Canvas::requestPaint() | 
| 951 |  | 
| 952 |     Request the entire visible region be re-drawn. | 
| 953 |  | 
| 954 |     \sa markDirty() | 
| 955 | */ | 
| 956 |  | 
| 957 | void QQuickCanvasItem::requestPaint() | 
| 958 | { | 
| 959 |     markDirty(dirtyRect: d_func()->canvasWindow); | 
| 960 | } | 
| 961 |  | 
| 962 | /*! | 
| 963 |     \qmlmethod QtQuick::Canvas::markDirty(rect area) | 
| 964 |  | 
| 965 |     Marks the given \a area as dirty, so that when this area is visible the | 
| 966 |     canvas renderer will redraw it. This will trigger the \c paint signal. | 
| 967 |  | 
| 968 |     \sa paint, requestPaint() | 
| 969 | */ | 
| 970 |  | 
| 971 | void QQuickCanvasItem::markDirty(const QRectF& rect) | 
| 972 | { | 
| 973 |     Q_D(QQuickCanvasItem); | 
| 974 |     if (!d->available) | 
| 975 |         return; | 
| 976 |  | 
| 977 |     d->dirtyRect |= rect; | 
| 978 |  | 
| 979 |     polish(); | 
| 980 | } | 
| 981 |  | 
| 982 | void QQuickCanvasItem::checkAnimationCallbacks() | 
| 983 | { | 
| 984 |     if (d_func()->animationCallbacks.size() > 0 && isVisible()) | 
| 985 |         polish(); | 
| 986 | } | 
| 987 |  | 
| 988 | /*! | 
| 989 |     \qmlmethod bool QtQuick::Canvas::save(string filename) | 
| 990 |  | 
| 991 |     Saves the current canvas content into an image file \a filename. | 
| 992 |     The saved image format is automatically decided by the \a filename's | 
| 993 |     suffix. Returns \c true on success. | 
| 994 |  | 
| 995 |     \note Calling this method will force painting the whole canvas, not just the | 
| 996 |     current canvas visible window. | 
| 997 |  | 
| 998 |     \sa canvasWindow, canvasSize, toDataURL() | 
| 999 | */ | 
| 1000 | bool QQuickCanvasItem::save(const QString &filename) const | 
| 1001 | { | 
| 1002 |     Q_D(const QQuickCanvasItem); | 
| 1003 |     QUrl url = d->baseUrl.resolved(relative: QUrl::fromLocalFile(localfile: filename)); | 
| 1004 |     return toImage().save(fileName: url.toLocalFile()); | 
| 1005 | } | 
| 1006 |  | 
| 1007 | QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url) | 
| 1008 | { | 
| 1009 |     Q_D(QQuickCanvasItem); | 
| 1010 |     QUrl fullPathUrl = d->baseUrl.resolved(relative: url); | 
| 1011 |     if (!d->pixmaps.contains(key: fullPathUrl)) { | 
| 1012 |         loadImage(url); | 
| 1013 |     } | 
| 1014 |     return d->pixmaps.value(key: fullPathUrl); | 
| 1015 | } | 
| 1016 |  | 
| 1017 | /*! | 
| 1018 |     \qmlsignal QtQuick::Canvas::imageLoaded() | 
| 1019 |  | 
| 1020 |     This signal is emitted when an image has been loaded. | 
| 1021 |  | 
| 1022 |     \sa loadImage() | 
| 1023 | */ | 
| 1024 |  | 
| 1025 | /*! | 
| 1026 |     \qmlmethod QtQuick::Canvas::loadImage(url image) | 
| 1027 |  | 
| 1028 |     Loads the given \a image asynchronously. | 
| 1029 |  | 
| 1030 |     Once the image is ready, imageLoaded() signal will be emitted. | 
| 1031 |     The loaded image can be unloaded with the unloadImage() method. | 
| 1032 |  | 
| 1033 |     \note Only loaded images can be painted on the Canvas item. | 
| 1034 |  | 
| 1035 |     \sa unloadImage(), imageLoaded(), isImageLoaded(), | 
| 1036 |         Context2D::createImageData(), Context2D::drawImage() | 
| 1037 | */ | 
| 1038 | void QQuickCanvasItem::loadImage(const QUrl& url) | 
| 1039 | { | 
| 1040 |     Q_D(QQuickCanvasItem); | 
| 1041 |     QUrl fullPathUrl = d->baseUrl.resolved(relative: url); | 
| 1042 |     if (!d->pixmaps.contains(key: fullPathUrl)) { | 
| 1043 |         QQuickPixmap* pix = new QQuickPixmap(); | 
| 1044 |         QQmlRefPointer<QQuickCanvasPixmap> canvasPix; | 
| 1045 |         canvasPix.adopt(new QQuickCanvasPixmap(pix)); | 
| 1046 |         d->pixmaps.insert(key: fullPathUrl, value: canvasPix); | 
| 1047 |  | 
| 1048 |         pix->load(qmlEngine(this) | 
| 1049 |                 , fullPathUrl | 
| 1050 |                 , options: QQuickPixmap::Cache | QQuickPixmap::Asynchronous); | 
| 1051 |         if (pix->isLoading()) | 
| 1052 |             pix->connectFinished(this, SIGNAL(imageLoaded())); | 
| 1053 |     } | 
| 1054 | } | 
| 1055 | /*! | 
| 1056 |     \qmlmethod QtQuick::Canvas::unloadImage(url image) | 
| 1057 |  | 
| 1058 |     Unloads the \a image. | 
| 1059 |  | 
| 1060 |     Once an image is unloaded, it cannot be painted by the canvas context | 
| 1061 |     unless it is loaded again. | 
| 1062 |  | 
| 1063 |     \sa loadImage(), imageLoaded(), isImageLoaded(), | 
| 1064 |         Context2D::createImageData(), Context2D::drawImage | 
| 1065 | */ | 
| 1066 | void QQuickCanvasItem::unloadImage(const QUrl& url) | 
| 1067 | { | 
| 1068 |     Q_D(QQuickCanvasItem); | 
| 1069 |     d->pixmaps.remove(key: d->baseUrl.resolved(relative: url)); | 
| 1070 | } | 
| 1071 |  | 
| 1072 | /*! | 
| 1073 |     \qmlmethod QtQuick::Canvas::isImageError(url image) | 
| 1074 |  | 
| 1075 |     Returns \c true if the \a image failed to load, \c false otherwise. | 
| 1076 |  | 
| 1077 |     \sa loadImage() | 
| 1078 | */ | 
| 1079 | bool QQuickCanvasItem::isImageError(const QUrl& url) const | 
| 1080 | { | 
| 1081 |     Q_D(const QQuickCanvasItem); | 
| 1082 |     QUrl fullPathUrl = d->baseUrl.resolved(relative: url); | 
| 1083 |     return d->pixmaps.contains(key: fullPathUrl) | 
| 1084 |         && d->pixmaps.value(key: fullPathUrl)->pixmap()->isError(); | 
| 1085 | } | 
| 1086 |  | 
| 1087 | /*! | 
| 1088 |   \qmlmethod QtQuick::Canvas::isImageLoading(url image) | 
| 1089 |   Returns true if the \a image is currently loading. | 
| 1090 |  | 
| 1091 |   \sa loadImage() | 
| 1092 | */ | 
| 1093 | bool QQuickCanvasItem::isImageLoading(const QUrl& url) const | 
| 1094 | { | 
| 1095 |     Q_D(const QQuickCanvasItem); | 
| 1096 |     QUrl fullPathUrl = d->baseUrl.resolved(relative: url); | 
| 1097 |     return d->pixmaps.contains(key: fullPathUrl) | 
| 1098 |         && d->pixmaps.value(key: fullPathUrl)->pixmap()->isLoading(); | 
| 1099 | } | 
| 1100 | /*! | 
| 1101 |   \qmlmethod QtQuick::Canvas::isImageLoaded(url image) | 
| 1102 |   Returns true if the \a image is successfully loaded and ready to use. | 
| 1103 |  | 
| 1104 |   \sa loadImage() | 
| 1105 | */ | 
| 1106 | bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const | 
| 1107 | { | 
| 1108 |     Q_D(const QQuickCanvasItem); | 
| 1109 |     QUrl fullPathUrl = d->baseUrl.resolved(relative: url); | 
| 1110 |     return d->pixmaps.contains(key: fullPathUrl) | 
| 1111 |         && d->pixmaps.value(key: fullPathUrl)->pixmap()->isReady(); | 
| 1112 | } | 
| 1113 |  | 
| 1114 | QImage QQuickCanvasItem::toImage(const QRectF& rect) const | 
| 1115 | { | 
| 1116 |     Q_D(const QQuickCanvasItem); | 
| 1117 |  | 
| 1118 |     if (!d->context) | 
| 1119 |         return QImage(); | 
| 1120 |  | 
| 1121 |     const QRectF &rectSource = rect.isEmpty() ? canvasWindow() : rect; | 
| 1122 |     const qreal dpr = window() ? window()->effectiveDevicePixelRatio() : qreal(1); | 
| 1123 |     const QRectF rectScaled(rectSource.topLeft() * dpr, rectSource.size() * dpr); | 
| 1124 |  | 
| 1125 |     QImage image = d->context->toImage(bounds: rectScaled); | 
| 1126 |     image.setDevicePixelRatio(dpr); | 
| 1127 |     return image; | 
| 1128 | } | 
| 1129 |  | 
| 1130 | static const char* mimeToType(const QString &mime) | 
| 1131 | { | 
| 1132 |     const QLatin1String imagePrefix("image/" ); | 
| 1133 |     if (!mime.startsWith(s: imagePrefix)) | 
| 1134 |         return nullptr; | 
| 1135 |     const QStringRef mimeExt = mime.midRef(position: imagePrefix.size()); | 
| 1136 |     if (mimeExt == QLatin1String("png" )) | 
| 1137 |         return "png" ; | 
| 1138 |     else if (mimeExt == QLatin1String("bmp" )) | 
| 1139 |         return "bmp" ; | 
| 1140 |     else if (mimeExt == QLatin1String("jpeg" )) | 
| 1141 |         return "jpeg" ; | 
| 1142 |     else if (mimeExt == QLatin1String("x-portable-pixmap" )) | 
| 1143 |         return "ppm" ; | 
| 1144 |     else if (mimeExt == QLatin1String("tiff" )) | 
| 1145 |         return "tiff" ; | 
| 1146 |     else if (mimeExt == QLatin1String("xpm" )) | 
| 1147 |         return "xpm" ; | 
| 1148 |     return nullptr; | 
| 1149 | } | 
| 1150 |  | 
| 1151 | /*! | 
| 1152 |   \qmlmethod string QtQuick::Canvas::toDataURL(string mimeType) | 
| 1153 |  | 
| 1154 |    Returns a data URL for the image in the canvas. | 
| 1155 |  | 
| 1156 |    The default \a mimeType is "image/png". | 
| 1157 |  | 
| 1158 |    \sa save() | 
| 1159 | */ | 
| 1160 | QString QQuickCanvasItem::toDataURL(const QString& mimeType) const | 
| 1161 | { | 
| 1162 |     QImage image = toImage(); | 
| 1163 |  | 
| 1164 |     if (!image.isNull()) { | 
| 1165 |         QByteArray ba; | 
| 1166 |         QBuffer buffer(&ba); | 
| 1167 |         buffer.open(openMode: QIODevice::WriteOnly); | 
| 1168 |         const QString mime = mimeType.toLower(); | 
| 1169 |         const char* type = mimeToType(mime); | 
| 1170 |         if (!type) | 
| 1171 |             return QStringLiteral("data:," ); | 
| 1172 |  | 
| 1173 |         image.save(device: &buffer, format: type); | 
| 1174 |         buffer.close(); | 
| 1175 |         return QLatin1String("data:" ) + mime + QLatin1String(";base64," ) + QLatin1String(ba.toBase64().constData()); | 
| 1176 |     } | 
| 1177 |     return QStringLiteral("data:," ); | 
| 1178 | } | 
| 1179 |  | 
| 1180 | void QQuickCanvasItem::delayedCreate() | 
| 1181 | { | 
| 1182 |     Q_D(QQuickCanvasItem); | 
| 1183 |  | 
| 1184 |     if (!d->context && !d->contextType.isNull()) | 
| 1185 |         createContext(contextType: d->contextType); | 
| 1186 |  | 
| 1187 |     requestPaint(); | 
| 1188 | } | 
| 1189 |  | 
| 1190 | bool QQuickCanvasItem::createContext(const QString &contextType) | 
| 1191 | { | 
| 1192 |     Q_D(QQuickCanvasItem); | 
| 1193 |  | 
| 1194 |     if (!window()) | 
| 1195 |         return false; | 
| 1196 |  | 
| 1197 |     if (contextType == QLatin1String("2d" )) { | 
| 1198 |         if (d->contextType.compare(other: QLatin1String("2d" ), cs: Qt::CaseInsensitive) != 0)  { | 
| 1199 |             d->contextType = QLatin1String("2d" ); | 
| 1200 |             emit contextTypeChanged(); // XXX: can't be in setContextType() | 
| 1201 |         } | 
| 1202 |         initializeContext(context: new QQuickContext2D(this)); | 
| 1203 |         return true; | 
| 1204 |     } | 
| 1205 |  | 
| 1206 |     return false; | 
| 1207 | } | 
| 1208 |  | 
| 1209 | void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args) | 
| 1210 | { | 
| 1211 |     Q_D(QQuickCanvasItem); | 
| 1212 |  | 
| 1213 |     d->context = context; | 
| 1214 |     d->context->init(canvasItem: this, args); | 
| 1215 |     d->context->setV4Engine(qmlEngine(this)->handle()); | 
| 1216 |     connect(asender: d->context, SIGNAL(textureChanged()), SLOT(update())); | 
| 1217 |     connect(asender: d->context, SIGNAL(textureChanged()), SIGNAL(painted())); | 
| 1218 |     emit contextChanged(); | 
| 1219 | } | 
| 1220 |  | 
| 1221 | QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize) | 
| 1222 | { | 
| 1223 |     if (window.isEmpty()) | 
| 1224 |         return QRect(); | 
| 1225 |  | 
| 1226 |     const int tw = tileSize.width(); | 
| 1227 |     const int th = tileSize.height(); | 
| 1228 |     const int h1 = window.left() / tw; | 
| 1229 |     const int v1 = window.top() / th; | 
| 1230 |  | 
| 1231 |     const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw; | 
| 1232 |     const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th; | 
| 1233 |  | 
| 1234 |     return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th); | 
| 1235 | } | 
| 1236 |  | 
| 1237 | /*! | 
| 1238 |     \qmlsignal QtQuick::Canvas::paint(rect region) | 
| 1239 |  | 
| 1240 |     This signal is emitted when the \a region needs to be rendered. If a context | 
| 1241 |     is active it can be referenced from the context property. | 
| 1242 |  | 
| 1243 |     This signal can be triggered by markdirty(), requestPaint() or by changing | 
| 1244 |     the current canvas window. | 
| 1245 | */ | 
| 1246 |  | 
| 1247 | /*! | 
| 1248 |     \qmlsignal QtQuick::Canvas::painted() | 
| 1249 |  | 
| 1250 |     This signal is emitted after all context painting commands are executed and | 
| 1251 |     the Canvas has been rendered. | 
| 1252 | */ | 
| 1253 |  | 
| 1254 | QT_END_NAMESPACE | 
| 1255 |  | 
| 1256 | #include "moc_qquickcanvasitem_p.cpp" | 
| 1257 |  |