7673c39400e652214dd8d9191b8e6e9a58b45976
[profile/ivi/qtdeclarative.git] / src / quick / items / context2d / qquickcanvasitem.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <private/qsgadaptationlayer_p.h>
43 #include "qquickcanvasitem_p.h"
44 #include <private/qquickitem_p.h>
45 #include <private/qquickcanvascontext_p.h>
46 #include <private/qquickcontext2d_p.h>
47 #include <qsgsimpletexturenode.h>
48 #include <QtQuick/private/qquickpixmapcache_p.h>
49
50 #include <qqmlinfo.h>
51 #include <private/qqmlengine_p.h>
52 #include <QtCore/QBuffer>
53
54 QT_BEGIN_NAMESPACE
55
56 QHash<QQmlEngine *,QQuickContext2DRenderThread*> QQuickContext2DRenderThread::renderThreads;
57 QMutex QQuickContext2DRenderThread::renderThreadsMutex;
58
59 QQuickContext2DRenderThread::QQuickContext2DRenderThread(QQmlEngine *eng)
60     : QThread(eng), m_engine(eng), m_eventLoopQuitHack(0)
61 {
62     Q_ASSERT(eng);
63     m_eventLoopQuitHack = new QObject;
64     m_eventLoopQuitHack->moveToThread(this);
65     connect(m_eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection);
66     start(QThread::IdlePriority);
67 }
68
69 QQuickContext2DRenderThread::~QQuickContext2DRenderThread()
70 {
71     renderThreadsMutex.lock();
72     renderThreads.remove(m_engine);
73     renderThreadsMutex.unlock();
74
75     m_eventLoopQuitHack->deleteLater();
76     wait();
77 }
78
79 QQuickContext2DRenderThread *QQuickContext2DRenderThread::instance(QQmlEngine *engine)
80 {
81     QQuickContext2DRenderThread *thread = 0;
82     renderThreadsMutex.lock();
83     if (renderThreads.contains(engine))
84         thread = renderThreads.value(engine);
85     else {
86         thread = new QQuickContext2DRenderThread(engine);
87         renderThreads.insert(engine, thread);
88     }
89     renderThreadsMutex.unlock();
90     return thread;
91 }
92
93 class QQuickCanvasItemPrivate : public QQuickItemPrivate
94 {
95 public:
96     QQuickCanvasItemPrivate();
97     ~QQuickCanvasItemPrivate();
98     QQuickCanvasContext* context;
99     QSizeF canvasSize;
100     QSize tileSize;
101     QRectF canvasWindow;
102     QRectF dirtyRect;
103     uint hasCanvasSize :1;
104     uint hasTileSize :1;
105     uint hasCanvasWindow :1;
106     uint available :1;
107     uint contextInitialized :1;
108     QQuickCanvasItem::RenderTarget renderTarget;
109     QQuickCanvasItem::RenderStrategy renderStrategy;
110     QString contextType;
111     QHash<QUrl, QQuickPixmap*> images;
112     QUrl baseUrl;
113     QMap<int, v8::Persistent<v8::Function> > animationCallbacks;
114 };
115
116 QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
117     : QQuickItemPrivate()
118     , context(0)
119     , canvasSize(1, 1)
120     , tileSize(1, 1)
121     , hasCanvasSize(false)
122     , hasTileSize(false)
123     , hasCanvasWindow(false)
124     , available(false)
125     , contextInitialized(false)
126     , renderTarget(QQuickCanvasItem::FramebufferObject)
127     , renderStrategy(QQuickCanvasItem::Cooperative)
128 {
129 }
130
131 QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
132 {
133     qDeleteAll(images);
134 }
135
136
137 /*!
138     \qmlclass Canvas QQuickCanvasItem
139     \inqmlmodule QtQuick 2
140     \since QtQuick 2.0
141     \brief The Canvas item provides a 2D canvas element which enables drawing via Javascript.
142     \inherits Item
143     \ingroup qml-basic-visual-elements
144
145     The Canvas item allows drawing of straight and curved lines, simple and
146     complex shapes, graphs, and referenced graphic images.  It can also add
147     text, colors, shadows, gradients, and patterns, and do low level pixel
148     operations. The Canvas output may be saved as an image file or serialized
149     to a URL.
150
151     To define a drawing area in the Canvas item set the \c width and \c height
152     properties.  For example, the following code creates a Canvas item which
153     has a drawing area with a height of 100 pixels and width of 200 pixels:
154     \qml
155     import QtQuick 2.0
156     Canvas {
157         id: mycanvas
158         width: 100
159         height: 200
160     }
161     \endqml
162
163     Currently the Canvas item only supports the two-dimensional rendering context.
164
165     \section1 Threaded Rendering and Render Target
166
167     The Canvas item supports two render targets: \c Canvas.Image and
168     \c Canvas.FramebufferObject.
169
170     The \c Canvas.Image render target is a \a QImage object.  This render
171     target supports background thread rendering, allowing complex or long
172     running painting to be executed without blocking the UI.
173
174     The Canvas.FramebufferObject render target utilizes OpenGL hardware
175     acceleration rather than rendering into system memory, which in many cases
176     results in faster rendering.
177
178     The default render target is Canvas.Image and the default renderStrategy is
179     Canvas.Threaded.
180
181     \section1 Tiled Canvas
182     The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize
183     and \l canvasWindow properties.
184
185     Tiling allows efficient display of a very large virtual canvas via a smaller
186     canvas window. The actual memory consumption is in relation to the canvas
187     window size.  The painting code can draw within the virtual canvas without
188     handling coordinate system transformations.
189
190     The tiles overlapping with the canvas window may be cached eliminating the
191     need to redraw, which can lead to significantly improved performance in
192     some situations.
193
194     \section1 Pixel Operations
195     All HTML5 2D context pixel operations are supported. In order to ensure
196     improved pixel reading/writing performance the \a Canvas.Image render
197     target should be chosen. The \a Canvas.FramebufferObject render target
198     requires the pixel data to be exchanged between the system memory and the
199     graphic card, which is significantly more expensive.  Rendering may also be
200     synchronized with the V-sync signal (to avoid
201     {en.wikipedia.org/wiki/Screen_tearing}{screen tearing}) which will further
202     impact pixel operations with \c Canvas.FrambufferObject render target.
203
204     \section1 Tips for Porting Existing HTML5 Canvas applications
205
206     Although the Canvas item is provides a HTML5 like API, HTML5 canvas
207     applications need to be modified to run in the Canvas item:
208     \list
209     \li Replace all DOM API calls with QML property bindings or Canvas item methods.
210     \li Replace all HTML event handlers with the \a MouseArea item.
211     \li Change setInterval/setTimeout function calls with the \a Timer item or
212        the use of requestAnimationFrame.
213     \li Place painting code into the \a QtQuick2::Canvas::onPaint handler and trigger
214        painting by calling the \c markDirty or \c requestPaint methods.
215     \li To draw images, load them by calling the Canvas's loadImage method and then request to paint
216        them in the onImageLoaded handler.
217     \endlist
218
219     \sa QtQuick2::Context2D
220 */
221
222 QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
223     : QQuickItem(*(new QQuickCanvasItemPrivate), parent)
224 {
225     setFlag(ItemHasContents);
226 }
227
228 QQuickCanvasItem::~QQuickCanvasItem()
229 {
230     Q_D(QQuickCanvasItem);
231     delete d->context;
232 }
233
234 /*!
235     \qmlproperty size QtQuick2::Canvas::available
236
237     Indicates when Canvas is able to provide a drawing context to operate on.
238 */
239
240 bool QQuickCanvasItem::isAvailable() const
241 {
242     return d_func()->available;
243 }
244
245 /*!
246     \qmlproperty string QtQuick2::Canvas::contextType
247     The type of drawing context to use.
248
249     This property is set to the name of the active context type.
250
251     If set explicitly the canvas will attempt to create a context of the
252     named type after becoming available.
253
254     The type name is the same as used in the getContext() call, for the 2d
255     canvas the value will be "2d".
256
257     \sa QtQuick2::Canvas::getContext QtQuick2::Canvas::available
258 */
259
260 QString QQuickCanvasItem::contextType() const
261 {
262     return d_func()->contextType;
263 }
264
265 void QQuickCanvasItem::setContextType(const QString &contextType)
266 {
267     Q_D(QQuickCanvasItem);
268
269     if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
270         return;
271
272     if (d->contextInitialized) {
273         qmlInfo(this) << "Canvas already initialized with a different context type";
274         return;
275     }
276
277     d->contextType = contextType;
278
279     if (d->available)
280         createContext(contextType);
281
282     emit contextTypeChanged();
283 }
284
285 /*!
286     \qmlproperty object QtQuick2::Canvas::context
287     Holds the active drawing context.
288
289     If the canvas is ready and there has been a successful call to getContext()
290     or the contextType property has been set with a supported context type,
291     this property will contain the current drawing context, otherwise null.
292 */
293
294 QQmlV8Handle QQuickCanvasItem::context() const
295 {
296     Q_D(const QQuickCanvasItem);
297     if (d->contextInitialized)
298         return QQmlV8Handle::fromHandle(d->context->v8value());
299
300     return QQmlV8Handle::fromHandle(v8::Null());
301 }
302
303 /*!
304     \qmlproperty size QtQuick2::Canvas::canvasSize
305     Holds the logical canvas size that the context paints on.
306
307     By default, the canvas size is the same size as the current canvas item
308     size.
309
310     By setting the canvasSize, tileSize and canvasWindow, the Canvas item can
311     act as a large virtual canvas with many separately rendered tile rectangles
312     Only those tiles within the current canvas window are painted by the Canvas
313     render engine.
314
315     \sa QtQuick2::Canvas::tileSize QtQuick2::Canvas::canvasWindow
316 */
317 QSizeF QQuickCanvasItem::canvasSize() const
318 {
319     Q_D(const QQuickCanvasItem);
320     return d->canvasSize;
321 }
322
323 void QQuickCanvasItem::setCanvasSize(const QSizeF & size)
324 {
325     Q_D(QQuickCanvasItem);
326     if (d->canvasSize != size) {
327         d->hasCanvasSize = true;
328         d->canvasSize = size;
329         emit canvasSizeChanged();
330
331         if (d->contextInitialized)
332             polish();
333     }
334 }
335
336 /*!
337     \qmlproperty size QtQuick2::Canvas::tileSize
338     Holds the canvas rendering tile size.
339
340     The Canvas item enters tiled mode by setting canvasSize, tileSize and the
341     canvasWindow. This can improve rendering performance by rendering and
342     caching tiles instead of rendering the whole canvas every time.
343
344     Memory will be consumed only by those tiles within the current visible
345     region.
346
347     By default the tileSize is the same as the canvasSize.
348
349     \sa QtQuick2::Canvas::canvaasSize QtQuick2::Canvas::canvasWindow
350 */
351 QSize QQuickCanvasItem::tileSize() const
352 {
353     Q_D(const QQuickCanvasItem);
354     return d->tileSize;
355 }
356
357 void QQuickCanvasItem::setTileSize(const QSize & size)
358 {
359     Q_D(QQuickCanvasItem);
360     if (d->tileSize != size) {
361         d->hasTileSize = true;
362         d->tileSize = size;
363
364         emit tileSizeChanged();
365
366         if (d->contextInitialized)
367             polish();
368     }
369 }
370
371 /*!
372     \qmlproperty rect QtQuick2::Canvas::canvasWindow
373      Holds the current canvas visible window.
374
375      By default the canvasWindow size is the same as the Canvas item size with
376      the top-left point as (0, 0).
377
378      If the canvasSize is different to the Canvas item size, the Canvas item
379      can display different visible areas by changing the canvas windowSize
380      and/or position.
381
382     \sa QtQuick2::Canvas::canvasSize QtQuick2::Canvas::tileSize
383 */
384 QRectF QQuickCanvasItem::canvasWindow() const
385 {
386     Q_D(const QQuickCanvasItem);
387     return d->canvasWindow;
388 }
389
390 void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
391 {
392     Q_D(QQuickCanvasItem);
393     if (d->canvasWindow != rect) {
394         d->canvasWindow = rect;
395
396         d->hasCanvasWindow = true;
397         emit canvasWindowChanged();
398
399         if (d->contextInitialized)
400             polish();
401     }
402 }
403
404 /*!
405     \qmlproperty bool QtQuick2::Canvas::renderTarget
406     Holds the current canvas render target.
407
408     \list
409     \li Canvas.Image  - render to an in memory image buffer.
410     \li Canvas.FramebufferObject - render to an OpenGL frame buffer
411     \endlist
412
413     This hint is supplied along with renderStrategy to the graphics context to
414     determine the method of rendering. A renderStrategy, renderTarget or a
415     combination may not be supported by a graphics context, in which case the
416     context will choose appropriate options and Canvas will signal the change
417     to the properties.
418
419     The default render target is \c Canvas.FramebufferObject.
420 */
421 QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const
422 {
423     Q_D(const QQuickCanvasItem);
424     return d->renderTarget;
425 }
426
427 void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target)
428 {
429     Q_D(QQuickCanvasItem);
430     if (d->renderTarget != target) {
431         if (d->contextInitialized)         // target not changeable once context is active
432             return;
433
434         d->renderTarget = target;
435         emit renderTargetChanged();
436     }
437 }
438
439 /*!
440     \qmlproperty bool QtQuick2::Canvas::renderStrategy
441     Holds the current canvas rendering strategy.
442
443     \list
444     \li Canvas.Immediate - context will perform graphics commands immediately in the main UI thread.
445     \li Canvas.Threaded - context will defer graphics commands to a private rendering thread.
446     \li Canvas.Cooperative - context will defer graphics commands to the applications global render thread.
447     \endlist
448
449     This hint is supplied along with renderTarget to the graphics context to
450     determine the method of rendering. A renderStrategy, renderTarget or a
451     combination may not be supported by a graphics context, in which case the
452     context will choose appropriate options and Canvas will signal the change
453     to the properties.
454
455     Configuration or runtime tests may cause the QML Scene Graph to render in
456     the GUI thread.  Selecting \c Canvas.Cooperative, does not guarantee
457     rendering will occur on a thread separate from the GUI thread.
458
459     The default value is \c Canvas.Cooperative.
460
461     \sa QtQuick2::Canvas::renderTarget
462 */
463
464 QQuickCanvasItem::RenderStrategy QQuickCanvasItem::renderStrategy() const
465 {
466     return d_func()->renderStrategy;
467 }
468
469 void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strategy)
470 {
471     Q_D(QQuickCanvasItem);
472     if (d->renderStrategy != strategy) {
473         if (d->contextInitialized)   // Render strategy not changeable once context is active
474             return;
475
476         d->renderStrategy = strategy;
477         emit renderStrategyChanged();
478     }
479 }
480
481 QQuickCanvasContext* QQuickCanvasItem::rawContext() const
482 {
483     return d_func()->context;
484 }
485
486 bool QQuickCanvasItem::isPaintConnected()
487 {
488     IS_SIGNAL_CONNECTED(this, "paint(QRect)");
489 }
490
491 void QQuickCanvasItem::sceneGraphInitialized()
492 {
493     Q_D(QQuickCanvasItem);
494
495     d->available = true;
496     connect(this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
497     QMetaObject::invokeMethod(this, "availableChanged", Qt::QueuedConnection);
498
499     if (!d->contextType.isNull())
500         QMetaObject::invokeMethod(this, "delayedCreate", Qt::QueuedConnection);
501     else if (isPaintConnected())
502         QMetaObject::invokeMethod(this, "requestPaint", Qt::QueuedConnection);
503 }
504
505 void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
506 {
507     Q_D(QQuickCanvasItem);
508
509     QQuickItem::geometryChanged(newGeometry, oldGeometry);
510
511     QSizeF newSize = newGeometry.size();
512     if (!d->hasCanvasSize && d->canvasSize != newSize) {
513         d->canvasSize = newSize;
514         emit canvasSizeChanged();
515     }
516
517     if (!d->hasTileSize && d->tileSize != newSize) {
518         d->tileSize = newSize.toSize();
519         emit tileSizeChanged();
520     }
521
522     const QRectF rect = QRectF(QPointF(0, 0), newSize);
523
524     if (!d->hasCanvasWindow && d->canvasWindow != rect) {
525         d->canvasWindow = rect;
526         emit canvasWindowChanged();
527     }
528
529     if (d->available)
530         requestPaint();
531 }
532
533 void QQuickCanvasItem::componentComplete()
534 {
535     QQuickItem::componentComplete();
536
537     Q_D(QQuickCanvasItem);
538     d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
539 }
540
541 void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
542 {
543     QQuickItem::itemChange(change, value);
544     if (change != QQuickItem::ItemSceneChange)
545         return;
546
547     Q_D(QQuickCanvasItem);
548     if (d->available)
549         return;
550
551     if (value.canvas == 0)
552         return;
553
554     d->canvas = value.canvas;
555     if (d->canvas->openglContext() != 0) // available context == initialized
556         sceneGraphInitialized();
557     else
558         connect(d->canvas, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
559 }
560
561 void QQuickCanvasItem::updatePolish()
562 {
563     QQuickItem::updatePolish();
564
565     Q_D(QQuickCanvasItem);
566
567     if (d->contextInitialized)
568         d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth);
569
570     if (d->animationCallbacks.size() > 0 && isVisible()) {
571         QMap<int, v8::Persistent<v8::Function> > animationCallbacks = d->animationCallbacks;
572         d->animationCallbacks.clear();
573
574         foreach (int key, animationCallbacks.keys()) {
575             v8::HandleScope handle_scope;
576             v8::Handle<v8::Object> self = QQmlEnginePrivate::getV8Engine(qmlEngine(this))->newQObject(this).As<v8::Object>();
577             v8::Handle<v8::Value> args[] = { v8::Uint32::New(QDateTime::currentDateTimeUtc().toTime_t()) };
578             v8::Persistent<v8::Function> f = animationCallbacks.value(key);
579             f->Call(self, 1, args);
580             f.Dispose();
581         }
582     }
583     else {
584         if (d->dirtyRect.isValid()) {
585             if (d->hasTileSize && d->hasCanvasWindow)
586                 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
587             else
588                 emit paint(d->dirtyRect.toRect());
589             d->dirtyRect = QRectF();
590         }
591     }
592
593     if (d->contextInitialized) {
594         if (d->renderStrategy == QQuickCanvasItem::Cooperative)
595             update();
596         else
597             d->context->flush();
598     }
599 }
600
601 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
602 {
603     Q_D(QQuickCanvasItem);
604
605     if (!d->contextInitialized)
606         return 0;
607
608     class CanvasTextureNode : public QSGSimpleTextureNode
609     {
610     public:
611         CanvasTextureNode() : QSGSimpleTextureNode() {}
612         ~CanvasTextureNode() {delete texture();}
613     };
614
615     CanvasTextureNode *node = static_cast<CanvasTextureNode*>(oldNode);
616     if (!node) {
617         node = new CanvasTextureNode;
618     }
619
620     if (d->renderStrategy == QQuickCanvasItem::Cooperative)
621         d->context->flush();
622
623     node->setTexture(d->context->texture());
624     node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
625     return node;
626 }
627
628 /*!
629     \qmlmethod object QtQuick2::Canvas::getContext(string contextId, any... args)
630
631     Returns a drawing context or null if no context available.
632
633     The \a contextId parameter names the required context. The Canvas element
634     will return a context that implements the required drawing mode. After the
635     first call to getContext any subsequent call to getContext with the same
636     contextId will return the same context object.
637
638     If the context type is not supported or the canvas has previously been
639     requested to provide a different and incompatible context type, null will
640     be returned.
641
642     Canvas only supports a 2d context.
643 */
644
645 void QQuickCanvasItem::getContext(QQmlV8Function *args)
646 {
647     Q_D(QQuickCanvasItem);
648
649     if (args->Length() < 1 || !(*args)[0]->IsString()) {
650         qmlInfo(this) << "getContext should be called with a string naming the required context type";
651         args->returnValue(v8::Null());
652         return;
653     }
654
655     if (!d->available) {
656         qmlInfo(this) << "Unable to use getContext() at this time, please wait for available: true";
657         args->returnValue(v8::Null());
658         return;
659     }
660
661     QString contextId = QString::fromUtf16(*v8::String::Value((*args)[0]));
662
663     if (d->context != 0) {
664         if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
665             args->returnValue(d->context->v8value());
666             return;
667         }
668
669         qmlInfo(this) << "Canvas already initialized with a different context type";
670         args->returnValue(v8::Null());
671         return;
672     }
673
674     if (createContext(contextId))
675         args->returnValue(d->context->v8value());
676     else
677         args->returnValue(v8::Null());
678 }
679
680 /*!
681     \qmlmethod long QtQuick2::Canvas::requestAnimationFrame(callback)
682
683     This function schedules callback to be invoked before composing the QtQuick
684     scene.
685 */
686
687 void QQuickCanvasItem::requestAnimationFrame(QQmlV8Function *args)
688 {
689     if (args->Length() < 1 || !(*args)[0]->IsFunction()) {
690         qmlInfo(this) << "requestAnimationFrame should be called with an animation callback function";
691         args->returnValue(v8::Null());
692         return;
693     }
694
695     Q_D(QQuickCanvasItem);
696
697     static int id = 0;
698
699     d->animationCallbacks.insert(++id, v8::Persistent<v8::Function>::New(((*args)[0]).As<v8::Function>()));
700
701     if (isVisible())
702         polish();
703
704     args->returnValue(v8::Int32::New(id));
705 }
706
707 /*!
708     \qmlmethod void QtQuick2::Canvas::cancelRequestAnimationFrmae(long handle)
709
710     This function will cancel the animation callback referenced by \a handle.
711 */
712
713 void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV8Function *args)
714 {
715     if (args->Length() < 1 || !(*args)[0]->IsInt32()) {
716         qmlInfo(this) << "cancelRequestAnimationFrame should be called with an animation callback id";
717         args->returnValue(v8::Null());
718         return;
719     }
720
721     d_func()->animationCallbacks.remove((*args)[0]->Int32Value());
722 }
723
724
725 /*!
726     \qmlmethod void QtQuick2::Canvas::requestPaint()
727
728     Request the entire visible region be re-drawn.
729
730     \sa QtQuick::Canvas::markDirty
731 */
732
733 void QQuickCanvasItem::requestPaint()
734 {
735     markDirty(d_func()->canvasWindow);
736 }
737
738 /*!
739     \qmlmethod void QtQuick2::Canvas::markDirty(rect area)
740
741     Mark the given \a area as dirty, so that when this area is visible the
742     canvas renderer will redraw it. This will trigger the "onPaint" signal
743     handler function.
744
745     \sa QtQuick2::Canvas::paint QtQuick2::Canvas::requestPaint
746   */
747
748 void QQuickCanvasItem::markDirty(const QRectF& rect)
749 {
750     Q_D(QQuickCanvasItem);
751     if (!d->available)
752         return;
753
754     d->dirtyRect |= rect;
755
756     polish();
757 }
758
759 void QQuickCanvasItem::checkAnimationCallbacks()
760 {
761     if (d_func()->animationCallbacks.size() > 0 && isVisible())
762         polish();
763 }
764
765 /*!
766   \qmlmethod bool QtQuick2::Canvas::save(string filename)
767
768    Save the current canvas content into an image file \a filename.
769    The saved image format is automatically decided by the \a filename's
770    suffix.
771
772    Note: calling this method will force painting the whole canvas, not just the
773    current canvas visible window.
774
775    \sa canvasWindow canvasSize toDataURL
776   */
777 bool QQuickCanvasItem::save(const QString &filename) const
778 {
779     Q_D(const QQuickCanvasItem);
780     QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
781     return toImage().save(url.toLocalFile());
782 }
783
784 QImage QQuickCanvasItem::loadedImage(const QUrl& url)
785 {
786     Q_D(QQuickCanvasItem);
787     QUrl fullPathUrl = d->baseUrl.resolved(url);
788     if (!d->images.contains(fullPathUrl)) {
789         loadImage(url);
790     }
791     QQuickPixmap* pix = d->images.value(fullPathUrl);
792     if (pix->isLoading() || pix->isError()) {
793         return QImage();
794     }
795     return pix->image();
796 }
797
798 /*!
799   \qmlmethod void QtQuick2::Canvas::loadImage(url image)
800     Loads the given \c image asynchronously.
801
802     When the image is ready, onImageLoaded will be emitted.
803     The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
804
805     Note: Only loaded images can be painted on the Canvas item.
806   \sa QtQuick2::Canvas::unloadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
807   \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
808   */
809 void QQuickCanvasItem::loadImage(const QUrl& url)
810 {
811     Q_D(QQuickCanvasItem);
812     QUrl fullPathUrl = d->baseUrl.resolved(url);
813     if (!d->images.contains(fullPathUrl)) {
814         QQuickPixmap* pix = new QQuickPixmap();
815         d->images.insert(fullPathUrl, pix);
816
817         pix->load(qmlEngine(this)
818                 , fullPathUrl
819                 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
820         if (pix->isLoading())
821             pix->connectFinished(this, SIGNAL(imageLoaded()));
822     }
823 }
824 /*!
825   \qmlmethod void QtQuick2::Canvas::unloadImage(url image)
826   Unloads the \c image.
827
828   Once an image is unloaded it cannot be painted by the canvas context
829   unless it is loaded again.
830
831   \sa QtQuick2::Canvas::loadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
832   \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
833   */
834 void QQuickCanvasItem::unloadImage(const QUrl& url)
835 {
836     Q_D(QQuickCanvasItem);
837     QUrl removeThis = d->baseUrl.resolved(url);
838     if (d->images.contains(removeThis)) {
839         delete d->images.value(removeThis);
840         d->images.remove(removeThis);
841     }
842 }
843
844 /*!
845   \qmlmethod void QtQuick2::Canvas::isImageError(url image)
846   Returns true if the \a image failed to load.
847
848   \sa QtQuick2::Canvas::loadImage
849   */
850 bool QQuickCanvasItem::isImageError(const QUrl& url) const
851 {
852     Q_D(const QQuickCanvasItem);
853     QUrl fullPathUrl = d->baseUrl.resolved(url);
854     return d->images.contains(fullPathUrl)
855         && d->images.value(fullPathUrl)->isError();
856 }
857
858 /*!
859   \qmlmethod void QtQuick2::Canvas::isImageLoading(url image)
860   Returns true if the \a image is currently loading.
861
862   \sa QtQuick2::Canvas::loadImage
863   */
864 bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
865 {
866     Q_D(const QQuickCanvasItem);
867     QUrl fullPathUrl = d->baseUrl.resolved(url);
868     return d->images.contains(fullPathUrl)
869         && d->images.value(fullPathUrl)->isLoading();
870 }
871 /*!
872   \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
873   Returns true if the \a image is sucessfully loaded and ready to use.
874
875   \sa QtQuick2::Canvas::loadImage
876   */
877 bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
878 {
879     Q_D(const QQuickCanvasItem);
880     QUrl fullPathUrl = d->baseUrl.resolved(url);
881     return d->images.contains(fullPathUrl)
882         && d->images.value(fullPathUrl)->isReady();
883 }
884
885 QImage QQuickCanvasItem::toImage(const QRectF& rect) const
886 {
887     Q_D(const QQuickCanvasItem);
888     if (d->contextInitialized) {
889         if (rect.isEmpty())
890             return d->context->toImage(canvasWindow());
891         else
892             return d->context->toImage(rect);
893     }
894
895     return QImage();
896 }
897
898 /*!
899   \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
900
901    Returns a data URL for the image in the canvas.
902
903    The default \a mimeType is "image/png".
904
905    \sa QtQuick2::Canvas::save
906   */
907 QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
908 {
909     QImage image = toImage();
910
911     if (!image.isNull()) {
912         QByteArray ba;
913         QBuffer buffer(&ba);
914         buffer.open(QIODevice::WriteOnly);
915         QString mime = mimeType.toLower();
916         QString type;
917         if (mime == QLatin1Literal("image/png")) {
918             type = QLatin1Literal("PNG");
919         } else if (mime == QLatin1Literal("image/bmp"))
920             type = QLatin1Literal("BMP");
921         else if (mime == QLatin1Literal("image/jpeg"))
922             type = QLatin1Literal("JPEG");
923         else if (mime == QLatin1Literal("image/x-portable-pixmap"))
924             type = QLatin1Literal("PPM");
925         else if (mime == QLatin1Literal("image/tiff"))
926             type = QLatin1Literal("TIFF");
927         else if (mime == QLatin1Literal("image/xpm"))
928             type = QLatin1Literal("XPM");
929         else
930             return QLatin1Literal("data:,");
931
932         image.save(&buffer, type.toAscii());
933         buffer.close();
934         QString dataUrl = QLatin1Literal("data:%1;base64,%2");
935         return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
936     }
937     return QLatin1Literal("data:,");
938 }
939
940 void QQuickCanvasItem::delayedCreate()
941 {
942     Q_D(QQuickCanvasItem);
943
944     if (!d->contextInitialized && !d->contextType.isNull())
945         createContext(d->contextType);
946
947     requestPaint();
948 }
949
950 bool QQuickCanvasItem::createContext(const QString &contextType)
951 {
952     Q_D(QQuickCanvasItem);
953
954     if (contextType == QLatin1String("2d")) {
955         if (d->contextType.compare(QLatin1String("2d"), Qt::CaseInsensitive) != 0)  {
956             d->contextType = QLatin1String("2d");
957             emit contextTypeChanged(); // XXX: can't be in setContextType()
958         }
959         initializeContext(new QQuickContext2D(this));
960         return true;
961     }
962
963     return false;
964 }
965
966 void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args)
967 {
968     Q_D(QQuickCanvasItem);
969
970     d->context = context;
971     d->context->init(this, args);
972     d->context->setV8Engine(QQmlEnginePrivate::getV8Engine(qmlEngine(this)));
973     d->contextInitialized = true;
974     connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
975     connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted()));
976     emit contextChanged();
977 }
978
979 QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
980 {
981     if (window.isEmpty())
982         return QRect();
983
984     const int tw = tileSize.width();
985     const int th = tileSize.height();
986     const int h1 = window.left() / tw;
987     const int v1 = window.top() / th;
988
989     const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
990     const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
991
992     return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
993 }
994
995 /*!
996     \qmlsignal QtQuick2::Canvas::onPaint(rect region)
997
998     This handler is called to render the \a region. If a context is active it
999     can be referenced from the context property.
1000
1001     This signal can be triggered by QtQuick2::Canvas::markdirty,
1002     QtQuick2::Canvas::requestPaint or by changing the current canvas window.
1003 */
1004
1005 /*!
1006     \qmlsignal QtQuick2::Canvas::onPainted()
1007
1008     This handler is called after all context painting commands are executed and
1009     the Canvas has been rendered.
1010 */
1011
1012 QT_END_NAMESPACE