deb3493ac1bcd856b47e63dd7d6b5027bb39f84e
[profile/ivi/qtdeclarative.git] / src / declarative / items / context2d / qsgcanvasitem.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qsgadaptationlayer_p.h"
43 #include "qsgcanvasitem_p.h"
44 #include "qsgitem_p.h"
45 #include "qsgcontext2d_p.h"
46 #include "qsgcontext2dnode_p.h"
47 #include "qsgcontext2dtexture_p.h"
48 #include "qdeclarativepixmapcache_p.h"
49
50 #include <qdeclarativeinfo.h>
51 #include "qdeclarativeengine_p.h"
52 #include <QtCore/QBuffer>
53
54 QT_BEGIN_NAMESPACE
55
56 class QSGCanvasItemPrivate : public QSGItemPrivate
57 {
58 public:
59     QSGCanvasItemPrivate();
60     ~QSGCanvasItemPrivate();
61     QSGContext2D* context;
62     QSGContext2DTexture* texture;
63     QSizeF canvasSize;
64     QSize tileSize;
65     QRectF canvasWindow;
66     QRectF dirtyRect;
67     uint renderInThread : 1;
68     uint hasCanvasSize :1;
69     uint hasTileSize :1;
70     uint hasCanvasWindow :1;
71     uint componentCompleted :1;
72     QSGCanvasItem::RenderTarget renderTarget;
73     QHash<QUrl, QDeclarativePixmap*> images;
74     QUrl baseUrl;
75 };
76
77 QSGCanvasItemPrivate::QSGCanvasItemPrivate()
78     : QSGItemPrivate()
79     , context(0)
80     , texture(0)
81     , canvasSize(1, 1)
82     , tileSize(1, 1)
83     , renderInThread(false)
84     , hasCanvasSize(false)
85     , hasTileSize(false)
86     , hasCanvasWindow(false)
87     , componentCompleted(false)
88     , renderTarget(QSGCanvasItem::FramebufferObject)
89 {
90 }
91
92 QSGCanvasItemPrivate::~QSGCanvasItemPrivate()
93 {
94     qDeleteAll(images);
95 }
96
97 /*!
98     \qmlclass Canvas QSGCanvasItem
99     \inqmlmodule QtQuick 2
100     \since QtQuick 2.0
101     \brief The Canvas item provides HTML5 like canvas element which enables you to
102     draw within the item area by using Javascript.
103     \inherits Item
104     \ingroup qml-basic-visual-elements
105
106     With the Canvas item, users can draw straight and curved lines, simple and
107     complex shapes, graphs, and referenced graphic images. can also add texts, colors,
108     shadows, gradients, and patterns, and do low level pixel operations, etc. The Canvas item
109     also enables you to save or export the canvas as a image file or serialize the image data
110     to data url string.
111
112     To define a drawing area in the Canvas item, just set the \c width and \c height properties.
113     For example, the following code creates a Canvas item which has a drawing area with a height of 100
114     pixels and width of 200 pixels:
115     \qml
116     import QtQuick 2.0
117     Canvas {
118       id:mycanvas
119       width:100
120       height:200
121     }
122     \endqml
123
124     Currently the Canvas item only supports the two-dimensional rendering context.
125
126     \section1 Thread Rendering and Render Target
127     The Canvas item supports two render targets:Canvas.Image and Canvas.FramebufferObject.
128     The Canvas.Image render target is a \a QImage object which is actually a block of system
129     memory. This render target support background thread rendering. So if some complex or long
130     running painting need to be done, the Canvas.Image with thread rendering mode should be
131     chosen to avoid blocking the UI. Otherwise the Canvas.FramebufferObject render target should
132     be chosen as it could be much faster with good OpenGL hardware accelaration than rendering into
133     system memory, especially when the CPU is already very busy.
134
135     The default render target is Canvas.Image and the default renderInThread property is
136     false.
137
138     \section1 Tiled Canvas
139     The Canvas item also supports tiled rendering mode by setting the proper canvasSize, tileSize
140     and the canvasWindow properties.
141
142     With tiled canvas, a virtually very large canvas can be provided by a relatively small canvas
143     window. The actual memory consumption only relates to the canvas window size. So the canvas size
144     can be chosen freely as needed. The painting code then doesn't need to worry about the coordinate
145     system and complex matrix transformations at all.
146
147     As a side effect, by setting a good tile size, the tiles overlapped with the canvas window could be
148     cached and don't need to redraw, which can improve the performance significantly in some situations.
149
150     \section1 Pixel Operations
151     The Canvas item support all HTML5 2d context pixel operations. In order to get better
152     pixel reading/writing performance, the Canvas.Image render target should be chosen. As
153     for Canvas.FramebufferObject render target, the pixel data need to be exchanged between
154     the system memory and the graphic card, which can't be benefit from the hardware acceleration
155     at all. And the OpenGL rendering may synchronise with the V-Sync signal to avoid the
156     {en.wikipedia.org/wiki/Screen_tearing}{screen tearing} which makes the pixel operations
157     even slower with the Canvas.FrambufferObject render target.
158
159     \section1 Tips for Porting Existing HTML5 Canvas applications
160
161     Although the Canvas item is provided as a HTML5 like API, and
162     the canvas context API is as compatible with HTML5 2d context standard
163     as possible, the working HTML5 canvas applications are still need to
164     be modified to run in the Canvas item:
165     \list
166     \o Removes and replaces all DOM API calls with QML property bindings or Canvas item methods.
167     \o Removes and replaces all HTML envent handlers with the \a MouseArea item.
168     \o Changes the setInterval/setTimeout function calls with the \a Timer item.
169     \o Puts the actual painting code into the \a QtQuick2::Canvas::onPaint handler and triggers the
170        painting by calling the Canvas's \c markDirty or \c requestPaint methods.
171     \o For drawing images, loads them by calling the Canvas's loadImage method and then request to paint
172        them in the onImageLoaded handler.
173     \endlist
174
175     \sa QtQuick2::Context2D
176 */
177
178 QSGCanvasItem::QSGCanvasItem(QSGItem *parent)
179     : QSGItem(*(new QSGCanvasItemPrivate), parent)
180 {
181     setFlag(ItemHasContents);
182 }
183
184 QSGCanvasItem::~QSGCanvasItem()
185 {
186     Q_D(QSGCanvasItem);
187     if (d->texture) {
188         d->texture->setItem(0);
189         d->texture->deleteLater();
190     }
191     delete d->context;
192 }
193
194 /*!
195     \qmlproperty size QtQuick2::Canvas::canvasSize
196      Holds the logical canvas size that the context paints on.
197
198      By default, the canvas size is the same size as the current canvas item size.
199      By setting the canvas size, tile size and canvas window, the Canvas
200      item can act as a virtual large canvas with many seperately rendered tile rectangle
201      areas. Only those tiles within the current canvas window would be painted by
202      the Canvas render engine.
203     \sa QtQuick2::Canvas::tileSize QtQuick2::Canvas::canvasWindow
204 */
205 QSizeF QSGCanvasItem::canvasSize() const
206 {
207     Q_D(const QSGCanvasItem);
208     return d->canvasSize;
209 }
210
211 void QSGCanvasItem::setCanvasSize(const QSizeF & size)
212 {
213     Q_D(QSGCanvasItem);
214     if (d->canvasSize != size) {
215         d->hasCanvasSize = true;
216         d->canvasSize = size;
217         emit canvasSizeChanged();
218         polish();
219         update();
220     }
221 }
222
223 /*!
224     \qmlproperty size QtQuick2::Canvas::tileSize
225      Holds the canvas rendering tile size.
226
227      When the Canvas item in tiled mode by setting the canvas size, tile size and
228      the canvas window. The canvas render can improve the rendering performance
229      by rendering and caching tiles instead of rendering the whole canvas everytime.
230
231      Additionally, the canvas size could be infinitely large without allocating more
232      memories because only those tiles within the current visible region
233      are actually rendered.
234
235      By default, the tile size is the same with the canvas size.
236      \sa QtQuick2::Canvas::canvaasSize QtQuick2::Canvas::canvasWindow
237 */
238 QSize QSGCanvasItem::tileSize() const
239 {
240     Q_D(const QSGCanvasItem);
241     return d->tileSize;
242 }
243
244 void QSGCanvasItem::setTileSize(const QSize & size)
245 {
246     Q_D(QSGCanvasItem);
247     if (d->tileSize != size) {
248         d->hasTileSize = true;
249         d->tileSize = size;
250
251         emit tileSizeChanged();
252         polish();
253         update();
254     }
255 }
256
257 /*!
258     \qmlproperty rect QtQuick2::Canvas::canvasWindow
259      Holds the current canvas visible window.
260
261      By default, the canvas window size is the same as the Canvas item
262      size with the topleft point as (0, 0).
263
264      If the canvas size is different with the Canvas item size, the Canvas
265      item can display different visible areas by changing the canvas window's size
266      and/or position.
267     \sa QtQuick2::Canvas::canvasSize QtQuick2::Canvas::tileSize
268 */
269 QRectF QSGCanvasItem::canvasWindow() const
270 {
271     Q_D(const QSGCanvasItem);
272     return d->canvasWindow;
273 }
274
275 void QSGCanvasItem::setCanvasWindow(const QRectF& rect)
276 {
277     Q_D(QSGCanvasItem);
278     if (d->canvasWindow != rect) {
279         d->canvasWindow = rect;
280
281         d->hasCanvasWindow = true;
282         emit canvasWindowChanged();
283         polish();
284         update();
285     }
286 }
287
288
289 QSGContext2D* QSGCanvasItem::context() const
290 {
291     Q_D(const QSGCanvasItem);
292     return d->context;
293 }
294 /*!
295     \qmlproperty bool QtQuick2::Canvas::renderInThread
296      Holds the current canvas rendering mode.
297
298      By setting the renderInThread to true, complex and long
299      running painting can be rendered in a dedicated background
300      rendering thread to avoid blocking the main GUI.
301
302      Note: Different renderTarget may or may not support the
303      background rendering thread, if not, the renderInThread
304      property will be ignored.
305
306      The default value is false.
307     \sa QtQuick2::Canvas::renderTarget
308 */
309 bool QSGCanvasItem::renderInThread() const
310 {
311     Q_D(const QSGCanvasItem);
312     return d->renderInThread;
313 }
314 /*!
315     \qmlproperty bool QtQuick2::Canvas::renderTarget
316      Holds the current canvas render target.
317
318      \list
319      \o Canvas.Image  - render to an in memory image buffer, the render
320                         target supports background rendering.
321      \o Canvas.FramebufferObject - render to an OpenGL frame buffer,
322                                    this render target will ignore the
323                                    renderInThread property. The actual
324                                    rendering happens in the main QML rendering
325                                    process, which may be in a seperate render thread
326                                    or in the main GUI thread depends on the platforms.
327      \endlist
328
329      The default render target is \c Canvas.Image.
330     \sa QtQuick2::Canvas::renderInThread
331 */
332 QSGCanvasItem::RenderTarget QSGCanvasItem::renderTarget() const
333 {
334     Q_D(const QSGCanvasItem);
335     return d->renderTarget;
336 }
337
338 void QSGCanvasItem::setRenderTarget(RenderTarget target)
339 {
340     Q_D(QSGCanvasItem);
341     if (d->renderTarget != target) {
342         d->renderTarget = target;
343
344         if (d->componentCompleted)
345             createTexture();
346         emit renderTargetChanged();
347     }
348 }
349
350 void QSGCanvasItem::_doPainting(const QRectF& region)
351 {
352     Q_D(QSGCanvasItem);
353     emit paint(QDeclarativeV8Handle::fromHandle(d->context->v8value())
354              , QSGContext2DTexture::tiledRect(region, d->tileSize));
355     if (d->texture)
356         d->texture->wake();
357 }
358
359 /*!
360     \qmlproperty bool QtQuick2::Canvas::renderInThread
361      Holds the current canvas rendering mode.
362
363      When this property is true, all canvas painting commands
364      are rendered in a background rendering thread, otherwise
365      the rendering happens in the main GUI thread.
366
367      The default renderInThread value is false.
368 */
369 void QSGCanvasItem::setRenderInThread(bool renderInThread)
370 {
371     Q_D(QSGCanvasItem);
372     if (d->renderInThread != renderInThread) {
373         d->renderInThread = renderInThread;
374
375         if (d->componentCompleted)
376             createTexture();
377
378         if (d->renderInThread)
379             connect(this, SIGNAL(painted()), SLOT(update()));
380         else
381             disconnect(this, SIGNAL(painted()), this, SLOT(update()));
382         emit renderInThreadChanged();
383         polish();
384         update();
385     }
386 }
387
388 void QSGCanvasItem::geometryChanged(const QRectF &newGeometry,
389                              const QRectF &oldGeometry)
390 {
391     Q_D(QSGCanvasItem);
392     QSGItem::geometryChanged(newGeometry, oldGeometry);
393
394     const qreal w = newGeometry.width();
395     const qreal h = newGeometry.height();
396
397     if (!d->hasCanvasSize) {
398         d->canvasSize = QSizeF(w, h);
399         emit canvasSizeChanged();
400     }
401
402     if (!d->hasTileSize) {
403         d->tileSize = d->canvasSize.toSize();
404         emit tileSizeChanged();
405     }
406
407     if (!d->hasCanvasWindow) {
408         d->canvasWindow = newGeometry;
409         emit canvasWindowChanged();
410     }
411
412     polish();
413     update();
414 }
415
416 void QSGCanvasItem::componentComplete()
417 {
418     Q_D(QSGCanvasItem);
419     QSGItem::componentComplete();
420
421     if (!d->context)
422         createContext();
423     createTexture();
424
425     d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
426     requestPaint();
427     updatePolish(); //force update the canvas sizes to texture for the first time
428     d->componentCompleted = true;
429 }
430
431 void QSGCanvasItem::updatePolish()
432 {
433     Q_D(QSGCanvasItem);
434     QSGItem::updatePolish();
435     if (d->texture) {
436         if (!d->renderInThread && d->dirtyRect.isValid())
437             _doPainting(d->dirtyRect);
438
439         d->texture->canvasChanged(d->canvasSize.toSize()
440                                 , d->tileSize
441                                 , d->canvasWindow.toAlignedRect()
442                                 , d->dirtyRect.toAlignedRect()
443                                 , d->smooth);
444         d->dirtyRect = QRectF();
445     }
446 }
447
448 QSGNode *QSGCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
449 {
450     Q_D(QSGCanvasItem);
451     QSGContext2DNode *node = static_cast<QSGContext2DNode *>(oldNode);
452     if (!node)
453         node = new QSGContext2DNode(this);
454
455     node->setTexture(d->texture);
456     node->update();
457     return node;
458 }
459
460 void QSGCanvasItem::createTexture()
461 {
462     Q_D(QSGCanvasItem);
463
464     if (!d->texture
465       || d->texture->threadRendering() != d->renderInThread
466       || d->texture->renderTarget() != d->renderTarget) {
467         if (d->texture) {
468             d->texture->deleteLater();
469             d->texture = 0;
470         }
471
472         if (d->renderTarget == QSGCanvasItem::Image) {
473             d->texture = new QSGContext2DImageTexture(d->renderInThread);
474         } else if (d->renderTarget == QSGCanvasItem::FramebufferObject) {
475             d->texture = new QSGContext2DFBOTexture();
476         }
477
478         if (d->renderInThread && !d->texture->supportThreadRendering()) {
479             qWarning("Canvas: render target does not support thread rendering, force to non-thread rendering mode.");
480             d->renderInThread = false;
481             emit renderInThreadChanged();
482         }
483
484         if (d->renderInThread)
485             connect(d->texture, SIGNAL(textureChanged()), this, SLOT(update()));
486
487         d->texture->setItem(this);
488     }
489 }
490
491 void QSGCanvasItem::createContext()
492 {
493     Q_D(QSGCanvasItem);
494
495     delete d->context;
496
497     d->context = new QSGContext2D(this);
498
499     QV8Engine *e = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this));
500     d->context->setV8Engine(e);
501 }
502
503 /*!
504   \qmlmethod object QtQuick2::Canvas::getContext(string contextId)
505
506   Currently, the canvas item only support the 2D context. If the \a contextId
507   parameter isn't provided or is "2d", then the QtQuick2::Context2D object is
508   returned, otherwise returns an invalid value.
509   */
510 QDeclarativeV8Handle QSGCanvasItem::getContext(const QString &contextId)
511 {
512     Q_D(QSGCanvasItem);
513
514     if (contextId.toLower() != QLatin1String("2d"))
515         return QDeclarativeV8Handle::fromHandle(v8::Undefined());
516
517     if (!d->context)
518         createContext();
519     return QDeclarativeV8Handle::fromHandle(d->context->v8value());
520 }
521
522 /*!
523   \qmlmethod void QtQuick2::Canvas::markDirty(rect region)
524
525   Mark the given \a region as dirty, so that when this region is visible
526   the canvas render will redraw it. During the rendering process, the
527   canvas renderer may emit the canvas' "paint" signal so the actual painting
528   scripts can be putted into the canvas's "onPaint" signal handler function.
529
530   \sa QtQuick2::Canvas::paint QtQuick2::Canvas::requestPaint
531   */
532 void QSGCanvasItem::markDirty(const QRectF& region)
533 {
534     Q_D(QSGCanvasItem);
535     d->dirtyRect |= region;
536     if (d->componentCompleted)
537         polish();
538     update();
539 }
540
541
542 /*!
543   \qmlmethod bool QtQuick2::Canvas::save(string filename)
544
545    Save the current canvas content into an image file \a filename.
546    The saved image format is automatically decided by the \a filename's
547    suffix.
548
549    Note: calling this method will force painting the whole canvas, not the
550    current canvas visible window.
551
552    \sa canvasWindow canvasSize toDataURL
553   */
554 bool QSGCanvasItem::save(const QString &filename) const
555 {
556     Q_D(const QSGCanvasItem);
557     QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
558     return toImage().save(url.toLocalFile());
559 }
560
561 QImage QSGCanvasItem::loadedImage(const QUrl& url)
562 {
563     Q_D(QSGCanvasItem);
564     QUrl fullPathUrl = d->baseUrl.resolved(url);
565     if (!d->images.contains(fullPathUrl)) {
566         loadImage(url);
567     }
568     QDeclarativePixmap* pix = d->images.value(fullPathUrl);
569     if (pix->isLoading() || pix->isError()) {
570         return QImage();
571     }
572     return pix->pixmap().toImage();
573 }
574
575 /*!
576   \qmlmethod void QtQuick2::Canvas::loadImage(url image)
577     Loads the given \c image asynchronously, when the image is
578     ready, an imageLoaded signal will be emitted.
579     The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
580
581     Note: Only loaded images can be painted on the Canvas item.
582   \sa QtQuick2::Canvas::unloadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
583   \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
584   */
585 void QSGCanvasItem::loadImage(const QUrl& url)
586 {
587     Q_D(QSGCanvasItem);
588     QUrl fullPathUrl = d->baseUrl.resolved(url);
589     if (!d->images.contains(fullPathUrl)) {
590         QDeclarativePixmap* pix = new QDeclarativePixmap();
591         d->images.insert(fullPathUrl, pix);
592
593         pix->load(qmlEngine(this)
594                 , fullPathUrl
595                 , QDeclarativePixmap::Cache | QDeclarativePixmap::Asynchronous);
596         pix->connectFinished(this, SIGNAL(imageLoaded()));
597     }
598 }
599 /*!
600   \qmlmethod void QtQuick2::Canvas::loadImage(url image)
601   Unloads the \c image.
602
603   If the image is unloaded from the Canvas item, it can't be painted by the canvas context
604   until it's loaded again.
605
606   \sa QtQuick2::Canvas::loadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
607   \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
608   */
609 void QSGCanvasItem::unloadImage(const QUrl& url)
610 {
611     Q_D(QSGCanvasItem);
612     QUrl removeThis = d->baseUrl.resolved(url);
613     if (d->images.contains(removeThis)) {
614         delete d->images.value(removeThis);
615         d->images.remove(removeThis);
616     }
617 }
618
619 /*!
620   \qmlmethod void QtQuick2::Canvas::isImageError(url image)
621   Returns true if the image can't be loaded because of error happens.
622
623   \sa QtQuick2::Canvas::loadImage
624   */
625 bool QSGCanvasItem::isImageError(const QUrl& url) const
626 {
627     Q_D(const QSGCanvasItem);
628     QUrl fullPathUrl = d->baseUrl.resolved(url);
629     return d->images.contains(fullPathUrl)
630         && d->images.value(fullPathUrl)->isError();
631 }
632
633 /*!
634   \qmlmethod void QtQuick2::Canvas::isImageLoading(url image)
635   Returns true if the Canvas item still is loading the \c image.
636
637   \sa QtQuick2::Canvas::loadImage
638   */
639 bool QSGCanvasItem::isImageLoading(const QUrl& url) const
640 {
641     Q_D(const QSGCanvasItem);
642     QUrl fullPathUrl = d->baseUrl.resolved(url);
643     return d->images.contains(fullPathUrl)
644         && d->images.value(fullPathUrl)->isLoading();
645 }
646 /*!
647   \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
648   Returns true if the \c image is sucessfully loaded and ready to use.
649
650   \sa QtQuick2::Canvas::loadImage
651   */
652 bool QSGCanvasItem::isImageLoaded(const QUrl& url) const
653 {
654     Q_D(const QSGCanvasItem);
655     QUrl fullPathUrl = d->baseUrl.resolved(url);
656     return d->images.contains(fullPathUrl)
657         && d->images.value(fullPathUrl)->isReady();
658 }
659
660 QImage QSGCanvasItem::toImage(const QRectF& region) const
661 {
662     Q_D(const QSGCanvasItem);
663     if (d->texture) {
664         if (region.isEmpty())
665             return d->texture->toImage(canvasWindow());
666         else
667             return d->texture->toImage(region);
668     }
669     return QImage();
670 }
671
672 /*!
673   \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
674
675    Returns a data: URL for the image in the canvas.
676
677    The default \a mimeType is "image/png".
678
679    \sa QtQuick2::Canvas::save
680   */
681 QString QSGCanvasItem::toDataURL(const QString& mimeType) const
682 {
683     QImage image = toImage();
684
685     if (!image.isNull()) {
686         QByteArray ba;
687         QBuffer buffer(&ba);
688         buffer.open(QIODevice::WriteOnly);
689         QString mime = mimeType.toLower();
690         QString type;
691         if (mime == QLatin1Literal("image/png")) {
692             type = QLatin1Literal("PNG");
693         } else if (mime == QLatin1Literal("image/bmp"))
694             type = QLatin1Literal("BMP");
695         else if (mime == QLatin1Literal("image/jpeg"))
696             type = QLatin1Literal("JPEG");
697         else if (mime == QLatin1Literal("image/x-portable-pixmap"))
698             type = QLatin1Literal("PPM");
699         else if (mime == QLatin1Literal("image/tiff"))
700             type = QLatin1Literal("TIFF");
701         else if (mime == QLatin1Literal("image/xpm"))
702             type = QLatin1Literal("XPM");
703         else
704             return QLatin1Literal("data:,");
705
706         image.save(&buffer, type.toAscii());
707         buffer.close();
708         QString dataUrl = QLatin1Literal("data:%1;base64,%2");
709         return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
710     }
711     return QLatin1Literal("data:,");
712 }
713
714 /*!
715     \qmlsignal QtQuick2::Canvas::onPaint(QtQuick2::Context2D context, rect region)
716
717     This handler is called before the given \c region needs to be rendered.
718
719     This signal can be triggered by QtQuick2::Canvas::markdirty, QtQuick2::Canvas::requestPaint
720     or by changing the current canvas window.
721 */
722
723 /*!
724     \qmlsignal QtQuick2::Canvas::onPainted()
725
726     This handler is called after all context painting commands are executed and
727     the Canvas is actually rendered.
728 */
729
730 QT_END_NAMESPACE