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