1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
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>
51 #include <private/qqmlengine_p.h>
52 #include <QtCore/QBuffer>
56 QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image, QQuickCanvas *canvas)
65 QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickCanvas *canvas)
73 QQuickCanvasPixmap::~QQuickCanvasPixmap()
77 m_texture->deleteLater();
80 qreal QQuickCanvasPixmap::width() const
83 return m_pixmap->width();
85 return m_image.width();
88 qreal QQuickCanvasPixmap::height() const
91 return m_pixmap->height();
93 return m_image.height();
96 bool QQuickCanvasPixmap::isValid() const
99 return m_pixmap->isReady();
100 return !m_image.isNull();
103 QSGTexture *QQuickCanvasPixmap::texture()
107 Q_ASSERT(m_pixmap->textureFactory());
108 m_texture = m_pixmap->textureFactory()->createTexture(m_canvas);
110 m_texture = QQuickCanvasPrivate::get(m_canvas)->context->createTexture(m_image);
115 QImage QQuickCanvasPixmap::image()
117 if (m_image.isNull() && m_pixmap)
118 m_image = m_pixmap->image();
123 QHash<QQmlEngine *,QQuickContext2DRenderThread*> QQuickContext2DRenderThread::renderThreads;
124 QMutex QQuickContext2DRenderThread::renderThreadsMutex;
126 QQuickContext2DRenderThread::QQuickContext2DRenderThread(QQmlEngine *eng)
127 : QThread(eng), m_engine(eng), m_eventLoopQuitHack(0)
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);
136 QQuickContext2DRenderThread::~QQuickContext2DRenderThread()
138 renderThreadsMutex.lock();
139 renderThreads.remove(m_engine);
140 renderThreadsMutex.unlock();
142 m_eventLoopQuitHack->deleteLater();
146 QQuickContext2DRenderThread *QQuickContext2DRenderThread::instance(QQmlEngine *engine)
148 QQuickContext2DRenderThread *thread = 0;
149 renderThreadsMutex.lock();
150 if (renderThreads.contains(engine))
151 thread = renderThreads.value(engine);
153 thread = new QQuickContext2DRenderThread(engine);
154 renderThreads.insert(engine, thread);
156 renderThreadsMutex.unlock();
160 class QQuickCanvasItemPrivate : public QQuickItemPrivate
163 QQuickCanvasItemPrivate();
164 ~QQuickCanvasItemPrivate();
165 QQuickCanvasContext *context;
170 uint hasCanvasSize :1;
172 uint hasCanvasWindow :1;
174 uint contextInitialized :1;
175 QQuickCanvasItem::RenderTarget renderTarget;
176 QQuickCanvasItem::RenderStrategy renderStrategy;
178 QHash<QUrl, QQmlRefPointer<QQuickCanvasPixmap> > pixmaps;
180 QMap<int, v8::Persistent<v8::Function> > animationCallbacks;
183 QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
184 : QQuickItemPrivate()
188 , hasCanvasSize(false)
190 , hasCanvasWindow(false)
192 , contextInitialized(false)
193 , renderTarget(QQuickCanvasItem::FramebufferObject)
194 , renderStrategy(QQuickCanvasItem::Cooperative)
198 QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
205 \qmlclass Canvas QQuickCanvasItem
206 \inqmlmodule QtQuick 2
208 \brief For specifying a 2D canvas element which enables drawing via Javascript
210 \ingroup qml-basic-visual-elements
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
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:
230 Currently the Canvas item only supports the two-dimensional rendering context.
232 \section1 Threaded Rendering and Render Target
234 The Canvas item supports two render targets: \c Canvas.Image and
235 \c Canvas.FramebufferObject.
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.
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.
245 The default render target is Canvas.Image and the default renderStrategy is
248 \section1 Tiled Canvas
249 The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize
250 and \l canvasWindow properties.
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.
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
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.
271 \section1 Tips for Porting Existing HTML5 Canvas applications
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:
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.
286 \sa QtQuick2::Context2D
289 QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
290 : QQuickItem(*(new QQuickCanvasItemPrivate), parent)
292 setFlag(ItemHasContents);
295 QQuickCanvasItem::~QQuickCanvasItem()
297 Q_D(QQuickCanvasItem);
302 \qmlproperty size QtQuick2::Canvas::available
304 Indicates when Canvas is able to provide a drawing context to operate on.
307 bool QQuickCanvasItem::isAvailable() const
309 return d_func()->available;
313 \qmlproperty string QtQuick2::Canvas::contextType
314 The type of drawing context to use.
316 This property is set to the name of the active context type.
318 If set explicitly the canvas will attempt to create a context of the
319 named type after becoming available.
321 The type name is the same as used in the getContext() call, for the 2d
322 canvas the value will be "2d".
324 \sa QtQuick2::Canvas::getContext, QtQuick2::Canvas::available
327 QString QQuickCanvasItem::contextType() const
329 return d_func()->contextType;
332 void QQuickCanvasItem::setContextType(const QString &contextType)
334 Q_D(QQuickCanvasItem);
336 if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
339 if (d->contextInitialized) {
340 qmlInfo(this) << "Canvas already initialized with a different context type";
344 d->contextType = contextType;
347 createContext(contextType);
349 emit contextTypeChanged();
353 \qmlproperty object QtQuick2::Canvas::context
354 Holds the active drawing context.
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.
361 QQmlV8Handle QQuickCanvasItem::context() const
363 Q_D(const QQuickCanvasItem);
364 if (d->contextInitialized)
365 return QQmlV8Handle::fromHandle(d->context->v8value());
367 return QQmlV8Handle::fromHandle(v8::Null());
371 \qmlproperty size QtQuick2::Canvas::canvasSize
372 Holds the logical canvas size that the context paints on.
374 By default, the canvas size is the same size as the current canvas item
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
382 \sa QtQuick2::Canvas::tileSize, QtQuick2::Canvas::canvasWindow
384 QSizeF QQuickCanvasItem::canvasSize() const
386 Q_D(const QQuickCanvasItem);
387 return d->canvasSize;
390 void QQuickCanvasItem::setCanvasSize(const QSizeF & size)
392 Q_D(QQuickCanvasItem);
393 if (d->canvasSize != size) {
394 d->hasCanvasSize = true;
395 d->canvasSize = size;
396 emit canvasSizeChanged();
398 if (d->contextInitialized)
404 \qmlproperty size QtQuick2::Canvas::tileSize
405 Holds the canvas rendering tile size.
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.
411 Memory will be consumed only by those tiles within the current visible
414 By default the tileSize is the same as the canvasSize.
416 \sa QtQuick2::Canvas::canvaasSize, QtQuick2::Canvas::canvasWindow
418 QSize QQuickCanvasItem::tileSize() const
420 Q_D(const QQuickCanvasItem);
424 void QQuickCanvasItem::setTileSize(const QSize & size)
426 Q_D(QQuickCanvasItem);
427 if (d->tileSize != size) {
428 d->hasTileSize = true;
431 emit tileSizeChanged();
433 if (d->contextInitialized)
439 \qmlproperty rect QtQuick2::Canvas::canvasWindow
440 Holds the current canvas visible window.
442 By default the canvasWindow size is the same as the Canvas item size with
443 the top-left point as (0, 0).
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
449 \sa QtQuick2::Canvas::canvasSize, QtQuick2::Canvas::tileSize
451 QRectF QQuickCanvasItem::canvasWindow() const
453 Q_D(const QQuickCanvasItem);
454 return d->canvasWindow;
457 void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
459 Q_D(QQuickCanvasItem);
460 if (d->canvasWindow != rect) {
461 d->canvasWindow = rect;
463 d->hasCanvasWindow = true;
464 emit canvasWindowChanged();
466 if (d->contextInitialized)
472 \qmlproperty enumeration QtQuick2::Canvas::renderTarget
473 Holds the current canvas render target.
476 \li Canvas.Image - render to an in memory image buffer.
477 \li Canvas.FramebufferObject - render to an OpenGL frame buffer
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
486 The default render target is \c Canvas.FramebufferObject.
488 QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const
490 Q_D(const QQuickCanvasItem);
491 return d->renderTarget;
494 void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target)
496 Q_D(QQuickCanvasItem);
497 if (d->renderTarget != target) {
498 if (d->contextInitialized) {
499 qmlInfo(this) << "Canvas:renderTarget not changeble once context is active.";
503 d->renderTarget = target;
504 emit renderTargetChanged();
509 \qmlproperty enumeration QtQuick2::Canvas::renderStrategy
510 Holds the current canvas rendering strategy.
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.
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
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.
528 The default value is \c Canvas.Cooperative.
530 \sa QtQuick2::Canvas::renderTarget
533 QQuickCanvasItem::RenderStrategy QQuickCanvasItem::renderStrategy() const
535 return d_func()->renderStrategy;
538 void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strategy)
540 Q_D(QQuickCanvasItem);
541 if (d->renderStrategy != strategy) {
542 if (d->contextInitialized) {
543 qmlInfo(this) << "Canvas:renderStrategy not changeable once context is active.";
547 d->renderStrategy = strategy;
548 emit renderStrategyChanged();
552 QQuickCanvasContext* QQuickCanvasItem::rawContext() const
554 return d_func()->context;
557 bool QQuickCanvasItem::isPaintConnected()
559 IS_SIGNAL_CONNECTED(this, "paint(QRect)");
562 void QQuickCanvasItem::sceneGraphInitialized()
564 Q_D(QQuickCanvasItem);
567 connect(this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
568 QMetaObject::invokeMethod(this, "availableChanged", Qt::QueuedConnection);
570 if (!d->contextType.isNull())
571 QMetaObject::invokeMethod(this, "delayedCreate", Qt::QueuedConnection);
572 else if (isPaintConnected())
573 QMetaObject::invokeMethod(this, "requestPaint", Qt::QueuedConnection);
576 void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
578 Q_D(QQuickCanvasItem);
580 QQuickItem::geometryChanged(newGeometry, oldGeometry);
582 QSizeF newSize = newGeometry.size();
583 if (!d->hasCanvasSize && d->canvasSize != newSize) {
584 d->canvasSize = newSize;
585 emit canvasSizeChanged();
588 if (!d->hasTileSize && d->tileSize != newSize) {
589 d->tileSize = newSize.toSize();
590 emit tileSizeChanged();
593 const QRectF rect = QRectF(QPointF(0, 0), newSize);
595 if (!d->hasCanvasWindow && d->canvasWindow != rect) {
596 d->canvasWindow = rect;
597 emit canvasWindowChanged();
604 void QQuickCanvasItem::componentComplete()
606 QQuickItem::componentComplete();
608 Q_D(QQuickCanvasItem);
609 d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
612 void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
614 QQuickItem::itemChange(change, value);
615 if (change != QQuickItem::ItemSceneChange)
618 Q_D(QQuickCanvasItem);
622 if (value.canvas == 0)
625 d->canvas = value.canvas;
626 if (d->canvas->openglContext() != 0) // available context == initialized
627 sceneGraphInitialized();
629 connect(d->canvas, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
632 void QQuickCanvasItem::updatePolish()
634 QQuickItem::updatePolish();
636 Q_D(QQuickCanvasItem);
638 if (d->contextInitialized)
639 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth);
641 if (d->animationCallbacks.size() > 0 && isVisible()) {
642 QMap<int, v8::Persistent<v8::Function> > animationCallbacks = d->animationCallbacks;
643 d->animationCallbacks.clear();
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);
655 if (d->dirtyRect.isValid()) {
656 if (d->hasTileSize && d->hasCanvasWindow)
657 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
659 emit paint(d->dirtyRect.toRect());
660 d->dirtyRect = QRectF();
664 if (d->contextInitialized) {
665 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
672 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
674 Q_D(QQuickCanvasItem);
676 if (!d->contextInitialized)
679 class CanvasTextureNode : public QSGSimpleTextureNode
682 CanvasTextureNode() : QSGSimpleTextureNode() {}
683 ~CanvasTextureNode() {delete texture();}
686 CanvasTextureNode *node = static_cast<CanvasTextureNode*>(oldNode);
688 node = new CanvasTextureNode;
691 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
694 node->setTexture(d->context->texture());
695 node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
700 \qmlmethod object QtQuick2::Canvas::getContext(string contextId, any... args)
702 Returns a drawing context or null if no context available.
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.
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
713 Canvas only supports a 2d context.
716 void QQuickCanvasItem::getContext(QQmlV8Function *args)
718 Q_D(QQuickCanvasItem);
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());
727 qmlInfo(this) << "Unable to use getContext() at this time, please wait for available: true";
728 args->returnValue(v8::Null());
732 QString contextId = QString::fromUtf16(*v8::String::Value((*args)[0]));
734 if (d->context != 0) {
735 if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
736 args->returnValue(d->context->v8value());
740 qmlInfo(this) << "Canvas already initialized with a different context type";
741 args->returnValue(v8::Null());
745 if (createContext(contextId))
746 args->returnValue(d->context->v8value());
748 args->returnValue(v8::Null());
752 \qmlmethod long QtQuick2::Canvas::requestAnimationFrame(callback)
754 This function schedules callback to be invoked before composing the QtQuick
758 void QQuickCanvasItem::requestAnimationFrame(QQmlV8Function *args)
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());
766 Q_D(QQuickCanvasItem);
770 d->animationCallbacks.insert(++id, v8::Persistent<v8::Function>::New(((*args)[0]).As<v8::Function>()));
775 args->returnValue(v8::Int32::New(id));
779 \qmlmethod void QtQuick2::Canvas::cancelRequestAnimationFrmae(long handle)
781 This function will cancel the animation callback referenced by \a handle.
784 void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV8Function *args)
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());
792 d_func()->animationCallbacks.remove((*args)[0]->Int32Value());
797 \qmlmethod void QtQuick2::Canvas::requestPaint()
799 Request the entire visible region be re-drawn.
801 \sa QtQuick::Canvas::markDirty
804 void QQuickCanvasItem::requestPaint()
806 markDirty(d_func()->canvasWindow);
810 \qmlmethod void QtQuick2::Canvas::markDirty(rect area)
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
816 \sa QtQuick2::Canvas::paint, QtQuick2::Canvas::requestPaint
819 void QQuickCanvasItem::markDirty(const QRectF& rect)
821 Q_D(QQuickCanvasItem);
825 d->dirtyRect |= rect;
830 void QQuickCanvasItem::checkAnimationCallbacks()
832 if (d_func()->animationCallbacks.size() > 0 && isVisible())
837 \qmlmethod bool QtQuick2::Canvas::save(string filename)
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
843 Note: calling this method will force painting the whole canvas, not just the
844 current canvas visible window.
846 \sa canvasWindow, canvasSize, toDataURL
848 bool QQuickCanvasItem::save(const QString &filename) const
850 Q_D(const QQuickCanvasItem);
851 QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
852 return toImage().save(url.toLocalFile());
855 QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url)
857 Q_D(QQuickCanvasItem);
858 QUrl fullPathUrl = d->baseUrl.resolved(url);
859 if (!d->pixmaps.contains(fullPathUrl)) {
862 return d->pixmaps.value(fullPathUrl);
866 \qmlmethod void QtQuick2::Canvas::loadImage(url image)
867 Loads the given \c image asynchronously.
869 When the image is ready, onImageLoaded will be emitted.
870 The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
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
876 void QQuickCanvasItem::loadImage(const QUrl& url)
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);
886 pix->load(qmlEngine(this)
888 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
889 if (pix->isLoading())
890 pix->connectFinished(this, SIGNAL(imageLoaded()));
894 \qmlmethod void QtQuick2::Canvas::unloadImage(url image)
895 Unloads the \c image.
897 Once an image is unloaded it cannot be painted by the canvas context
898 unless it is loaded again.
900 \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
901 QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
903 void QQuickCanvasItem::unloadImage(const QUrl& url)
905 Q_D(QQuickCanvasItem);
906 d->pixmaps.remove(d->baseUrl.resolved(url));
910 \qmlmethod void QtQuick2::Canvas::isImageError(url image)
911 Returns true if the \a image failed to load.
913 \sa QtQuick2::Canvas::loadImage
915 bool QQuickCanvasItem::isImageError(const QUrl& url) const
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();
924 \qmlmethod void QtQuick2::Canvas::isImageLoading(url image)
925 Returns true if the \a image is currently loading.
927 \sa QtQuick2::Canvas::loadImage
929 bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
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();
937 \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
938 Returns true if the \a image is sucessfully loaded and ready to use.
940 \sa QtQuick2::Canvas::loadImage
942 bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
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();
950 QImage QQuickCanvasItem::toImage(const QRectF& rect) const
952 Q_D(const QQuickCanvasItem);
953 if (d->contextInitialized) {
955 return d->context->toImage(canvasWindow());
957 return d->context->toImage(rect);
964 \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
966 Returns a data URL for the image in the canvas.
968 The default \a mimeType is "image/png".
970 \sa QtQuick2::Canvas::save
972 QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
974 QImage image = toImage();
976 if (!image.isNull()) {
979 buffer.open(QIODevice::WriteOnly);
980 QString mime = mimeType.toLower();
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");
995 return QStringLiteral("data:,");
997 image.save(&buffer, type.toLatin1());
999 QString dataUrl = QStringLiteral("data:%1;base64,%2");
1000 return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
1002 return QStringLiteral("data:,");
1005 void QQuickCanvasItem::delayedCreate()
1007 Q_D(QQuickCanvasItem);
1009 if (!d->contextInitialized && !d->contextType.isNull())
1010 createContext(d->contextType);
1015 bool QQuickCanvasItem::createContext(const QString &contextType)
1017 Q_D(QQuickCanvasItem);
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()
1024 initializeContext(new QQuickContext2D(this));
1031 void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args)
1033 Q_D(QQuickCanvasItem);
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();
1044 QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
1046 if (window.isEmpty())
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;
1054 const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1055 const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1057 return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1061 \qmlsignal QtQuick2::Canvas::onPaint(rect region)
1063 This handler is called to render the \a region. If a context is active it
1064 can be referenced from the context property.
1066 This signal can be triggered by QtQuick2::Canvas::markdirty,
1067 QtQuick2::Canvas::requestPaint or by changing the current canvas window.
1071 \qmlsignal QtQuick2::Canvas::onPainted()
1073 This handler is called after all context painting commands are executed and
1074 the Canvas has been rendered.