doc fix
[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 enumeration 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) {
432             qmlInfo(this) << "Canvas:renderTarget not changeble once context is active.";
433             return;
434         }
435
436         d->renderTarget = target;
437         emit renderTargetChanged();
438     }
439 }
440
441 /*!
442     \qmlproperty enumeration QtQuick2::Canvas::renderStrategy
443     Holds the current canvas rendering strategy.
444
445     \list
446     \li Canvas.Immediate - context will perform graphics commands immediately in the main UI thread.
447     \li Canvas.Threaded - context will defer graphics commands to a private rendering thread.
448     \li Canvas.Cooperative - context will defer graphics commands to the applications global render thread.
449     \endlist
450
451     This hint is supplied along with renderTarget to the graphics context to
452     determine the method of rendering. A renderStrategy, renderTarget or a
453     combination may not be supported by a graphics context, in which case the
454     context will choose appropriate options and Canvas will signal the change
455     to the properties.
456
457     Configuration or runtime tests may cause the QML Scene Graph to render in
458     the GUI thread.  Selecting \c Canvas.Cooperative, does not guarantee
459     rendering will occur on a thread separate from the GUI thread.
460
461     The default value is \c Canvas.Cooperative.
462
463     \sa QtQuick2::Canvas::renderTarget
464 */
465
466 QQuickCanvasItem::RenderStrategy QQuickCanvasItem::renderStrategy() const
467 {
468     return d_func()->renderStrategy;
469 }
470
471 void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strategy)
472 {
473     Q_D(QQuickCanvasItem);
474     if (d->renderStrategy != strategy) {
475         if (d->contextInitialized) {
476             qmlInfo(this) << "Canvas:renderStrategy not changeable once context is active.";
477             return;
478         }
479
480         d->renderStrategy = strategy;
481         emit renderStrategyChanged();
482     }
483 }
484
485 QQuickCanvasContext* QQuickCanvasItem::rawContext() const
486 {
487     return d_func()->context;
488 }
489
490 bool QQuickCanvasItem::isPaintConnected()
491 {
492     IS_SIGNAL_CONNECTED(this, "paint(QRect)");
493 }
494
495 void QQuickCanvasItem::sceneGraphInitialized()
496 {
497     Q_D(QQuickCanvasItem);
498
499     d->available = true;
500     connect(this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
501     QMetaObject::invokeMethod(this, "availableChanged", Qt::QueuedConnection);
502
503     if (!d->contextType.isNull())
504         QMetaObject::invokeMethod(this, "delayedCreate", Qt::QueuedConnection);
505     else if (isPaintConnected())
506         QMetaObject::invokeMethod(this, "requestPaint", Qt::QueuedConnection);
507 }
508
509 void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
510 {
511     Q_D(QQuickCanvasItem);
512
513     QQuickItem::geometryChanged(newGeometry, oldGeometry);
514
515     QSizeF newSize = newGeometry.size();
516     if (!d->hasCanvasSize && d->canvasSize != newSize) {
517         d->canvasSize = newSize;
518         emit canvasSizeChanged();
519     }
520
521     if (!d->hasTileSize && d->tileSize != newSize) {
522         d->tileSize = newSize.toSize();
523         emit tileSizeChanged();
524     }
525
526     const QRectF rect = QRectF(QPointF(0, 0), newSize);
527
528     if (!d->hasCanvasWindow && d->canvasWindow != rect) {
529         d->canvasWindow = rect;
530         emit canvasWindowChanged();
531     }
532
533     if (d->available)
534         requestPaint();
535 }
536
537 void QQuickCanvasItem::componentComplete()
538 {
539     QQuickItem::componentComplete();
540
541     Q_D(QQuickCanvasItem);
542     d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
543 }
544
545 void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
546 {
547     QQuickItem::itemChange(change, value);
548     if (change != QQuickItem::ItemSceneChange)
549         return;
550
551     Q_D(QQuickCanvasItem);
552     if (d->available)
553         return;
554
555     if (value.canvas == 0)
556         return;
557
558     d->canvas = value.canvas;
559     if (d->canvas->openglContext() != 0) // available context == initialized
560         sceneGraphInitialized();
561     else
562         connect(d->canvas, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
563 }
564
565 void QQuickCanvasItem::updatePolish()
566 {
567     QQuickItem::updatePolish();
568
569     Q_D(QQuickCanvasItem);
570
571     if (d->contextInitialized)
572         d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth);
573
574     if (d->animationCallbacks.size() > 0 && isVisible()) {
575         QMap<int, v8::Persistent<v8::Function> > animationCallbacks = d->animationCallbacks;
576         d->animationCallbacks.clear();
577
578         foreach (int key, animationCallbacks.keys()) {
579             v8::HandleScope handle_scope;
580             v8::Handle<v8::Object> self = QQmlEnginePrivate::getV8Engine(qmlEngine(this))->newQObject(this).As<v8::Object>();
581             v8::Handle<v8::Value> args[] = { v8::Uint32::New(QDateTime::currentDateTimeUtc().toTime_t()) };
582             v8::Persistent<v8::Function> f = animationCallbacks.value(key);
583             f->Call(self, 1, args);
584             f.Dispose();
585         }
586     }
587     else {
588         if (d->dirtyRect.isValid()) {
589             if (d->hasTileSize && d->hasCanvasWindow)
590                 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
591             else
592                 emit paint(d->dirtyRect.toRect());
593             d->dirtyRect = QRectF();
594         }
595     }
596
597     if (d->contextInitialized) {
598         if (d->renderStrategy == QQuickCanvasItem::Cooperative)
599             update();
600         else
601             d->context->flush();
602     }
603 }
604
605 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
606 {
607     Q_D(QQuickCanvasItem);
608
609     if (!d->contextInitialized)
610         return 0;
611
612     class CanvasTextureNode : public QSGSimpleTextureNode
613     {
614     public:
615         CanvasTextureNode() : QSGSimpleTextureNode() {}
616         ~CanvasTextureNode() {delete texture();}
617     };
618
619     CanvasTextureNode *node = static_cast<CanvasTextureNode*>(oldNode);
620     if (!node) {
621         node = new CanvasTextureNode;
622     }
623
624     if (d->renderStrategy == QQuickCanvasItem::Cooperative)
625         d->context->flush();
626
627     node->setTexture(d->context->texture());
628     node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
629     return node;
630 }
631
632 /*!
633     \qmlmethod object QtQuick2::Canvas::getContext(string contextId, any... args)
634
635     Returns a drawing context or null if no context available.
636
637     The \a contextId parameter names the required context. The Canvas element
638     will return a context that implements the required drawing mode. After the
639     first call to getContext any subsequent call to getContext with the same
640     contextId will return the same context object.
641
642     If the context type is not supported or the canvas has previously been
643     requested to provide a different and incompatible context type, null will
644     be returned.
645
646     Canvas only supports a 2d context.
647 */
648
649 void QQuickCanvasItem::getContext(QQmlV8Function *args)
650 {
651     Q_D(QQuickCanvasItem);
652
653     if (args->Length() < 1 || !(*args)[0]->IsString()) {
654         qmlInfo(this) << "getContext should be called with a string naming the required context type";
655         args->returnValue(v8::Null());
656         return;
657     }
658
659     if (!d->available) {
660         qmlInfo(this) << "Unable to use getContext() at this time, please wait for available: true";
661         args->returnValue(v8::Null());
662         return;
663     }
664
665     QString contextId = QString::fromUtf16(*v8::String::Value((*args)[0]));
666
667     if (d->context != 0) {
668         if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
669             args->returnValue(d->context->v8value());
670             return;
671         }
672
673         qmlInfo(this) << "Canvas already initialized with a different context type";
674         args->returnValue(v8::Null());
675         return;
676     }
677
678     if (createContext(contextId))
679         args->returnValue(d->context->v8value());
680     else
681         args->returnValue(v8::Null());
682 }
683
684 /*!
685     \qmlmethod long QtQuick2::Canvas::requestAnimationFrame(callback)
686
687     This function schedules callback to be invoked before composing the QtQuick
688     scene.
689 */
690
691 void QQuickCanvasItem::requestAnimationFrame(QQmlV8Function *args)
692 {
693     if (args->Length() < 1 || !(*args)[0]->IsFunction()) {
694         qmlInfo(this) << "requestAnimationFrame should be called with an animation callback function";
695         args->returnValue(v8::Null());
696         return;
697     }
698
699     Q_D(QQuickCanvasItem);
700
701     static int id = 0;
702
703     d->animationCallbacks.insert(++id, v8::Persistent<v8::Function>::New(((*args)[0]).As<v8::Function>()));
704
705     if (isVisible())
706         polish();
707
708     args->returnValue(v8::Int32::New(id));
709 }
710
711 /*!
712     \qmlmethod void QtQuick2::Canvas::cancelRequestAnimationFrmae(long handle)
713
714     This function will cancel the animation callback referenced by \a handle.
715 */
716
717 void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV8Function *args)
718 {
719     if (args->Length() < 1 || !(*args)[0]->IsInt32()) {
720         qmlInfo(this) << "cancelRequestAnimationFrame should be called with an animation callback id";
721         args->returnValue(v8::Null());
722         return;
723     }
724
725     d_func()->animationCallbacks.remove((*args)[0]->Int32Value());
726 }
727
728
729 /*!
730     \qmlmethod void QtQuick2::Canvas::requestPaint()
731
732     Request the entire visible region be re-drawn.
733
734     \sa QtQuick::Canvas::markDirty
735 */
736
737 void QQuickCanvasItem::requestPaint()
738 {
739     markDirty(d_func()->canvasWindow);
740 }
741
742 /*!
743     \qmlmethod void QtQuick2::Canvas::markDirty(rect area)
744
745     Mark the given \a area as dirty, so that when this area is visible the
746     canvas renderer will redraw it. This will trigger the "onPaint" signal
747     handler function.
748
749     \sa QtQuick2::Canvas::paint, QtQuick2::Canvas::requestPaint
750 */
751
752 void QQuickCanvasItem::markDirty(const QRectF& rect)
753 {
754     Q_D(QQuickCanvasItem);
755     if (!d->available)
756         return;
757
758     d->dirtyRect |= rect;
759
760     polish();
761 }
762
763 void QQuickCanvasItem::checkAnimationCallbacks()
764 {
765     if (d_func()->animationCallbacks.size() > 0 && isVisible())
766         polish();
767 }
768
769 /*!
770   \qmlmethod bool QtQuick2::Canvas::save(string filename)
771
772    Save the current canvas content into an image file \a filename.
773    The saved image format is automatically decided by the \a filename's
774    suffix.
775
776    Note: calling this method will force painting the whole canvas, not just the
777    current canvas visible window.
778
779    \sa canvasWindow, canvasSize, toDataURL
780 */
781 bool QQuickCanvasItem::save(const QString &filename) const
782 {
783     Q_D(const QQuickCanvasItem);
784     QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
785     return toImage().save(url.toLocalFile());
786 }
787
788 QImage QQuickCanvasItem::loadedImage(const QUrl& url)
789 {
790     Q_D(QQuickCanvasItem);
791     QUrl fullPathUrl = d->baseUrl.resolved(url);
792     if (!d->images.contains(fullPathUrl)) {
793         loadImage(url);
794     }
795     QQuickPixmap* pix = d->images.value(fullPathUrl);
796     if (pix->isLoading() || pix->isError()) {
797         return QImage();
798     }
799     return pix->image();
800 }
801
802 /*!
803   \qmlmethod void QtQuick2::Canvas::loadImage(url image)
804     Loads the given \c image asynchronously.
805
806     When the image is ready, onImageLoaded will be emitted.
807     The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
808
809     Note: Only loaded images can be painted on the Canvas item.
810   \sa QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
811       QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
812 */
813 void QQuickCanvasItem::loadImage(const QUrl& url)
814 {
815     Q_D(QQuickCanvasItem);
816     QUrl fullPathUrl = d->baseUrl.resolved(url);
817     if (!d->images.contains(fullPathUrl)) {
818         QQuickPixmap* pix = new QQuickPixmap();
819         d->images.insert(fullPathUrl, pix);
820
821         pix->load(qmlEngine(this)
822                 , fullPathUrl
823                 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
824         if (pix->isLoading())
825             pix->connectFinished(this, SIGNAL(imageLoaded()));
826     }
827 }
828 /*!
829   \qmlmethod void QtQuick2::Canvas::unloadImage(url image)
830   Unloads the \c image.
831
832   Once an image is unloaded it cannot be painted by the canvas context
833   unless it is loaded again.
834
835   \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
836       QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
837 */
838 void QQuickCanvasItem::unloadImage(const QUrl& url)
839 {
840     Q_D(QQuickCanvasItem);
841     QUrl removeThis = d->baseUrl.resolved(url);
842     if (d->images.contains(removeThis)) {
843         delete d->images.value(removeThis);
844         d->images.remove(removeThis);
845     }
846 }
847
848 /*!
849   \qmlmethod void QtQuick2::Canvas::isImageError(url image)
850   Returns true if the \a image failed to load.
851
852   \sa QtQuick2::Canvas::loadImage
853 */
854 bool QQuickCanvasItem::isImageError(const QUrl& url) const
855 {
856     Q_D(const QQuickCanvasItem);
857     QUrl fullPathUrl = d->baseUrl.resolved(url);
858     return d->images.contains(fullPathUrl)
859         && d->images.value(fullPathUrl)->isError();
860 }
861
862 /*!
863   \qmlmethod void QtQuick2::Canvas::isImageLoading(url image)
864   Returns true if the \a image is currently loading.
865
866   \sa QtQuick2::Canvas::loadImage
867 */
868 bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
869 {
870     Q_D(const QQuickCanvasItem);
871     QUrl fullPathUrl = d->baseUrl.resolved(url);
872     return d->images.contains(fullPathUrl)
873         && d->images.value(fullPathUrl)->isLoading();
874 }
875 /*!
876   \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
877   Returns true if the \a image is sucessfully loaded and ready to use.
878
879   \sa QtQuick2::Canvas::loadImage
880 */
881 bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
882 {
883     Q_D(const QQuickCanvasItem);
884     QUrl fullPathUrl = d->baseUrl.resolved(url);
885     return d->images.contains(fullPathUrl)
886         && d->images.value(fullPathUrl)->isReady();
887 }
888
889 QImage QQuickCanvasItem::toImage(const QRectF& rect) const
890 {
891     Q_D(const QQuickCanvasItem);
892     if (d->contextInitialized) {
893         if (rect.isEmpty())
894             return d->context->toImage(canvasWindow());
895         else
896             return d->context->toImage(rect);
897     }
898
899     return QImage();
900 }
901
902 /*!
903   \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
904
905    Returns a data URL for the image in the canvas.
906
907    The default \a mimeType is "image/png".
908
909    \sa QtQuick2::Canvas::save
910 */
911 QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
912 {
913     QImage image = toImage();
914
915     if (!image.isNull()) {
916         QByteArray ba;
917         QBuffer buffer(&ba);
918         buffer.open(QIODevice::WriteOnly);
919         QString mime = mimeType.toLower();
920         QString type;
921         if (mime == QLatin1String("image/png")) {
922             type = QStringLiteral("PNG");
923         } else if (mime == QLatin1String("image/bmp"))
924             type = QStringLiteral("BMP");
925         else if (mime == QLatin1String("image/jpeg"))
926             type = QStringLiteral("JPEG");
927         else if (mime == QLatin1String("image/x-portable-pixmap"))
928             type = QStringLiteral("PPM");
929         else if (mime == QLatin1String("image/tiff"))
930             type = QStringLiteral("TIFF");
931         else if (mime == QLatin1String("image/xpm"))
932             type = QStringLiteral("XPM");
933         else
934             return QStringLiteral("data:,");
935
936         image.save(&buffer, type.toLatin1());
937         buffer.close();
938         QString dataUrl = QStringLiteral("data:%1;base64,%2");
939         return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
940     }
941     return QStringLiteral("data:,");
942 }
943
944 void QQuickCanvasItem::delayedCreate()
945 {
946     Q_D(QQuickCanvasItem);
947
948     if (!d->contextInitialized && !d->contextType.isNull())
949         createContext(d->contextType);
950
951     requestPaint();
952 }
953
954 bool QQuickCanvasItem::createContext(const QString &contextType)
955 {
956     Q_D(QQuickCanvasItem);
957
958     if (contextType == QLatin1String("2d")) {
959         if (d->contextType.compare(QLatin1String("2d"), Qt::CaseInsensitive) != 0)  {
960             d->contextType = QLatin1String("2d");
961             emit contextTypeChanged(); // XXX: can't be in setContextType()
962         }
963         initializeContext(new QQuickContext2D(this));
964         return true;
965     }
966
967     return false;
968 }
969
970 void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args)
971 {
972     Q_D(QQuickCanvasItem);
973
974     d->context = context;
975     d->context->init(this, args);
976     d->context->setV8Engine(QQmlEnginePrivate::getV8Engine(qmlEngine(this)));
977     d->contextInitialized = true;
978     connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
979     connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted()));
980     emit contextChanged();
981 }
982
983 QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
984 {
985     if (window.isEmpty())
986         return QRect();
987
988     const int tw = tileSize.width();
989     const int th = tileSize.height();
990     const int h1 = window.left() / tw;
991     const int v1 = window.top() / th;
992
993     const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
994     const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
995
996     return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
997 }
998
999 /*!
1000     \qmlsignal QtQuick2::Canvas::onPaint(rect region)
1001
1002     This handler is called to render the \a region. If a context is active it
1003     can be referenced from the context property.
1004
1005     This signal can be triggered by QtQuick2::Canvas::markdirty,
1006     QtQuick2::Canvas::requestPaint or by changing the current canvas window.
1007 */
1008
1009 /*!
1010     \qmlsignal QtQuick2::Canvas::onPainted()
1011
1012     This handler is called after all context painting commands are executed and
1013     the Canvas has been rendered.
1014 */
1015
1016 QT_END_NAMESPACE