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