939e7b4fba33d2c1df1634fd17333f452bcc86fa
[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     {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 \a MouseArea item.
280     \li Change setInterval/setTimeout function calls with the \a Timer item or
281        the use of requestAnimationFrame.
282     \li Place painting code into the \a QtQuick2::Canvas::onPaint handler and trigger
283        painting by calling the \c markDirty or \c 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 QtQuick2::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 QtQuick2::Canvas::getContext, QtQuick2::Canvas::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 QtQuick2::Canvas::tileSize, QtQuick2::Canvas::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 QtQuick2::Canvas::canvaasSize, QtQuick2::Canvas::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 QtQuick2::Canvas::canvasSize, QtQuick2::Canvas::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 QtQuick2::Canvas::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
549         d->renderStrategy = strategy;
550         emit renderStrategyChanged();
551     }
552 }
553
554 QQuickCanvasContext* QQuickCanvasItem::rawContext() const
555 {
556     return d_func()->context;
557 }
558
559 bool QQuickCanvasItem::isPaintConnected()
560 {
561     IS_SIGNAL_CONNECTED(this, QQuickCanvasItem, paint, (const QRect &));
562 }
563
564 void QQuickCanvasItem::sceneGraphInitialized()
565 {
566     Q_D(QQuickCanvasItem);
567
568     d->available = true;
569     connect(this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
570     QMetaObject::invokeMethod(this, "availableChanged", Qt::QueuedConnection);
571
572     if (!d->contextType.isNull())
573         QMetaObject::invokeMethod(this, "delayedCreate", Qt::QueuedConnection);
574     else if (isPaintConnected())
575         QMetaObject::invokeMethod(this, "requestPaint", Qt::QueuedConnection);
576 }
577
578 void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
579 {
580     Q_D(QQuickCanvasItem);
581
582     QQuickItem::geometryChanged(newGeometry, oldGeometry);
583
584     QSizeF newSize = newGeometry.size();
585     if (!d->hasCanvasSize && d->canvasSize != newSize) {
586         d->canvasSize = newSize;
587         emit canvasSizeChanged();
588     }
589
590     if (!d->hasTileSize && d->tileSize != newSize) {
591         d->tileSize = newSize.toSize();
592         emit tileSizeChanged();
593     }
594
595     const QRectF rect = QRectF(QPointF(0, 0), newSize);
596
597     if (!d->hasCanvasWindow && d->canvasWindow != rect) {
598         d->canvasWindow = rect;
599         emit canvasWindowChanged();
600     }
601
602     if (d->available)
603         requestPaint();
604 }
605
606 void QQuickCanvasItem::componentComplete()
607 {
608     QQuickItem::componentComplete();
609
610     Q_D(QQuickCanvasItem);
611     d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
612 }
613
614 void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
615 {
616     QQuickItem::itemChange(change, value);
617     if (change != QQuickItem::ItemSceneChange)
618         return;
619
620     Q_D(QQuickCanvasItem);
621     if (d->available)
622         return;
623
624     if (value.window== 0)
625         return;
626
627     d->window = value.window;
628     if (d->window->openglContext() != 0) // available context == initialized
629         sceneGraphInitialized();
630     else
631         connect(d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
632 }
633
634 void QQuickCanvasItem::updatePolish()
635 {
636     QQuickItem::updatePolish();
637
638     Q_D(QQuickCanvasItem);
639
640     if (d->contextInitialized)
641         d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth);
642
643     if (d->animationCallbacks.size() > 0 && isVisible()) {
644         QMap<int, v8::Persistent<v8::Function> > animationCallbacks = d->animationCallbacks;
645         d->animationCallbacks.clear();
646
647         foreach (int key, animationCallbacks.keys()) {
648             v8::HandleScope handle_scope;
649             v8::Handle<v8::Object> self = QQmlEnginePrivate::getV8Engine(qmlEngine(this))->newQObject(this).As<v8::Object>();
650             v8::Handle<v8::Value> args[] = { v8::Uint32::New(QDateTime::currentDateTimeUtc().toTime_t()) };
651             v8::Persistent<v8::Function> f = animationCallbacks.value(key);
652             f->Call(self, 1, args);
653             f.Dispose();
654         }
655     }
656     else {
657         if (d->dirtyRect.isValid()) {
658             if (d->hasTileSize && d->hasCanvasWindow)
659                 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
660             else
661                 emit paint(d->dirtyRect.toRect());
662             d->dirtyRect = QRectF();
663         }
664     }
665
666     if (d->contextInitialized) {
667         if (d->renderStrategy == QQuickCanvasItem::Cooperative)
668             update();
669         else
670             d->context->flush();
671     }
672 }
673
674 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
675 {
676     Q_D(QQuickCanvasItem);
677
678     if (!d->contextInitialized)
679         return 0;
680
681     class CanvasTextureNode : public QSGSimpleTextureNode
682     {
683     public:
684         CanvasTextureNode() : QSGSimpleTextureNode() {}
685         ~CanvasTextureNode() {delete texture();}
686     };
687
688     CanvasTextureNode *node = static_cast<CanvasTextureNode*>(oldNode);
689     if (!node) {
690         node = new CanvasTextureNode;
691     }
692
693     if (d->renderStrategy == QQuickCanvasItem::Cooperative)
694         d->context->flush();
695
696     node->setTexture(d->context->texture());
697     node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
698     return node;
699 }
700
701 /*!
702     \qmlmethod object QtQuick2::Canvas::getContext(string contextId, any... args)
703
704     Returns a drawing context or null if no context available.
705
706     The \a contextId parameter names the required context. The Canvas item
707     will return a context that implements the required drawing mode. After the
708     first call to getContext any subsequent call to getContext with the same
709     contextId will return the same context object.
710
711     If the context type is not supported or the canvas has previously been
712     requested to provide a different and incompatible context type, null will
713     be returned.
714
715     Canvas only supports a 2d context.
716 */
717
718 void QQuickCanvasItem::getContext(QQmlV8Function *args)
719 {
720     Q_D(QQuickCanvasItem);
721
722     if (args->Length() < 1 || !(*args)[0]->IsString()) {
723         qmlInfo(this) << "getContext should be called with a string naming the required context type";
724         args->returnValue(v8::Null());
725         return;
726     }
727
728     if (!d->available) {
729         qmlInfo(this) << "Unable to use getContext() at this time, please wait for available: true";
730         args->returnValue(v8::Null());
731         return;
732     }
733
734     QString contextId = QString::fromUtf16(*v8::String::Value((*args)[0]));
735
736     if (d->context != 0) {
737         if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
738             args->returnValue(d->context->v8value());
739             return;
740         }
741
742         qmlInfo(this) << "Canvas already initialized with a different context type";
743         args->returnValue(v8::Null());
744         return;
745     }
746
747     if (createContext(contextId))
748         args->returnValue(d->context->v8value());
749     else
750         args->returnValue(v8::Null());
751 }
752
753 /*!
754     \qmlmethod long QtQuick2::Canvas::requestAnimationFrame(callback)
755
756     This function schedules callback to be invoked before composing the QtQuick
757     scene.
758 */
759
760 void QQuickCanvasItem::requestAnimationFrame(QQmlV8Function *args)
761 {
762     if (args->Length() < 1 || !(*args)[0]->IsFunction()) {
763         qmlInfo(this) << "requestAnimationFrame should be called with an animation callback function";
764         args->returnValue(v8::Null());
765         return;
766     }
767
768     Q_D(QQuickCanvasItem);
769
770     static int id = 0;
771
772     d->animationCallbacks.insert(++id, v8::Persistent<v8::Function>::New(((*args)[0]).As<v8::Function>()));
773
774     if (isVisible())
775         polish();
776
777     args->returnValue(v8::Int32::New(id));
778 }
779
780 /*!
781     \qmlmethod void QtQuick2::Canvas::cancelRequestAnimationFrmae(long handle)
782
783     This function will cancel the animation callback referenced by \a handle.
784 */
785
786 void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV8Function *args)
787 {
788     if (args->Length() < 1 || !(*args)[0]->IsInt32()) {
789         qmlInfo(this) << "cancelRequestAnimationFrame should be called with an animation callback id";
790         args->returnValue(v8::Null());
791         return;
792     }
793
794     d_func()->animationCallbacks.remove((*args)[0]->Int32Value());
795 }
796
797
798 /*!
799     \qmlmethod void QtQuick2::Canvas::requestPaint()
800
801     Request the entire visible region be re-drawn.
802
803     \sa QtQuick::Canvas::markDirty
804 */
805
806 void QQuickCanvasItem::requestPaint()
807 {
808     markDirty(d_func()->canvasWindow);
809 }
810
811 /*!
812     \qmlmethod void QtQuick2::Canvas::markDirty(rect area)
813
814     Mark the given \a area as dirty, so that when this area is visible the
815     canvas renderer will redraw it. This will trigger the "onPaint" signal
816     handler function.
817
818     \sa QtQuick2::Canvas::paint, QtQuick2::Canvas::requestPaint
819 */
820
821 void QQuickCanvasItem::markDirty(const QRectF& rect)
822 {
823     Q_D(QQuickCanvasItem);
824     if (!d->available)
825         return;
826
827     d->dirtyRect |= rect;
828
829     polish();
830 }
831
832 void QQuickCanvasItem::checkAnimationCallbacks()
833 {
834     if (d_func()->animationCallbacks.size() > 0 && isVisible())
835         polish();
836 }
837
838 /*!
839   \qmlmethod bool QtQuick2::Canvas::save(string filename)
840
841    Save the current canvas content into an image file \a filename.
842    The saved image format is automatically decided by the \a filename's
843    suffix.
844
845    Note: calling this method will force painting the whole canvas, not just the
846    current canvas visible window.
847
848    \sa canvasWindow, canvasSize, toDataURL
849 */
850 bool QQuickCanvasItem::save(const QString &filename) const
851 {
852     Q_D(const QQuickCanvasItem);
853     QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
854     return toImage().save(url.toLocalFile());
855 }
856
857 QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url)
858 {
859     Q_D(QQuickCanvasItem);
860     QUrl fullPathUrl = d->baseUrl.resolved(url);
861     if (!d->pixmaps.contains(fullPathUrl)) {
862         loadImage(url);
863     }
864     return d->pixmaps.value(fullPathUrl);
865 }
866
867 /*!
868   \qmlmethod void QtQuick2::Canvas::loadImage(url image)
869     Loads the given \c image asynchronously.
870
871     When the image is ready, onImageLoaded will be emitted.
872     The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
873
874     Note: Only loaded images can be painted on the Canvas item.
875   \sa QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
876       QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
877 */
878 void QQuickCanvasItem::loadImage(const QUrl& url)
879 {
880     Q_D(QQuickCanvasItem);
881     QUrl fullPathUrl = d->baseUrl.resolved(url);
882     if (!d->pixmaps.contains(fullPathUrl)) {
883         QQuickPixmap* pix = new QQuickPixmap();
884         QQmlRefPointer<QQuickCanvasPixmap> canvasPix;
885         canvasPix.take(new QQuickCanvasPixmap(pix, d->window));
886         d->pixmaps.insert(fullPathUrl, canvasPix);
887
888         pix->load(qmlEngine(this)
889                 , fullPathUrl
890                 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
891         if (pix->isLoading())
892             pix->connectFinished(this, SIGNAL(imageLoaded()));
893     }
894 }
895 /*!
896   \qmlmethod void QtQuick2::Canvas::unloadImage(url image)
897   Unloads the \c image.
898
899   Once an image is unloaded it cannot be painted by the canvas context
900   unless it is loaded again.
901
902   \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
903       QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
904 */
905 void QQuickCanvasItem::unloadImage(const QUrl& url)
906 {
907     Q_D(QQuickCanvasItem);
908     d->pixmaps.remove(d->baseUrl.resolved(url));
909 }
910
911 /*!
912   \qmlmethod void QtQuick2::Canvas::isImageError(url image)
913   Returns true if the \a image failed to load.
914
915   \sa QtQuick2::Canvas::loadImage
916 */
917 bool QQuickCanvasItem::isImageError(const QUrl& url) const
918 {
919     Q_D(const QQuickCanvasItem);
920     QUrl fullPathUrl = d->baseUrl.resolved(url);
921     return d->pixmaps.contains(fullPathUrl)
922         && d->pixmaps.value(fullPathUrl)->pixmap()->isError();
923 }
924
925 /*!
926   \qmlmethod void QtQuick2::Canvas::isImageLoading(url image)
927   Returns true if the \a image is currently loading.
928
929   \sa QtQuick2::Canvas::loadImage
930 */
931 bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
932 {
933     Q_D(const QQuickCanvasItem);
934     QUrl fullPathUrl = d->baseUrl.resolved(url);
935     return d->pixmaps.contains(fullPathUrl)
936         && d->pixmaps.value(fullPathUrl)->pixmap()->isLoading();
937 }
938 /*!
939   \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
940   Returns true if the \a image is sucessfully loaded and ready to use.
941
942   \sa QtQuick2::Canvas::loadImage
943 */
944 bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
945 {
946     Q_D(const QQuickCanvasItem);
947     QUrl fullPathUrl = d->baseUrl.resolved(url);
948     return d->pixmaps.contains(fullPathUrl)
949         && d->pixmaps.value(fullPathUrl)->pixmap()->isReady();
950 }
951
952 QImage QQuickCanvasItem::toImage(const QRectF& rect) const
953 {
954     Q_D(const QQuickCanvasItem);
955     if (d->contextInitialized) {
956         if (rect.isEmpty())
957             return d->context->toImage(canvasWindow());
958         else
959             return d->context->toImage(rect);
960     }
961
962     return QImage();
963 }
964
965 /*!
966   \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
967
968    Returns a data URL for the image in the canvas.
969
970    The default \a mimeType is "image/png".
971
972    \sa QtQuick2::Canvas::save
973 */
974 QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
975 {
976     QImage image = toImage();
977
978     if (!image.isNull()) {
979         QByteArray ba;
980         QBuffer buffer(&ba);
981         buffer.open(QIODevice::WriteOnly);
982         QString mime = mimeType.toLower();
983         QString type;
984         if (mime == QLatin1String("image/png")) {
985             type = QStringLiteral("PNG");
986         } else if (mime == QLatin1String("image/bmp"))
987             type = QStringLiteral("BMP");
988         else if (mime == QLatin1String("image/jpeg"))
989             type = QStringLiteral("JPEG");
990         else if (mime == QLatin1String("image/x-portable-pixmap"))
991             type = QStringLiteral("PPM");
992         else if (mime == QLatin1String("image/tiff"))
993             type = QStringLiteral("TIFF");
994         else if (mime == QLatin1String("image/xpm"))
995             type = QStringLiteral("XPM");
996         else
997             return QStringLiteral("data:,");
998
999         image.save(&buffer, type.toLatin1());
1000         buffer.close();
1001         QString dataUrl = QStringLiteral("data:%1;base64,%2");
1002         return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
1003     }
1004     return QStringLiteral("data:,");
1005 }
1006
1007 void QQuickCanvasItem::delayedCreate()
1008 {
1009     Q_D(QQuickCanvasItem);
1010
1011     if (!d->contextInitialized && !d->contextType.isNull())
1012         createContext(d->contextType);
1013
1014     requestPaint();
1015 }
1016
1017 bool QQuickCanvasItem::createContext(const QString &contextType)
1018 {
1019     Q_D(QQuickCanvasItem);
1020
1021     if (contextType == QLatin1String("2d")) {
1022         if (d->contextType.compare(QLatin1String("2d"), Qt::CaseInsensitive) != 0)  {
1023             d->contextType = QLatin1String("2d");
1024             emit contextTypeChanged(); // XXX: can't be in setContextType()
1025         }
1026         initializeContext(new QQuickContext2D(this));
1027         return true;
1028     }
1029
1030     return false;
1031 }
1032
1033 void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args)
1034 {
1035     Q_D(QQuickCanvasItem);
1036
1037     d->context = context;
1038     d->context->init(this, args);
1039     d->context->setV8Engine(QQmlEnginePrivate::getV8Engine(qmlEngine(this)));
1040     d->contextInitialized = true;
1041     connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
1042     connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted()));
1043     emit contextChanged();
1044 }
1045
1046 QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
1047 {
1048     if (window.isEmpty())
1049         return QRect();
1050
1051     const int tw = tileSize.width();
1052     const int th = tileSize.height();
1053     const int h1 = window.left() / tw;
1054     const int v1 = window.top() / th;
1055
1056     const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1057     const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1058
1059     return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1060 }
1061
1062 /*!
1063     \qmlsignal QtQuick2::Canvas::onPaint(rect region)
1064
1065     This handler is called to render the \a region. If a context is active it
1066     can be referenced from the context property.
1067
1068     This signal can be triggered by QtQuick2::Canvas::markdirty,
1069     QtQuick2::Canvas::requestPaint or by changing the current canvas window.
1070 */
1071
1072 /*!
1073     \qmlsignal QtQuick2::Canvas::onPainted()
1074
1075     This handler is called after all context painting commands are executed and
1076     the Canvas has been rendered.
1077 */
1078
1079 QT_END_NAMESPACE