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